4 * ipptool command for CUPS.
6 * Copyright 2007-2015 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
15 * This file is subject to the Apple OS-Developed Software exception.
19 * Include necessary headers...
22 #include <cups/cups-private.h>
23 #include <cups/file-private.h>
37 #endif /* !O_BINARY */
44 typedef enum _cups_transfer_e
/**** How to send request data ****/
46 _CUPS_TRANSFER_AUTO
, /* Chunk for files, length for static */
47 _CUPS_TRANSFER_CHUNKED
, /* Chunk always */
48 _CUPS_TRANSFER_LENGTH
/* Length always */
51 typedef enum _cups_output_e
/**** Output mode ****/
53 _CUPS_OUTPUT_QUIET
, /* No output */
54 _CUPS_OUTPUT_TEST
, /* Traditional CUPS test output */
55 _CUPS_OUTPUT_PLIST
, /* XML plist test output */
56 _CUPS_OUTPUT_LIST
, /* Tabular list output */
57 _CUPS_OUTPUT_CSV
/* Comma-separated values output */
60 typedef enum _cups_with_e
/**** WITH flags ****/
62 _CUPS_WITH_LITERAL
= 0, /* Match string is a literal value */
63 _CUPS_WITH_ALL
= 1, /* Must match all values */
64 _CUPS_WITH_REGEX
= 2, /* Match string is a regular expression */
65 _CUPS_WITH_HOSTNAME
= 4, /* Match string is a URI hostname */
66 _CUPS_WITH_RESOURCE
= 8, /* Match string is a URI resource */
67 _CUPS_WITH_SCHEME
= 16 /* Match string is a URI scheme */
70 typedef struct _cups_expect_s
/**** Expected attribute info ****/
72 int optional
, /* Optional attribute? */
73 not_expect
, /* Don't expect attribute? */
74 expect_all
; /* Expect all attributes to match/not match */
75 char *name
, /* Attribute name */
76 *of_type
, /* Type name */
77 *same_count_as
, /* Parallel attribute name */
78 *if_defined
, /* Only required if variable defined */
79 *if_not_defined
, /* Only required if variable is not defined */
80 *with_value
, /* Attribute must include this value */
81 *with_value_from
, /* Attribute must have one of the values in this attribute */
82 *define_match
, /* Variable to define on match */
83 *define_no_match
, /* Variable to define on no-match */
84 *define_value
; /* Variable to define with value */
85 int repeat_limit
, /* Maximum number of times to repeat */
86 repeat_match
, /* Repeat test on match */
87 repeat_no_match
, /* Repeat test on no match */
88 with_flags
, /* WITH flags */
89 count
; /* Expected count if > 0 */
90 ipp_tag_t in_group
; /* IN-GROUP value */
93 typedef struct _cups_status_s
/**** Status info ****/
95 ipp_status_t status
; /* Expected status code */
96 char *if_defined
, /* Only if variable is defined */
97 *if_not_defined
, /* Only if variable is not defined */
98 *define_match
, /* Variable to define on match */
99 *define_no_match
, /* Variable to define on no-match */
100 *define_value
; /* Variable to define with value */
101 int repeat_limit
, /* Maximum number of times to repeat */
102 repeat_match
, /* Repeat the test when it does not match */
103 repeat_no_match
; /* Repeat the test when it matches */
106 typedef struct _cups_var_s
/**** Variable ****/
108 char *name
, /* Name of variable */
109 *value
; /* Value of variable */
112 typedef struct _cups_vars_s
/**** Set of variables ****/
114 char *uri
, /* URI for printer */
115 *filename
, /* Filename */
116 scheme
[64], /* Scheme from URI */
117 userpass
[256], /* Username/password from URI */
118 hostname
[256], /* Hostname from URI */
119 resource
[1024]; /* Resource path from URI */
120 int port
; /* Port number from URI */
121 http_encryption_t encryption
; /* Encryption for connection? */
122 double timeout
; /* Timeout for connection */
123 int family
; /* Address family */
124 cups_array_t
*vars
; /* Array of variables */
132 static _cups_transfer_t Transfer
= _CUPS_TRANSFER_AUTO
;
133 /* How to transfer requests */
134 static _cups_output_t Output
= _CUPS_OUTPUT_LIST
;
136 static int Cancel
= 0, /* Cancel test? */
137 IgnoreErrors
= 0, /* Ignore errors? */
138 StopAfterIncludeError
= 0,
139 /* Stop after include errors? */
140 Verbosity
= 0, /* Show all attributes? */
141 Version
= 11, /* Default IPP version */
142 XMLHeader
= 0, /* 1 if header is written */
143 TestCount
= 0, /* Number of tests run */
144 PassCount
= 0, /* Number of passing tests */
145 FailCount
= 0, /* Number of failing tests */
146 SkipCount
= 0; /* Number of skipped tests */
147 static char *Username
= NULL
, /* Username from URI */
148 *Password
= NULL
; /* Password from URI */
149 static int PasswordTries
= 0; /* Number of tries with password */
156 static void add_stringf(cups_array_t
*a
, const char *s
, ...)
157 __attribute__ ((__format__ (__printf__
, 2, 3)));
158 static int compare_vars(_cups_var_t
*a
, _cups_var_t
*b
);
159 static int do_tests(FILE *outfile
, _cups_vars_t
*vars
, const char *testfile
);
160 static void expand_variables(_cups_vars_t
*vars
, char *dst
, const char *src
,
161 size_t dstsize
) __attribute__((nonnull(1,2,3)));
162 static int expect_matches(_cups_expect_t
*expect
, ipp_tag_t value_tag
);
163 static ipp_t
*get_collection(FILE *outfile
, _cups_vars_t
*vars
, FILE *fp
, int *linenum
);
164 static char *get_filename(const char *testfile
, char *dst
, const char *src
,
166 static char *get_string(ipp_attribute_t
*attr
, int element
, int flags
,
167 char *buffer
, size_t bufsize
);
168 static char *get_token(FILE *fp
, char *buf
, int buflen
,
170 static char *get_variable(_cups_vars_t
*vars
, const char *name
);
171 static char *iso_date(ipp_uchar_t
*date
);
172 static const char *password_cb(const char *prompt
);
173 static void pause_message(const char *message
);
174 static void print_attr(FILE *outfile
, int format
, ipp_attribute_t
*attr
, ipp_tag_t
*group
);
175 static void print_csv(FILE *outfile
, ipp_attribute_t
*attr
, int num_displayed
,
176 char **displayed
, size_t *widths
);
177 static void print_fatal_error(FILE *outfile
, const char *s
, ...)
178 __attribute__ ((__format__ (__printf__
, 2, 3)));
179 static void print_line(FILE *outfile
, ipp_attribute_t
*attr
, int num_displayed
,
180 char **displayed
, size_t *widths
);
181 static void print_xml_header(FILE *outfile
);
182 static void print_xml_string(FILE *outfile
, const char *element
, const char *s
);
183 static void print_xml_trailer(FILE *outfile
, int success
, const char *message
);
184 static void set_variable(FILE *outfile
, _cups_vars_t
*vars
, const char *name
, const char *value
);
186 static void sigterm_handler(int sig
);
188 static int timeout_cb(http_t
*http
, void *user_data
);
189 static void usage(void) __attribute__((noreturn
));
190 static int validate_attr(FILE *outfile
, cups_array_t
*errors
, ipp_attribute_t
*attr
);
191 static int with_value(FILE *outfile
, cups_array_t
*errors
, char *value
, int flags
,
192 ipp_attribute_t
*attr
, char *matchbuf
,
194 static int with_value_from(cups_array_t
*errors
, ipp_attribute_t
*fromattr
, ipp_attribute_t
*attr
, char *matchbuf
, size_t matchlen
);
198 * 'main()' - Parse options and do tests.
201 int /* O - Exit status */
202 main(int argc
, /* I - Number of command-line args */
203 char *argv
[]) /* I - Command-line arguments */
205 int i
; /* Looping var */
206 int status
; /* Status of tests... */
207 FILE *outfile
= stdout
;
209 char *opt
, /* Current option */
210 name
[1024], /* Name/value buffer */
211 *value
, /* Pointer to value */
212 filename
[1024], /* Real filename */
213 testname
[1024], /* Real test filename */
214 uri
[1024]; /* Copy of printer URI */
215 const char *ext
, /* Extension on filename */
216 *testfile
; /* Test file to use */
217 int interval
, /* Test interval in microseconds */
218 repeat
; /* Repeat count */
219 _cups_vars_t vars
; /* Variables */
220 http_uri_status_t uri_status
; /* URI separation status */
221 _cups_globals_t
*cg
= _cupsGlobals();
227 * Catch SIGINT and SIGTERM...
230 signal(SIGINT
, sigterm_handler
);
231 signal(SIGTERM
, sigterm_handler
);
235 * Initialize the locale and variables...
238 _cupsSetLocale(argv
);
240 memset(&vars
, 0, sizeof(vars
));
241 vars
.family
= AF_UNSPEC
;
242 vars
.vars
= cupsArrayNew((cups_array_func_t
)compare_vars
, NULL
);
247 * ipptool URI testfile
255 for (i
= 1; i
< argc
; i
++)
257 if (!strcmp(argv
[i
], "--help"))
261 else if (!strcmp(argv
[i
], "--stop-after-include-error"))
263 StopAfterIncludeError
= 1;
265 else if (!strcmp(argv
[i
], "--version"))
270 else if (argv
[i
][0] == '-')
272 for (opt
= argv
[i
] + 1; *opt
; opt
++)
276 case '4' : /* Connect using IPv4 only */
277 vars
.family
= AF_INET
;
281 case '6' : /* Connect using IPv6 only */
282 vars
.family
= AF_INET6
;
284 #endif /* AF_INET6 */
286 case 'C' : /* Enable HTTP chunking */
287 Transfer
= _CUPS_TRANSFER_CHUNKED
;
290 case 'E' : /* Encrypt with TLS */
292 vars
.encryption
= HTTP_ENCRYPT_REQUIRED
;
294 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
296 #endif /* HAVE_SSL */
299 case 'I' : /* Ignore errors */
303 case 'L' : /* Disable HTTP chunking */
304 Transfer
= _CUPS_TRANSFER_LENGTH
;
307 case 'P' : /* Output to plist file */
312 _cupsLangPrintf(stderr
, _("%s: Missing filename for \"-P\"."), "ipptool");
316 if (outfile
!= stdout
)
319 if ((outfile
= fopen(argv
[i
], "w")) == NULL
)
321 _cupsLangPrintf(stderr
, _("%s: Unable to open \"%s\": %s"), "ipptool", argv
[i
], strerror(errno
));
325 Output
= _CUPS_OUTPUT_PLIST
;
327 if (interval
|| repeat
)
329 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
334 case 'S' : /* Encrypt with SSL */
336 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
338 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
340 #endif /* HAVE_SSL */
343 case 'T' : /* Set timeout */
348 _cupsLangPrintf(stderr
,
349 _("%s: Missing timeout for \"-T\"."),
354 vars
.timeout
= _cupsStrScand(argv
[i
], NULL
, localeconv());
357 case 'V' : /* Set IPP version */
362 _cupsLangPrintf(stderr
,
363 _("%s: Missing version for \"-V\"."),
368 if (!strcmp(argv
[i
], "1.0"))
370 else if (!strcmp(argv
[i
], "1.1"))
372 else if (!strcmp(argv
[i
], "2.0"))
374 else if (!strcmp(argv
[i
], "2.1"))
376 else if (!strcmp(argv
[i
], "2.2"))
380 _cupsLangPrintf(stderr
,
381 _("%s: Bad version %s for \"-V\"."),
387 case 'X' : /* Produce XML output */
388 Output
= _CUPS_OUTPUT_PLIST
;
390 if (interval
|| repeat
)
392 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
397 case 'c' : /* CSV output */
398 Output
= _CUPS_OUTPUT_CSV
;
401 case 'd' : /* Define a variable */
406 _cupsLangPuts(stderr
,
407 _("ipptool: Missing name=value for \"-d\"."));
411 strlcpy(name
, argv
[i
], sizeof(name
));
412 if ((value
= strchr(name
, '=')) != NULL
)
415 value
= name
+ strlen(name
);
417 set_variable(outfile
, &vars
, name
, value
);
420 case 'f' : /* Set the default test filename */
425 _cupsLangPuts(stderr
,
426 _("ipptool: Missing filename for \"-f\"."));
433 vars
.filename
= NULL
;
436 if (access(argv
[i
], 0))
442 snprintf(filename
, sizeof(filename
), "%s.gz", argv
[i
]);
443 if (access(filename
, 0) && filename
[0] != '/'
445 && (!isalpha(filename
[0] & 255) || filename
[1] != ':')
449 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s",
450 cg
->cups_datadir
, argv
[i
]);
451 if (access(filename
, 0))
453 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s.gz",
454 cg
->cups_datadir
, argv
[i
]);
455 if (access(filename
, 0))
456 vars
.filename
= strdup(argv
[i
]);
458 vars
.filename
= strdup(filename
);
461 vars
.filename
= strdup(filename
);
464 vars
.filename
= strdup(filename
);
467 vars
.filename
= strdup(argv
[i
]);
469 if ((ext
= strrchr(vars
.filename
, '.')) != NULL
)
472 * Guess the MIME media type based on the extension...
475 if (!_cups_strcasecmp(ext
, ".gif"))
476 set_variable(outfile
, &vars
, "filetype", "image/gif");
477 else if (!_cups_strcasecmp(ext
, ".htm") ||
478 !_cups_strcasecmp(ext
, ".htm.gz") ||
479 !_cups_strcasecmp(ext
, ".html") ||
480 !_cups_strcasecmp(ext
, ".html.gz"))
481 set_variable(outfile
, &vars
, "filetype", "text/html");
482 else if (!_cups_strcasecmp(ext
, ".jpg") ||
483 !_cups_strcasecmp(ext
, ".jpeg"))
484 set_variable(outfile
, &vars
, "filetype", "image/jpeg");
485 else if (!_cups_strcasecmp(ext
, ".pcl") ||
486 !_cups_strcasecmp(ext
, ".pcl.gz"))
487 set_variable(outfile
, &vars
, "filetype", "application/vnd.hp-PCL");
488 else if (!_cups_strcasecmp(ext
, ".pdf"))
489 set_variable(outfile
, &vars
, "filetype", "application/pdf");
490 else if (!_cups_strcasecmp(ext
, ".png"))
491 set_variable(outfile
, &vars
, "filetype", "image/png");
492 else if (!_cups_strcasecmp(ext
, ".ps") ||
493 !_cups_strcasecmp(ext
, ".ps.gz"))
494 set_variable(outfile
, &vars
, "filetype", "application/postscript");
495 else if (!_cups_strcasecmp(ext
, ".pwg") ||
496 !_cups_strcasecmp(ext
, ".pwg.gz") ||
497 !_cups_strcasecmp(ext
, ".ras") ||
498 !_cups_strcasecmp(ext
, ".ras.gz"))
499 set_variable(outfile
, &vars
, "filetype", "image/pwg-raster");
500 else if (!_cups_strcasecmp(ext
, ".tif") ||
501 !_cups_strcasecmp(ext
, ".tiff"))
502 set_variable(outfile
, &vars
, "filetype", "image/tiff");
503 else if (!_cups_strcasecmp(ext
, ".txt") ||
504 !_cups_strcasecmp(ext
, ".txt.gz"))
505 set_variable(outfile
, &vars
, "filetype", "text/plain");
506 else if (!_cups_strcasecmp(ext
, ".urf") ||
507 !_cups_strcasecmp(ext
, ".urf.gz"))
508 set_variable(outfile
, &vars
, "filetype", "image/urf");
509 else if (!_cups_strcasecmp(ext
, ".xps"))
510 set_variable(outfile
, &vars
, "filetype", "application/openxps");
512 set_variable(outfile
, &vars
, "filetype", "application/octet-stream");
517 * Use the "auto-type" MIME media type...
520 set_variable(outfile
, &vars
, "filetype", "application/octet-stream");
524 case 'i' : /* Test every N seconds */
529 _cupsLangPuts(stderr
,
530 _("ipptool: Missing seconds for \"-i\"."));
535 interval
= (int)(_cupsStrScand(argv
[i
], NULL
, localeconv()) *
539 _cupsLangPuts(stderr
,
540 _("ipptool: Invalid seconds for \"-i\"."));
545 if (Output
== _CUPS_OUTPUT_PLIST
&& interval
)
547 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
552 case 'l' : /* List as a table */
553 Output
= _CUPS_OUTPUT_LIST
;
556 case 'n' : /* Repeat count */
561 _cupsLangPuts(stderr
,
562 _("ipptool: Missing count for \"-n\"."));
566 repeat
= atoi(argv
[i
]);
568 if (Output
== _CUPS_OUTPUT_PLIST
&& repeat
)
570 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
575 case 'q' : /* Be quiet */
576 Output
= _CUPS_OUTPUT_QUIET
;
579 case 't' : /* CUPS test output */
580 Output
= _CUPS_OUTPUT_TEST
;
583 case 'v' : /* Be verbose */
588 _cupsLangPrintf(stderr
, _("ipptool: Unknown option \"-%c\"."),
594 else if (!strncmp(argv
[i
], "ipp://", 6) || !strncmp(argv
[i
], "http://", 7)
596 || !strncmp(argv
[i
], "ipps://", 7)
597 || !strncmp(argv
[i
], "https://", 8)
598 #endif /* HAVE_SSL */
607 _cupsLangPuts(stderr
, _("ipptool: May only specify a single URI."));
612 if (!strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8))
613 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
614 #endif /* HAVE_SSL */
617 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, vars
.uri
,
618 vars
.scheme
, sizeof(vars
.scheme
),
619 vars
.userpass
, sizeof(vars
.userpass
),
620 vars
.hostname
, sizeof(vars
.hostname
),
622 vars
.resource
, sizeof(vars
.resource
));
624 if (uri_status
!= HTTP_URI_OK
)
626 _cupsLangPrintf(stderr
, _("ipptool: Bad URI - %s."), httpURIStatusString(uri_status
));
630 if (vars
.userpass
[0])
632 if ((Password
= strchr(vars
.userpass
, ':')) != NULL
)
635 Username
= vars
.userpass
;
636 cupsSetPasswordCB(password_cb
);
637 set_variable(outfile
, &vars
, "uriuser", vars
.userpass
);
640 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), vars
.scheme
, NULL
,
641 vars
.hostname
, vars
.port
, vars
.resource
);
652 _cupsLangPuts(stderr
, _("ipptool: URI required before test file."));
653 _cupsLangPuts(stderr
, argv
[i
]);
657 if (access(argv
[i
], 0) && argv
[i
][0] != '/'
659 && (!isalpha(argv
[i
][0] & 255) || argv
[i
][1] != ':')
663 snprintf(testname
, sizeof(testname
), "%s/ipptool/%s", cg
->cups_datadir
,
665 if (access(testname
, 0))
673 if (!do_tests(outfile
, &vars
, testfile
))
678 if (!vars
.uri
|| !testfile
)
682 * Loop if the interval is set...
685 if (Output
== _CUPS_OUTPUT_PLIST
)
686 print_xml_trailer(outfile
, !status
, NULL
);
687 else if (interval
> 0 && repeat
> 0)
691 usleep((useconds_t
)interval
);
692 do_tests(outfile
, &vars
, testfile
);
696 else if (interval
> 0)
700 usleep((useconds_t
)interval
);
701 do_tests(outfile
, &vars
, testfile
);
705 if ((Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
)) && TestCount
> 1)
708 * Show a summary report if there were multiple tests...
711 printf("\nSummary: %d tests, %d passed, %d failed, %d skipped\n"
712 "Score: %d%%\n", TestCount
, PassCount
, FailCount
, SkipCount
,
713 100 * (PassCount
+ SkipCount
) / TestCount
);
725 * 'add_stringf()' - Add a formatted string to an array.
729 add_stringf(cups_array_t
*a
, /* I - Array */
730 const char *s
, /* I - Printf-style format string */
731 ...) /* I - Additional args as needed */
733 char buffer
[10240]; /* Format buffer */
734 va_list ap
; /* Argument pointer */
738 * Don't bother is the array is NULL...
745 * Format the message...
749 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
753 * Add it to the array...
756 cupsArrayAdd(a
, buffer
);
761 * 'compare_vars()' - Compare two variables.
764 static int /* O - Result of comparison */
765 compare_vars(_cups_var_t
*a
, /* I - First variable */
766 _cups_var_t
*b
) /* I - Second variable */
768 return (_cups_strcasecmp(a
->name
, b
->name
));
773 * 'do_tests()' - Do tests as specified in the test file.
776 static int /* 1 = success, 0 = failure */
777 do_tests(FILE *outfile
, /* I - Output file */
778 _cups_vars_t
*vars
, /* I - Variables */
779 const char *testfile
) /* I - Test file to use */
781 int i
, /* Looping var */
782 linenum
, /* Current line number */
783 pass
, /* Did we pass the test? */
784 prev_pass
= 1, /* Did we pass the previous test? */
785 request_id
, /* Current request ID */
786 show_header
= 1, /* Show the test header? */
787 ignore_errors
, /* Ignore test failures? */
788 skip_previous
= 0, /* Skip on previous test failure? */
789 repeat_count
, /* Repeat count */
790 repeat_interval
, /* Repeat interval */
791 repeat_prev
, /* Previous repeat interval */
792 repeat_test
; /* Repeat a test? */
793 http_t
*http
= NULL
; /* HTTP connection to server */
794 FILE *fp
= NULL
; /* Test file */
795 char resource
[512], /* Resource for request */
796 token
[1024], /* Token from file */
797 *tokenptr
, /* Pointer into token */
798 temp
[1024], /* Temporary string */
799 buffer
[8192], /* Copy buffer */
800 compression
[16]; /* COMPRESSION value */
801 ipp_t
*request
= NULL
, /* IPP request */
802 *response
= NULL
; /* IPP response */
803 size_t length
; /* Length of IPP request */
804 http_status_t status
; /* HTTP status */
805 cups_file_t
*reqfile
; /* File to send */
806 ssize_t bytes
; /* Bytes read/written */
807 char attr
[128]; /* Attribute name */
808 ipp_op_t op
; /* Operation */
809 ipp_tag_t group
; /* Current group */
810 ipp_tag_t value
; /* Current value type */
811 ipp_attribute_t
*attrptr
, /* Attribute pointer */
812 *found
, /* Found attribute */
813 *lastcol
= NULL
; /* Last collection attribute */
814 char name
[1024], /* Name of test */
815 file_id
[1024], /* File identifier */
816 test_id
[1024]; /* Test identifier */
817 char filename
[1024]; /* Filename */
818 _cups_transfer_t transfer
; /* To chunk or not to chunk */
819 int version
, /* IPP version number to use */
820 skip_test
; /* Skip this test? */
821 int num_statuses
= 0; /* Number of valid status codes */
822 _cups_status_t statuses
[100], /* Valid status codes */
823 *last_status
; /* Last STATUS (for predicates) */
824 int num_expects
= 0; /* Number of expected attributes */
825 _cups_expect_t expects
[200], /* Expected attributes */
826 *expect
, /* Current expected attribute */
827 *last_expect
; /* Last EXPECT (for predicates) */
828 int num_displayed
= 0; /* Number of displayed attributes */
829 char *displayed
[200]; /* Displayed attributes */
830 size_t widths
[200]; /* Width of columns */
831 cups_array_t
*a
, /* Duplicate attribute array */
832 *errors
= NULL
; /* Errors array */
833 const char *error
; /* Current error */
837 * Open the test file...
840 if ((fp
= fopen(testfile
, "r")) == NULL
)
842 print_fatal_error(outfile
, "Unable to open test file %s - %s", testfile
,
849 * Connect to the server...
852 if ((http
= httpConnect2(vars
->hostname
, vars
->port
, NULL
, vars
->family
,
853 vars
->encryption
, 1, 30000, NULL
)) == NULL
)
855 print_fatal_error(outfile
, "Unable to connect to %s on port %d - %s", vars
->hostname
,
856 vars
->port
, cupsLastErrorString());
862 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
,
863 "deflate, gzip, identity");
865 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
, "identity");
866 #endif /* HAVE_LIBZ */
868 if (vars
->timeout
> 0.0)
869 httpSetTimeout(http
, vars
->timeout
, timeout_cb
, NULL
);
875 CUPS_SRAND(time(NULL
));
877 errors
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
,
878 (cups_afree_func_t
)free
);
882 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
884 while (!Cancel
&& get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
887 * Expect an open brace...
890 if (!strcmp(token
, "DEFINE"))
896 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
897 get_token(fp
, temp
, sizeof(temp
), &linenum
))
899 expand_variables(vars
, token
, temp
, sizeof(token
));
900 set_variable(outfile
, vars
, attr
, token
);
904 print_fatal_error(outfile
, "Missing DEFINE name and/or value on line %d.",
912 else if (!strcmp(token
, "DEFINE-DEFAULT"))
915 * DEFINE-DEFAULT name value
918 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
919 get_token(fp
, temp
, sizeof(temp
), &linenum
))
921 expand_variables(vars
, token
, temp
, sizeof(token
));
922 if (!get_variable(vars
, attr
))
923 set_variable(outfile
, vars
, attr
, token
);
927 print_fatal_error(outfile
, "Missing DEFINE-DEFAULT name and/or value on line "
935 else if (!strcmp(token
, "FILE-ID"))
941 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
943 expand_variables(vars
, file_id
, temp
, sizeof(file_id
));
947 print_fatal_error(outfile
, "Missing FILE-ID value on line %d.", linenum
);
954 else if (!strcmp(token
, "IGNORE-ERRORS"))
961 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
962 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
964 IgnoreErrors
= !_cups_strcasecmp(temp
, "yes");
968 print_fatal_error(outfile
, "Missing IGNORE-ERRORS value on line %d.", linenum
);
975 else if (!strcmp(token
, "INCLUDE"))
982 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
985 * Map the filename to and then run the tests...
988 if (!do_tests(outfile
, vars
, get_filename(testfile
, filename
, temp
, sizeof(filename
))))
992 if (StopAfterIncludeError
)
998 print_fatal_error(outfile
, "Missing INCLUDE filename on line %d.", linenum
);
1006 else if (!strcmp(token
, "INCLUDE-IF-DEFINED"))
1009 * INCLUDE-IF-DEFINED name "filename"
1010 * INCLUDE-IF-DEFINED name <filename>
1013 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1014 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1017 * Map the filename to and then run the tests...
1020 if (get_variable(vars
, attr
) &&
1021 !do_tests(outfile
, vars
, get_filename(testfile
, filename
, temp
, sizeof(filename
))))
1025 if (StopAfterIncludeError
)
1031 print_fatal_error(outfile
, "Missing INCLUDE-IF-DEFINED name or filename on line "
1040 else if (!strcmp(token
, "INCLUDE-IF-NOT-DEFINED"))
1043 * INCLUDE-IF-NOT-DEFINED name "filename"
1044 * INCLUDE-IF-NOT-DEFINED name <filename>
1047 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1048 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1051 * Map the filename to and then run the tests...
1054 if (!get_variable(vars
, attr
) &&
1055 !do_tests(outfile
, vars
, get_filename(testfile
, filename
, temp
, sizeof(filename
))))
1059 if (StopAfterIncludeError
)
1065 print_fatal_error(outfile
, "Missing INCLUDE-IF-NOT-DEFINED name or filename on "
1066 "line %d.", linenum
);
1074 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1077 * SKIP-IF-DEFINED variable
1080 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1082 if (get_variable(vars
, temp
))
1087 print_fatal_error(outfile
, "Missing SKIP-IF-DEFINED variable on line %d.",
1093 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1096 * SKIP-IF-NOT-DEFINED variable
1099 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1101 if (!get_variable(vars
, temp
))
1106 print_fatal_error(outfile
, "Missing SKIP-IF-NOT-DEFINED variable on line %d.",
1112 else if (!strcmp(token
, "STOP-AFTER-INCLUDE-ERROR"))
1115 * STOP-AFTER-INCLUDE-ERROR yes
1116 * STOP-AFTER-INCLUDE-ERROR no
1119 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1120 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1122 StopAfterIncludeError
= !_cups_strcasecmp(temp
, "yes");
1126 print_fatal_error(outfile
, "Missing STOP-AFTER-INCLUDE-ERROR value on line %d.",
1134 else if (!strcmp(token
, "TRANSFER"))
1142 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1144 if (!strcmp(temp
, "auto"))
1145 Transfer
= _CUPS_TRANSFER_AUTO
;
1146 else if (!strcmp(temp
, "chunked"))
1147 Transfer
= _CUPS_TRANSFER_CHUNKED
;
1148 else if (!strcmp(temp
, "length"))
1149 Transfer
= _CUPS_TRANSFER_LENGTH
;
1152 print_fatal_error(outfile
, "Bad TRANSFER value \"%s\" on line %d.", temp
,
1160 print_fatal_error(outfile
, "Missing TRANSFER value on line %d.", linenum
);
1167 else if (!strcmp(token
, "VERSION"))
1169 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1171 if (!strcmp(temp
, "1.0"))
1173 else if (!strcmp(temp
, "1.1"))
1175 else if (!strcmp(temp
, "2.0"))
1177 else if (!strcmp(temp
, "2.1"))
1179 else if (!strcmp(temp
, "2.2"))
1183 print_fatal_error(outfile
, "Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1190 print_fatal_error(outfile
, "Missing VERSION number on line %d.", linenum
);
1197 else if (strcmp(token
, "{"))
1199 print_fatal_error(outfile
, "Unexpected token %s seen on line %d.", token
, linenum
);
1205 * Initialize things...
1210 if (Output
== _CUPS_OUTPUT_PLIST
)
1211 print_xml_header(outfile
);
1212 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
1213 printf("\"%s\":\n", testfile
);
1218 strlcpy(resource
, vars
->resource
, sizeof(resource
));
1223 group
= IPP_TAG_ZERO
;
1224 ignore_errors
= IgnoreErrors
;
1232 transfer
= Transfer
;
1233 compression
[0] = '\0';
1235 strlcpy(name
, testfile
, sizeof(name
));
1236 if (strrchr(name
, '.') != NULL
)
1237 *strrchr(name
, '.') = '\0';
1240 * Parse until we see a close brace...
1243 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
1245 if (_cups_strcasecmp(token
, "COUNT") &&
1246 _cups_strcasecmp(token
, "DEFINE-MATCH") &&
1247 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1248 _cups_strcasecmp(token
, "DEFINE-VALUE") &&
1249 _cups_strcasecmp(token
, "IF-DEFINED") &&
1250 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1251 _cups_strcasecmp(token
, "IN-GROUP") &&
1252 _cups_strcasecmp(token
, "OF-TYPE") &&
1253 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1254 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1255 _cups_strcasecmp(token
, "REPEAT-NO-MATCH") &&
1256 _cups_strcasecmp(token
, "SAME-COUNT-AS") &&
1257 _cups_strcasecmp(token
, "WITH-ALL-VALUES") &&
1258 _cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") &&
1259 _cups_strcasecmp(token
, "WITH-ALL-RESOURCES") &&
1260 _cups_strcasecmp(token
, "WITH-ALL-SCHEMES") &&
1261 _cups_strcasecmp(token
, "WITH-HOSTNAME") &&
1262 _cups_strcasecmp(token
, "WITH-RESOURCE") &&
1263 _cups_strcasecmp(token
, "WITH-SCHEME") &&
1264 _cups_strcasecmp(token
, "WITH-VALUE") &&
1265 _cups_strcasecmp(token
, "WITH-VALUE-FROM"))
1268 if (_cups_strcasecmp(token
, "DEFINE-MATCH") &&
1269 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1270 _cups_strcasecmp(token
, "IF-DEFINED") &&
1271 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1272 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1273 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1274 _cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
1277 if (!strcmp(token
, "}"))
1279 else if (!strcmp(token
, "{") && lastcol
)
1282 * Another collection value
1285 ipp_t
*col
= get_collection(outfile
, vars
, fp
, &linenum
);
1286 /* Collection value */
1290 ippSetCollection(request
, &lastcol
, ippGetCount(lastcol
), col
);
1298 else if (!strcmp(token
, "COMPRESSION"))
1302 * COMPRESSION deflate
1306 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1308 expand_variables(vars
, compression
, temp
, sizeof(compression
));
1310 if (strcmp(compression
, "none") && strcmp(compression
, "deflate") &&
1311 strcmp(compression
, "gzip"))
1313 if (strcmp(compression
, "none"))
1314 #endif /* HAVE_LIBZ */
1316 print_fatal_error(outfile
, "Unsupported COMPRESSION value '%s' on line %d.",
1317 compression
, linenum
);
1322 if (!strcmp(compression
, "none"))
1323 compression
[0] = '\0';
1327 print_fatal_error(outfile
, "Missing COMPRESSION value on line %d.", linenum
);
1332 else if (!strcmp(token
, "DEFINE"))
1338 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1339 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1341 expand_variables(vars
, token
, temp
, sizeof(token
));
1342 set_variable(outfile
, vars
, attr
, token
);
1346 print_fatal_error(outfile
, "Missing DEFINE name and/or value on line %d.",
1352 else if (!strcmp(token
, "IGNORE-ERRORS"))
1359 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1360 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1362 ignore_errors
= !_cups_strcasecmp(temp
, "yes");
1366 print_fatal_error(outfile
, "Missing IGNORE-ERRORS value on line %d.", linenum
);
1373 else if (!_cups_strcasecmp(token
, "NAME"))
1379 get_token(fp
, temp
, sizeof(temp
), &linenum
);
1380 expand_variables(vars
, name
, temp
, sizeof(name
));
1382 else if (!_cups_strcasecmp(token
, "PAUSE"))
1385 * Pause with a message...
1388 get_token(fp
, token
, sizeof(token
), &linenum
);
1389 pause_message(token
);
1391 else if (!strcmp(token
, "REQUEST-ID"))
1398 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1400 if (isdigit(temp
[0] & 255))
1401 request_id
= atoi(temp
);
1402 else if (!_cups_strcasecmp(temp
, "random"))
1403 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
1406 print_fatal_error(outfile
, "Bad REQUEST-ID value \"%s\" on line %d.", temp
,
1414 print_fatal_error(outfile
, "Missing REQUEST-ID value on line %d.", linenum
);
1419 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1422 * SKIP-IF-DEFINED variable
1425 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1427 if (get_variable(vars
, temp
))
1432 print_fatal_error(outfile
, "Missing SKIP-IF-DEFINED value on line %d.",
1438 else if (!strcmp(token
, "SKIP-IF-MISSING"))
1441 * SKIP-IF-MISSING filename
1444 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1446 expand_variables(vars
, token
, temp
, sizeof(token
));
1447 get_filename(testfile
, filename
, token
, sizeof(filename
));
1449 if (access(filename
, R_OK
))
1454 print_fatal_error(outfile
, "Missing SKIP-IF-MISSING filename on line %d.",
1460 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1463 * SKIP-IF-NOT-DEFINED variable
1466 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1468 if (!get_variable(vars
, temp
))
1473 print_fatal_error(outfile
, "Missing SKIP-IF-NOT-DEFINED value on line %d.",
1479 else if (!strcmp(token
, "SKIP-PREVIOUS-ERROR"))
1482 * SKIP-PREVIOUS-ERROR yes
1483 * SKIP-PREVIOUS-ERROR no
1486 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1487 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1489 skip_previous
= !_cups_strcasecmp(temp
, "yes");
1493 print_fatal_error(outfile
, "Missing SKIP-PREVIOUS-ERROR value on line %d.", linenum
);
1500 else if (!strcmp(token
, "TEST-ID"))
1506 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1508 expand_variables(vars
, test_id
, temp
, sizeof(test_id
));
1512 print_fatal_error(outfile
, "Missing TEST-ID value on line %d.", linenum
);
1519 else if (!strcmp(token
, "TRANSFER"))
1527 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1529 if (!strcmp(temp
, "auto"))
1530 transfer
= _CUPS_TRANSFER_AUTO
;
1531 else if (!strcmp(temp
, "chunked"))
1532 transfer
= _CUPS_TRANSFER_CHUNKED
;
1533 else if (!strcmp(temp
, "length"))
1534 transfer
= _CUPS_TRANSFER_LENGTH
;
1537 print_fatal_error(outfile
, "Bad TRANSFER value \"%s\" on line %d.", temp
,
1545 print_fatal_error(outfile
, "Missing TRANSFER value on line %d.", linenum
);
1550 else if (!_cups_strcasecmp(token
, "VERSION"))
1552 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1554 if (!strcmp(temp
, "0.0"))
1556 else if (!strcmp(temp
, "1.0"))
1558 else if (!strcmp(temp
, "1.1"))
1560 else if (!strcmp(temp
, "2.0"))
1562 else if (!strcmp(temp
, "2.1"))
1564 else if (!strcmp(temp
, "2.2"))
1568 print_fatal_error(outfile
, "Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1575 print_fatal_error(outfile
, "Missing VERSION number on line %d.", linenum
);
1580 else if (!_cups_strcasecmp(token
, "RESOURCE"))
1586 if (!get_token(fp
, resource
, sizeof(resource
), &linenum
))
1588 print_fatal_error(outfile
, "Missing RESOURCE path on line %d.", linenum
);
1593 else if (!_cups_strcasecmp(token
, "OPERATION"))
1599 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1601 print_fatal_error(outfile
, "Missing OPERATION code on line %d.", linenum
);
1606 expand_variables(vars
, token
, temp
, sizeof(token
));
1608 if ((op
= ippOpValue(token
)) == (ipp_op_t
)-1 &&
1609 (op
= (ipp_op_t
)strtol(token
, NULL
, 0)) == 0)
1611 print_fatal_error(outfile
, "Bad OPERATION code \"%s\" on line %d.", token
,
1617 else if (!_cups_strcasecmp(token
, "GROUP"))
1620 * Attribute group...
1623 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1625 print_fatal_error(outfile
, "Missing GROUP tag on line %d.", linenum
);
1630 if ((value
= ippTagValue(token
)) < 0)
1632 print_fatal_error(outfile
, "Bad GROUP tag \"%s\" on line %d.", token
, linenum
);
1638 ippAddSeparator(request
);
1642 else if (!_cups_strcasecmp(token
, "DELAY"))
1645 * Delay before operation...
1650 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1652 print_fatal_error(outfile
, "Missing DELAY value on line %d.", linenum
);
1657 expand_variables(vars
, token
, temp
, sizeof(token
));
1659 if ((delay
= _cupsStrScand(token
, NULL
, localeconv())) <= 0.0)
1661 print_fatal_error(outfile
, "Bad DELAY value \"%s\" on line %d.", token
,
1668 if (Output
== _CUPS_OUTPUT_TEST
)
1669 printf(" [%g second delay]\n", delay
);
1671 usleep((useconds_t
)(1000000.0 * delay
));
1674 else if (!_cups_strcasecmp(token
, "ATTR"))
1680 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1682 print_fatal_error(outfile
, "Missing ATTR value tag on line %d.", linenum
);
1687 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
1689 print_fatal_error(outfile
, "Bad ATTR value tag \"%s\" on line %d.", token
,
1695 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
1697 print_fatal_error(outfile
, "Missing ATTR name on line %d.", linenum
);
1702 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1704 print_fatal_error(outfile
, "Missing ATTR value on line %d.", linenum
);
1709 expand_variables(vars
, token
, temp
, sizeof(token
));
1714 case IPP_TAG_BOOLEAN
:
1715 if (!_cups_strcasecmp(token
, "true"))
1716 attrptr
= ippAddBoolean(request
, group
, attr
, 1);
1718 attrptr
= ippAddBoolean(request
, group
, attr
, (char)atoi(token
));
1721 case IPP_TAG_INTEGER
:
1723 if (!strchr(token
, ','))
1724 attrptr
= ippAddInteger(request
, group
, value
, attr
, (int)strtol(token
, &tokenptr
, 0));
1727 int values
[100], /* Values */
1728 num_values
= 1; /* Number of values */
1730 values
[0] = (int)strtol(token
, &tokenptr
, 10);
1731 while (tokenptr
&& *tokenptr
&&
1732 num_values
< (int)(sizeof(values
) / sizeof(values
[0])))
1734 if (*tokenptr
== ',')
1736 else if (!isdigit(*tokenptr
& 255) && *tokenptr
!= '-')
1739 values
[num_values
] = (int)strtol(tokenptr
, &tokenptr
, 0);
1743 attrptr
= ippAddIntegers(request
, group
, value
, attr
, num_values
, values
);
1746 if (!tokenptr
|| *tokenptr
)
1748 print_fatal_error(outfile
, "Bad %s value \"%s\" on line %d.",
1749 ippTagString(value
), token
, linenum
);
1755 case IPP_TAG_RESOLUTION
:
1757 int xres
, /* X resolution */
1758 yres
; /* Y resolution */
1759 char *ptr
; /* Pointer into value */
1761 xres
= yres
= (int)strtol(token
, (char **)&ptr
, 10);
1762 if (ptr
> token
&& xres
> 0)
1765 yres
= (int)strtol(ptr
+ 1, (char **)&ptr
, 10);
1768 if (ptr
<= token
|| xres
<= 0 || yres
<= 0 || !ptr
||
1769 (_cups_strcasecmp(ptr
, "dpi") &&
1770 _cups_strcasecmp(ptr
, "dpc") &&
1771 _cups_strcasecmp(ptr
, "dpcm") &&
1772 _cups_strcasecmp(ptr
, "other")))
1774 print_fatal_error(outfile
, "Bad resolution value \"%s\" on line %d.",
1780 if (!_cups_strcasecmp(ptr
, "dpi"))
1781 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
1782 else if (!_cups_strcasecmp(ptr
, "dpc") ||
1783 !_cups_strcasecmp(ptr
, "dpcm"))
1784 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_CM
, xres
, yres
);
1786 attrptr
= ippAddResolution(request
, group
, attr
, (ipp_res_t
)0, xres
, yres
);
1790 case IPP_TAG_RANGE
:
1792 int lowers
[4], /* Lower value */
1793 uppers
[4], /* Upper values */
1794 num_vals
; /* Number of values */
1797 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
1798 lowers
+ 0, uppers
+ 0,
1799 lowers
+ 1, uppers
+ 1,
1800 lowers
+ 2, uppers
+ 2,
1801 lowers
+ 3, uppers
+ 3);
1803 if ((num_vals
& 1) || num_vals
== 0)
1805 print_fatal_error(outfile
, "Bad rangeOfInteger value \"%s\" on line "
1806 "%d.", token
, linenum
);
1811 attrptr
= ippAddRanges(request
, group
, attr
, num_vals
/ 2, lowers
,
1816 case IPP_TAG_BEGIN_COLLECTION
:
1817 if (!strcmp(token
, "{"))
1819 ipp_t
*col
= get_collection(outfile
, vars
, fp
, &linenum
);
1820 /* Collection value */
1824 attrptr
= lastcol
= ippAddCollection(request
, group
, attr
, col
);
1835 print_fatal_error(outfile
, "Bad ATTR collection value on line %d.",
1843 ipp_t
*col
; /* Collection value */
1844 long pos
= ftell(fp
); /* Save position of file */
1846 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1849 if (strcmp(token
, ","))
1851 fseek(fp
, pos
, SEEK_SET
);
1855 if (!get_token(fp
, token
, sizeof(token
), &linenum
) || strcmp(token
, "{"))
1857 print_fatal_error(outfile
, "Unexpected \"%s\" on line %d.", token
, linenum
);
1863 if ((col
= get_collection(outfile
, vars
, fp
, &linenum
)) == NULL
)
1866 ippSetCollection(request
, &attrptr
, ippGetCount(attrptr
), col
);
1869 while (!strcmp(token
, "{"));
1872 case IPP_TAG_STRING
:
1873 attrptr
= ippAddOctetString(request
, group
, attr
, token
, (int)strlen(token
));
1877 print_fatal_error(outfile
, "Unsupported ATTR value tag %s on line %d.",
1878 ippTagString(value
), linenum
);
1882 case IPP_TAG_TEXTLANG
:
1883 case IPP_TAG_NAMELANG
:
1886 case IPP_TAG_KEYWORD
:
1888 case IPP_TAG_URISCHEME
:
1889 case IPP_TAG_CHARSET
:
1890 case IPP_TAG_LANGUAGE
:
1891 case IPP_TAG_MIMETYPE
:
1892 if (!strchr(token
, ','))
1893 attrptr
= ippAddString(request
, group
, value
, attr
, NULL
, token
);
1897 * Multiple string values...
1900 int num_values
; /* Number of values */
1901 char *values
[100], /* Values */
1902 *ptr
; /* Pointer to next value */
1908 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
1910 if (ptr
> token
&& ptr
[-1] == '\\')
1911 _cups_strcpy(ptr
- 1, ptr
);
1915 values
[num_values
] = ptr
;
1920 attrptr
= ippAddStrings(request
, group
, value
, attr
, num_values
,
1921 NULL
, (const char **)values
);
1928 print_fatal_error(outfile
, "Unable to add attribute on line %d: %s", linenum
,
1929 cupsLastErrorString());
1934 else if (!_cups_strcasecmp(token
, "FILE"))
1940 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1942 print_fatal_error(outfile
, "Missing FILE filename on line %d.", linenum
);
1947 expand_variables(vars
, token
, temp
, sizeof(token
));
1948 get_filename(testfile
, filename
, token
, sizeof(filename
));
1950 if (access(filename
, R_OK
))
1952 print_fatal_error(outfile
, "Filename \"%s\" on line %d cannot be read.",
1954 print_fatal_error(outfile
, "Filename mapped to \"%s\".", filename
);
1959 else if (!_cups_strcasecmp(token
, "STATUS"))
1965 if (num_statuses
>= (int)(sizeof(statuses
) / sizeof(statuses
[0])))
1967 print_fatal_error(outfile
, "Too many STATUS's on line %d.", linenum
);
1972 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1974 print_fatal_error(outfile
, "Missing STATUS code on line %d.", linenum
);
1979 if ((statuses
[num_statuses
].status
= ippErrorValue(token
))
1980 == (ipp_status_t
)-1 &&
1981 (statuses
[num_statuses
].status
= (ipp_status_t
)strtol(token
, NULL
, 0)) == 0)
1983 print_fatal_error(outfile
, "Bad STATUS code \"%s\" on line %d.", token
,
1989 last_status
= statuses
+ num_statuses
;
1992 last_status
->define_match
= NULL
;
1993 last_status
->define_no_match
= NULL
;
1994 last_status
->if_defined
= NULL
;
1995 last_status
->if_not_defined
= NULL
;
1996 last_status
->repeat_limit
= 1000;
1997 last_status
->repeat_match
= 0;
1998 last_status
->repeat_no_match
= 0;
2000 else if (!_cups_strcasecmp(token
, "EXPECT") || !_cups_strcasecmp(token
, "EXPECT-ALL"))
2003 * Expected attributes...
2006 int expect_all
= !_cups_strcasecmp(token
, "EXPECT-ALL");
2008 if (num_expects
>= (int)(sizeof(expects
) / sizeof(expects
[0])))
2010 print_fatal_error(outfile
, "Too many EXPECT's on line %d.", linenum
);
2015 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2017 print_fatal_error(outfile
, "Missing EXPECT name on line %d.", linenum
);
2022 last_expect
= expects
+ num_expects
;
2025 memset(last_expect
, 0, sizeof(_cups_expect_t
));
2026 last_expect
->repeat_limit
= 1000;
2027 last_expect
->expect_all
= expect_all
;
2029 if (token
[0] == '!')
2031 last_expect
->not_expect
= 1;
2032 last_expect
->name
= strdup(token
+ 1);
2034 else if (token
[0] == '?')
2036 last_expect
->optional
= 1;
2037 last_expect
->name
= strdup(token
+ 1);
2040 last_expect
->name
= strdup(token
);
2042 else if (!_cups_strcasecmp(token
, "COUNT"))
2044 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2046 print_fatal_error(outfile
, "Missing COUNT number on line %d.", linenum
);
2051 if ((i
= atoi(token
)) <= 0)
2053 print_fatal_error(outfile
, "Bad COUNT \"%s\" on line %d.", token
, linenum
);
2059 last_expect
->count
= i
;
2062 print_fatal_error(outfile
, "COUNT without a preceding EXPECT on line %d.",
2068 else if (!_cups_strcasecmp(token
, "DEFINE-MATCH"))
2070 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2072 print_fatal_error(outfile
, "Missing DEFINE-MATCH variable on line %d.",
2079 last_expect
->define_match
= strdup(token
);
2080 else if (last_status
)
2081 last_status
->define_match
= strdup(token
);
2084 print_fatal_error(outfile
, "DEFINE-MATCH without a preceding EXPECT or STATUS "
2085 "on line %d.", linenum
);
2090 else if (!_cups_strcasecmp(token
, "DEFINE-NO-MATCH"))
2092 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2094 print_fatal_error(outfile
, "Missing DEFINE-NO-MATCH variable on line %d.",
2101 last_expect
->define_no_match
= strdup(token
);
2102 else if (last_status
)
2103 last_status
->define_no_match
= strdup(token
);
2106 print_fatal_error(outfile
, "DEFINE-NO-MATCH without a preceding EXPECT or "
2107 "STATUS on line %d.", linenum
);
2112 else if (!_cups_strcasecmp(token
, "DEFINE-VALUE"))
2114 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2116 print_fatal_error(outfile
, "Missing DEFINE-VALUE variable on line %d.",
2123 last_expect
->define_value
= strdup(token
);
2126 print_fatal_error(outfile
, "DEFINE-VALUE without a preceding EXPECT on "
2127 "line %d.", linenum
);
2132 else if (!_cups_strcasecmp(token
, "OF-TYPE"))
2134 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2136 print_fatal_error(outfile
, "Missing OF-TYPE value tag(s) on line %d.",
2143 last_expect
->of_type
= strdup(token
);
2146 print_fatal_error(outfile
, "OF-TYPE without a preceding EXPECT on line %d.",
2152 else if (!_cups_strcasecmp(token
, "IN-GROUP"))
2154 ipp_tag_t in_group
; /* IN-GROUP value */
2157 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2159 print_fatal_error(outfile
, "Missing IN-GROUP group tag on line %d.", linenum
);
2164 if ((in_group
= ippTagValue(token
)) == (ipp_tag_t
)-1)
2167 else if (last_expect
)
2168 last_expect
->in_group
= in_group
;
2171 print_fatal_error(outfile
, "IN-GROUP without a preceding EXPECT on line %d.",
2177 else if (!_cups_strcasecmp(token
, "REPEAT-LIMIT"))
2179 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2181 print_fatal_error(outfile
, "Missing REPEAT-LIMIT value on line %d.", linenum
);
2185 else if (atoi(token
) <= 0)
2187 print_fatal_error(outfile
, "Bad REPEAT-LIMIT value on line %d.", linenum
);
2193 last_status
->repeat_limit
= atoi(token
);
2194 else if (last_expect
)
2195 last_expect
->repeat_limit
= atoi(token
);
2198 print_fatal_error(outfile
, "REPEAT-LIMIT without a preceding EXPECT or STATUS "
2199 "on line %d.", linenum
);
2204 else if (!_cups_strcasecmp(token
, "REPEAT-MATCH"))
2207 last_status
->repeat_match
= 1;
2208 else if (last_expect
)
2209 last_expect
->repeat_match
= 1;
2212 print_fatal_error(outfile
, "REPEAT-MATCH without a preceding EXPECT or STATUS "
2213 "on line %d.", linenum
);
2218 else if (!_cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
2221 last_status
->repeat_no_match
= 1;
2222 else if (last_expect
)
2223 last_expect
->repeat_no_match
= 1;
2226 print_fatal_error(outfile
, "REPEAT-NO-MATCH without a preceding EXPECT or "
2227 "STATUS on ine %d.", linenum
);
2232 else if (!_cups_strcasecmp(token
, "SAME-COUNT-AS"))
2234 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2236 print_fatal_error(outfile
, "Missing SAME-COUNT-AS name on line %d.", linenum
);
2242 last_expect
->same_count_as
= strdup(token
);
2245 print_fatal_error(outfile
, "SAME-COUNT-AS without a preceding EXPECT on line "
2251 else if (!_cups_strcasecmp(token
, "IF-DEFINED"))
2253 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2255 print_fatal_error(outfile
, "Missing IF-DEFINED name on line %d.", linenum
);
2261 last_expect
->if_defined
= strdup(token
);
2262 else if (last_status
)
2263 last_status
->if_defined
= strdup(token
);
2266 print_fatal_error(outfile
, "IF-DEFINED without a preceding EXPECT or STATUS "
2267 "on line %d.", linenum
);
2272 else if (!_cups_strcasecmp(token
, "IF-NOT-DEFINED"))
2274 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2276 print_fatal_error(outfile
, "Missing IF-NOT-DEFINED name on line %d.", linenum
);
2282 last_expect
->if_not_defined
= strdup(token
);
2283 else if (last_status
)
2284 last_status
->if_not_defined
= strdup(token
);
2287 print_fatal_error(outfile
, "IF-NOT-DEFINED without a preceding EXPECT or STATUS "
2288 "on line %d.", linenum
);
2293 else if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") ||
2294 !_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
2295 !_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
2296 !_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
2297 !_cups_strcasecmp(token
, "WITH-HOSTNAME") ||
2298 !_cups_strcasecmp(token
, "WITH-RESOURCE") ||
2299 !_cups_strcasecmp(token
, "WITH-SCHEME") ||
2300 !_cups_strcasecmp(token
, "WITH-VALUE"))
2304 if (!_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
2305 !_cups_strcasecmp(token
, "WITH-HOSTNAME"))
2306 last_expect
->with_flags
= _CUPS_WITH_HOSTNAME
;
2307 else if (!_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
2308 !_cups_strcasecmp(token
, "WITH-RESOURCE"))
2309 last_expect
->with_flags
= _CUPS_WITH_RESOURCE
;
2310 else if (!_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
2311 !_cups_strcasecmp(token
, "WITH-SCHEME"))
2312 last_expect
->with_flags
= _CUPS_WITH_SCHEME
;
2314 if (!_cups_strncasecmp(token
, "WITH-ALL-", 9))
2315 last_expect
->with_flags
|= _CUPS_WITH_ALL
;
2318 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
2320 print_fatal_error(outfile
, "Missing %s value on line %d.", token
, linenum
);
2328 * Expand any variables in the value and then save it.
2331 expand_variables(vars
, token
, temp
, sizeof(token
));
2333 tokenptr
= token
+ strlen(token
) - 1;
2335 if (token
[0] == '/' && tokenptr
> token
&& *tokenptr
== '/')
2338 * WITH-VALUE is a POSIX extended regular expression.
2341 last_expect
->with_value
= calloc(1, (size_t)(tokenptr
- token
));
2342 last_expect
->with_flags
|= _CUPS_WITH_REGEX
;
2344 if (last_expect
->with_value
)
2345 memcpy(last_expect
->with_value
, token
+ 1, (size_t)(tokenptr
- token
- 1));
2350 * WITH-VALUE is a literal value...
2353 char *ptr
; /* Pointer into value */
2355 for (ptr
= token
; *ptr
; ptr
++)
2357 if (*ptr
== '\\' && ptr
[1])
2360 * Remove \ from \foo...
2363 _cups_strcpy(ptr
, ptr
+ 1);
2367 last_expect
->with_value
= strdup(token
);
2368 last_expect
->with_flags
|= _CUPS_WITH_LITERAL
;
2373 print_fatal_error(outfile
, "%s without a preceding EXPECT on line %d.", token
,
2379 else if (!_cups_strcasecmp(token
, "WITH-VALUE-FROM"))
2381 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
2383 print_fatal_error(outfile
, "Missing %s value on line %d.", token
, linenum
);
2391 * Expand any variables in the value and then save it.
2394 expand_variables(vars
, token
, temp
, sizeof(token
));
2396 last_expect
->with_value_from
= strdup(token
);
2397 last_expect
->with_flags
= _CUPS_WITH_LITERAL
;
2401 print_fatal_error(outfile
, "%s without a preceding EXPECT on line %d.", token
,
2407 else if (!_cups_strcasecmp(token
, "DISPLAY"))
2410 * Display attributes...
2413 if (num_displayed
>= (int)(sizeof(displayed
) / sizeof(displayed
[0])))
2415 print_fatal_error(outfile
, "Too many DISPLAY's on line %d", linenum
);
2420 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2422 print_fatal_error(outfile
, "Missing DISPLAY name on line %d.", linenum
);
2427 displayed
[num_displayed
] = strdup(token
);
2432 print_fatal_error(outfile
, "Unexpected token %s seen on line %d.", token
,
2440 * Submit the IPP request...
2445 ippSetVersion(request
, version
/ 10, version
% 10);
2446 ippSetOperation(request
, op
);
2447 ippSetRequestId(request
, request_id
);
2449 if (Output
== _CUPS_OUTPUT_PLIST
)
2451 fputs("<dict>\n", outfile
);
2452 fputs("<key>Name</key>\n", outfile
);
2453 print_xml_string(outfile
, "string", name
);
2456 fputs("<key>FileId</key>\n", outfile
);
2457 print_xml_string(outfile
, "string", file_id
);
2461 fputs("<key>TestId</key>\n", outfile
);
2462 print_xml_string(outfile
, "string", test_id
);
2464 fputs("<key>Version</key>\n", outfile
);
2465 fprintf(outfile
, "<string>%d.%d</string>\n", version
/ 10, version
% 10);
2466 fputs("<key>Operation</key>\n", outfile
);
2467 print_xml_string(outfile
, "string", ippOpString(op
));
2468 fputs("<key>RequestId</key>\n", outfile
);
2469 fprintf(outfile
, "<integer>%d</integer>\n", request_id
);
2470 fputs("<key>RequestAttributes</key>\n", outfile
);
2471 fputs("<array>\n", outfile
);
2474 fputs("<dict>\n", outfile
);
2475 for (attrptr
= request
->attrs
,
2476 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2478 attrptr
= attrptr
->next
)
2479 print_attr(outfile
, Output
, attrptr
, &group
);
2480 fputs("</dict>\n", outfile
);
2482 fputs("</array>\n", outfile
);
2485 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
2489 printf(" %s:\n", ippOpString(op
));
2491 for (attrptr
= request
->attrs
; attrptr
; attrptr
= attrptr
->next
)
2492 print_attr(stdout
, _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
2495 printf(" %-68.68s [", name
);
2499 if ((skip_previous
&& !prev_pass
) || skip_test
)
2506 if (Output
== _CUPS_OUTPUT_PLIST
)
2508 fputs("<key>Successful</key>\n", outfile
);
2509 fputs("<true />\n", outfile
);
2510 fputs("<key>Skipped</key>\n", outfile
);
2511 fputs("<true />\n", outfile
);
2512 fputs("<key>StatusCode</key>\n", outfile
);
2513 print_xml_string(outfile
, "string", "skip");
2514 fputs("<key>ResponseAttributes</key>\n", outfile
);
2515 fputs("<dict />\n", outfile
);
2518 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
2526 repeat_interval
= 1;
2533 status
= HTTP_STATUS_OK
;
2535 if (transfer
== _CUPS_TRANSFER_CHUNKED
||
2536 (transfer
== _CUPS_TRANSFER_AUTO
&& filename
[0]))
2539 * Send request using chunking - a 0 length means "chunk".
2547 * Send request using content length...
2550 length
= ippLength(request
);
2552 if (filename
[0] && (reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2555 * Read the file to get the uncompressed file size...
2558 while ((bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
2559 length
+= (size_t)bytes
;
2561 cupsFileClose(reqfile
);
2566 * Send the request...
2573 if (status
!= HTTP_STATUS_ERROR
)
2575 while (!response
&& !Cancel
&& prev_pass
)
2577 status
= cupsSendRequest(http
, request
, resource
, length
);
2581 httpSetField(http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
2582 #endif /* HAVE_LIBZ */
2584 if (!Cancel
&& status
== HTTP_STATUS_CONTINUE
&&
2585 request
->state
== IPP_DATA
&& filename
[0])
2587 if ((reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2590 (bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
2591 if ((status
= cupsWriteRequestData(http
, buffer
, (size_t)bytes
)) != HTTP_STATUS_CONTINUE
)
2594 cupsFileClose(reqfile
);
2598 snprintf(buffer
, sizeof(buffer
), "%s: %s", filename
,
2600 _cupsSetError(IPP_INTERNAL_ERROR
, buffer
, 0);
2602 status
= HTTP_STATUS_ERROR
;
2607 * Get the server's response...
2610 if (!Cancel
&& status
!= HTTP_STATUS_ERROR
)
2612 response
= cupsGetResponse(http
, resource
);
2613 status
= httpGetStatus(http
);
2616 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& http
->error
!= EINVAL
&&
2618 http
->error
!= WSAETIMEDOUT
)
2620 http
->error
!= ETIMEDOUT
)
2623 if (httpReconnect(http
))
2626 else if (status
== HTTP_STATUS_ERROR
|| status
== HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED
)
2631 else if (status
!= HTTP_STATUS_OK
)
2635 if (status
== HTTP_STATUS_UNAUTHORIZED
)
2643 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& http
->error
!= EINVAL
&&
2645 http
->error
!= WSAETIMEDOUT
)
2647 http
->error
!= ETIMEDOUT
)
2650 if (httpReconnect(http
))
2653 else if (status
== HTTP_STATUS_ERROR
)
2656 httpReconnect(http
);
2660 else if (status
!= HTTP_STATUS_OK
)
2667 * Check results of request...
2670 cupsArrayClear(errors
);
2672 if (http
->version
!= HTTP_1_1
)
2673 add_stringf(errors
, "Bad HTTP version (%d.%d)", http
->version
/ 100,
2674 http
->version
% 100);
2679 * No response, log error...
2682 add_stringf(errors
, "IPP request failed with status %s (%s)",
2683 ippErrorString(cupsLastError()),
2684 cupsLastErrorString());
2689 * Collect common attribute values...
2692 if ((attrptr
= ippFindAttribute(response
, "job-id",
2693 IPP_TAG_INTEGER
)) != NULL
)
2695 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2696 set_variable(outfile
, vars
, "job-id", temp
);
2699 if ((attrptr
= ippFindAttribute(response
, "job-uri",
2700 IPP_TAG_URI
)) != NULL
)
2701 set_variable(outfile
, vars
, "job-uri", attrptr
->values
[0].string
.text
);
2703 if ((attrptr
= ippFindAttribute(response
, "notify-subscription-id",
2704 IPP_TAG_INTEGER
)) != NULL
)
2706 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2707 set_variable(outfile
, vars
, "notify-subscription-id", temp
);
2711 * Check response, validating groups and attributes and logging errors
2715 if (response
->state
!= IPP_DATA
)
2717 "Missing end-of-attributes-tag in response "
2718 "(RFC 2910 section 3.5.1)");
2721 (response
->request
.status
.version
[0] != (version
/ 10) ||
2722 response
->request
.status
.version
[1] != (version
% 10)))
2724 "Bad version %d.%d in response - expected %d.%d "
2725 "(RFC 2911 section 3.1.8).",
2726 response
->request
.status
.version
[0],
2727 response
->request
.status
.version
[1],
2728 version
/ 10, version
% 10);
2730 if (response
->request
.status
.request_id
!= request_id
)
2732 "Bad request ID %d in response - expected %d "
2733 "(RFC 2911 section 3.1.1)",
2734 response
->request
.status
.request_id
, request_id
);
2736 attrptr
= response
->attrs
;
2739 "Missing first attribute \"attributes-charset "
2740 "(charset)\" in group operation-attributes-tag "
2741 "(RFC 2911 section 3.1.4).");
2744 if (!attrptr
->name
||
2745 attrptr
->value_tag
!= IPP_TAG_CHARSET
||
2746 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2747 attrptr
->num_values
!= 1 ||
2748 strcmp(attrptr
->name
, "attributes-charset"))
2750 "Bad first attribute \"%s (%s%s)\" in group %s, "
2751 "expected \"attributes-charset (charset)\" in "
2752 "group operation-attributes-tag (RFC 2911 section "
2754 attrptr
->name
? attrptr
->name
: "(null)",
2755 attrptr
->num_values
> 1 ? "1setOf " : "",
2756 ippTagString(attrptr
->value_tag
),
2757 ippTagString(attrptr
->group_tag
));
2759 attrptr
= attrptr
->next
;
2762 "Missing second attribute \"attributes-natural-"
2763 "language (naturalLanguage)\" in group "
2764 "operation-attributes-tag (RFC 2911 section "
2766 else if (!attrptr
->name
||
2767 attrptr
->value_tag
!= IPP_TAG_LANGUAGE
||
2768 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2769 attrptr
->num_values
!= 1 ||
2770 strcmp(attrptr
->name
, "attributes-natural-language"))
2772 "Bad first attribute \"%s (%s%s)\" in group %s, "
2773 "expected \"attributes-natural-language "
2774 "(naturalLanguage)\" in group "
2775 "operation-attributes-tag (RFC 2911 section "
2777 attrptr
->name
? attrptr
->name
: "(null)",
2778 attrptr
->num_values
> 1 ? "1setOf " : "",
2779 ippTagString(attrptr
->value_tag
),
2780 ippTagString(attrptr
->group_tag
));
2783 if ((attrptr
= ippFindAttribute(response
, "status-message",
2784 IPP_TAG_ZERO
)) != NULL
)
2786 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2788 "status-message (text(255)) has wrong value tag "
2789 "%s (RFC 2911 section 3.1.6.2).",
2790 ippTagString(attrptr
->value_tag
));
2791 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2793 "status-message (text(255)) has wrong group tag "
2794 "%s (RFC 2911 section 3.1.6.2).",
2795 ippTagString(attrptr
->group_tag
));
2796 if (attrptr
->num_values
!= 1)
2798 "status-message (text(255)) has %d values "
2799 "(RFC 2911 section 3.1.6.2).",
2800 attrptr
->num_values
);
2801 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2802 strlen(attrptr
->values
[0].string
.text
) > 255)
2804 "status-message (text(255)) has bad length %d"
2805 " (RFC 2911 section 3.1.6.2).",
2806 (int)strlen(attrptr
->values
[0].string
.text
));
2809 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
2810 IPP_TAG_ZERO
)) != NULL
)
2812 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2814 "detailed-status-message (text(MAX)) has wrong "
2815 "value tag %s (RFC 2911 section 3.1.6.3).",
2816 ippTagString(attrptr
->value_tag
));
2817 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2819 "detailed-status-message (text(MAX)) has wrong "
2820 "group tag %s (RFC 2911 section 3.1.6.3).",
2821 ippTagString(attrptr
->group_tag
));
2822 if (attrptr
->num_values
!= 1)
2824 "detailed-status-message (text(MAX)) has %d values"
2825 " (RFC 2911 section 3.1.6.3).",
2826 attrptr
->num_values
);
2827 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2828 strlen(attrptr
->values
[0].string
.text
) > 1023)
2830 "detailed-status-message (text(MAX)) has bad "
2831 "length %d (RFC 2911 section 3.1.6.3).",
2832 (int)strlen(attrptr
->values
[0].string
.text
));
2835 a
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2837 for (attrptr
= response
->attrs
,
2838 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2840 attrptr
= attrptr
->next
)
2842 if (attrptr
->group_tag
!= group
)
2844 int out_of_order
= 0; /* Are attribute groups out-of-order? */
2847 switch (attrptr
->group_tag
)
2852 case IPP_TAG_OPERATION
:
2856 case IPP_TAG_UNSUPPORTED_GROUP
:
2857 if (group
!= IPP_TAG_OPERATION
)
2862 case IPP_TAG_PRINTER
:
2863 if (group
!= IPP_TAG_OPERATION
&&
2864 group
!= IPP_TAG_UNSUPPORTED_GROUP
)
2868 case IPP_TAG_SUBSCRIPTION
:
2869 if (group
> attrptr
->group_tag
&&
2870 group
!= IPP_TAG_DOCUMENT
)
2875 if (group
> attrptr
->group_tag
)
2881 add_stringf(errors
, "Attribute groups out of order (%s < %s)",
2882 ippTagString(attrptr
->group_tag
),
2883 ippTagString(group
));
2885 if (attrptr
->group_tag
!= IPP_TAG_ZERO
)
2886 group
= attrptr
->group_tag
;
2889 validate_attr(outfile
, errors
, attrptr
);
2893 if (cupsArrayFind(a
, attrptr
->name
))
2894 add_stringf(errors
, "Duplicate \"%s\" attribute in %s group",
2895 attrptr
->name
, ippTagString(group
));
2897 cupsArrayAdd(a
, attrptr
->name
);
2904 * Now check the test-defined expected status-code and attribute
2908 for (i
= 0; i
< num_statuses
; i
++)
2910 if (statuses
[i
].if_defined
&&
2911 !get_variable(vars
, statuses
[i
].if_defined
))
2914 if (statuses
[i
].if_not_defined
&&
2915 get_variable(vars
, statuses
[i
].if_not_defined
))
2918 if (response
->request
.status
.status_code
== statuses
[i
].status
)
2920 if (statuses
[i
].repeat_match
&&
2921 repeat_count
< statuses
[i
].repeat_limit
)
2924 if (statuses
[i
].define_match
)
2925 set_variable(outfile
, vars
, statuses
[i
].define_match
, "1");
2931 if (statuses
[i
].repeat_no_match
&&
2932 repeat_count
< statuses
[i
].repeat_limit
)
2935 if (statuses
[i
].define_no_match
)
2937 set_variable(outfile
, vars
, statuses
[i
].define_no_match
, "1");
2943 if (i
== num_statuses
&& num_statuses
> 0)
2945 for (i
= 0; i
< num_statuses
; i
++)
2947 if (statuses
[i
].if_defined
&&
2948 !get_variable(vars
, statuses
[i
].if_defined
))
2951 if (statuses
[i
].if_not_defined
&&
2952 get_variable(vars
, statuses
[i
].if_not_defined
))
2955 if (!statuses
[i
].repeat_match
)
2956 add_stringf(errors
, "EXPECTED: STATUS %s (got %s)",
2957 ippErrorString(statuses
[i
].status
),
2958 ippErrorString(cupsLastError()));
2961 if ((attrptr
= ippFindAttribute(response
, "status-message",
2962 IPP_TAG_TEXT
)) != NULL
)
2963 add_stringf(errors
, "status-message=\"%s\"",
2964 attrptr
->values
[0].string
.text
);
2967 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
2969 if (expect
->if_defined
&& !get_variable(vars
, expect
->if_defined
))
2972 if (expect
->if_not_defined
&&
2973 get_variable(vars
, expect
->if_not_defined
))
2976 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
2980 if ((found
&& expect
->not_expect
) ||
2981 (!found
&& !(expect
->not_expect
|| expect
->optional
)) ||
2982 (found
&& !expect_matches(expect
, found
->value_tag
)) ||
2983 (found
&& expect
->in_group
&&
2984 found
->group_tag
!= expect
->in_group
))
2986 if (expect
->define_no_match
)
2987 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
2988 else if (!expect
->define_match
&& !expect
->define_value
)
2990 if (found
&& expect
->not_expect
)
2991 add_stringf(errors
, "NOT EXPECTED: %s", expect
->name
);
2992 else if (!found
&& !(expect
->not_expect
|| expect
->optional
))
2993 add_stringf(errors
, "EXPECTED: %s", expect
->name
);
2996 if (!expect_matches(expect
, found
->value_tag
))
2997 add_stringf(errors
, "EXPECTED: %s OF-TYPE %s (got %s)",
2998 expect
->name
, expect
->of_type
,
2999 ippTagString(found
->value_tag
));
3001 if (expect
->in_group
&& found
->group_tag
!= expect
->in_group
)
3002 add_stringf(errors
, "EXPECTED: %s IN-GROUP %s (got %s).",
3003 expect
->name
, ippTagString(expect
->in_group
),
3004 ippTagString(found
->group_tag
));
3008 if (expect
->repeat_no_match
&&
3009 repeat_count
< expect
->repeat_limit
)
3016 ippAttributeString(found
, buffer
, sizeof(buffer
));
3018 if (found
&& expect
->with_value_from
&& !with_value_from(NULL
, ippFindAttribute(response
, expect
->with_value_from
, IPP_TAG_ZERO
), found
, buffer
, sizeof(buffer
)))
3020 if (expect
->define_no_match
)
3021 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3022 else if (!expect
->define_match
&& !expect
->define_value
&& !expect
->repeat_match
&& !expect
->repeat_no_match
)
3024 add_stringf(errors
, "EXPECTED: %s WITH-VALUES-FROM %s", expect
->name
, expect
->with_value_from
);
3026 with_value_from(errors
, ippFindAttribute(response
, expect
->with_value_from
, IPP_TAG_ZERO
), found
, buffer
, sizeof(buffer
));
3029 if (expect
->repeat_no_match
&& repeat_count
< expect
->repeat_limit
)
3034 else if (found
&& !with_value(outfile
, NULL
, expect
->with_value
, expect
->with_flags
, found
, buffer
, sizeof(buffer
)))
3036 if (expect
->define_no_match
)
3037 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3038 else if (!expect
->define_match
&& !expect
->define_value
&&
3039 !expect
->repeat_match
&& !expect
->repeat_no_match
)
3041 if (expect
->with_flags
& _CUPS_WITH_REGEX
)
3042 add_stringf(errors
, "EXPECTED: %s %s /%s/",
3044 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
3045 "WITH-ALL-VALUES" : "WITH-VALUE",
3046 expect
->with_value
);
3048 add_stringf(errors
, "EXPECTED: %s %s \"%s\"",
3050 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
3051 "WITH-ALL-VALUES" : "WITH-VALUE",
3052 expect
->with_value
);
3054 with_value(outfile
, errors
, expect
->with_value
, expect
->with_flags
, found
,
3055 buffer
, sizeof(buffer
));
3058 if (expect
->repeat_no_match
&&
3059 repeat_count
< expect
->repeat_limit
)
3065 if (found
&& expect
->count
> 0 &&
3066 found
->num_values
!= expect
->count
)
3068 if (expect
->define_no_match
)
3069 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3070 else if (!expect
->define_match
&& !expect
->define_value
)
3072 add_stringf(errors
, "EXPECTED: %s COUNT %d (got %d)", expect
->name
,
3073 expect
->count
, found
->num_values
);
3076 if (expect
->repeat_no_match
&&
3077 repeat_count
< expect
->repeat_limit
)
3083 if (found
&& expect
->same_count_as
)
3085 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
3088 if (!attrptr
|| attrptr
->num_values
!= found
->num_values
)
3090 if (expect
->define_no_match
)
3091 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3092 else if (!expect
->define_match
&& !expect
->define_value
)
3096 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
3097 "(not returned)", expect
->name
,
3098 found
->num_values
, expect
->same_count_as
);
3099 else if (attrptr
->num_values
!= found
->num_values
)
3101 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
3102 "(%d values)", expect
->name
, found
->num_values
,
3103 expect
->same_count_as
, attrptr
->num_values
);
3106 if (expect
->repeat_no_match
&&
3107 repeat_count
< expect
->repeat_limit
)
3114 if (found
&& expect
->define_match
)
3115 set_variable(outfile
, vars
, expect
->define_match
, "1");
3117 if (found
&& expect
->define_value
)
3119 if (!expect
->with_value
)
3121 int last
= ippGetCount(found
) - 1;
3122 /* Last element in attribute */
3124 switch (ippGetValueTag(found
))
3127 case IPP_TAG_INTEGER
:
3128 snprintf(buffer
, sizeof(buffer
), "%d", ippGetInteger(found
, last
));
3131 case IPP_TAG_BOOLEAN
:
3132 if (ippGetBoolean(found
, last
))
3133 strlcpy(buffer
, "true", sizeof(buffer
));
3135 strlcpy(buffer
, "false", sizeof(buffer
));
3138 case IPP_TAG_RESOLUTION
:
3140 int xres
, /* Horizontal resolution */
3141 yres
; /* Vertical resolution */
3142 ipp_res_t units
; /* Resolution units */
3144 xres
= ippGetResolution(found
, last
, &yres
, &units
);
3147 snprintf(buffer
, sizeof(buffer
), "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
3149 snprintf(buffer
, sizeof(buffer
), "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
3153 case IPP_TAG_CHARSET
:
3154 case IPP_TAG_KEYWORD
:
3155 case IPP_TAG_LANGUAGE
:
3156 case IPP_TAG_MIMETYPE
:
3158 case IPP_TAG_NAMELANG
:
3160 case IPP_TAG_TEXTLANG
:
3162 case IPP_TAG_URISCHEME
:
3163 strlcpy(buffer
, ippGetString(found
, last
, NULL
), sizeof(buffer
));
3167 ippAttributeString(found
, buffer
, sizeof(buffer
));
3172 set_variable(outfile
, vars
, expect
->define_value
, buffer
);
3175 if (found
&& expect
->repeat_match
&&
3176 repeat_count
< expect
->repeat_limit
)
3179 while (expect
->expect_all
&& (found
= ippFindNextAttribute(response
, expect
->name
, IPP_TAG_ZERO
)) != NULL
);
3184 * If we are going to repeat this test, sleep 1 second so we don't flood
3185 * the printer with requests...
3190 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
3192 printf("%04d]\n", repeat_count
);
3195 if (num_displayed
> 0)
3197 for (attrptr
= ippFirstAttribute(response
); attrptr
; attrptr
= ippNextAttribute(response
))
3199 const char *attrname
= ippGetName(attrptr
);
3202 for (i
= 0; i
< num_displayed
; i
++)
3204 if (!strcmp(displayed
[i
], attrname
))
3206 print_attr(stdout
, _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
3215 sleep((unsigned)repeat_interval
);
3216 repeat_interval
= _cupsNextDelay(repeat_interval
, &repeat_prev
);
3218 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
3220 printf(" %-68.68s [", name
);
3225 while (repeat_test
);
3231 if (cupsArrayCount(errors
) > 0)
3232 prev_pass
= pass
= 0;
3239 if (Output
== _CUPS_OUTPUT_PLIST
)
3241 fputs("<key>Successful</key>\n", outfile
);
3242 fputs(prev_pass
? "<true />\n" : "<false />\n", outfile
);
3243 fputs("<key>StatusCode</key>\n", outfile
);
3244 print_xml_string(outfile
, "string", ippErrorString(cupsLastError()));
3245 fputs("<key>ResponseAttributes</key>\n", outfile
);
3246 fputs("<array>\n", outfile
);
3247 fputs("<dict>\n", outfile
);
3248 for (attrptr
= response
? response
->attrs
: NULL
,
3249 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
3251 attrptr
= attrptr
->next
)
3252 print_attr(outfile
, Output
, attrptr
, &group
);
3253 fputs("</dict>\n", outfile
);
3254 fputs("</array>\n", outfile
);
3257 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
3259 puts(prev_pass
? "PASS]" : "FAIL]");
3261 if (!prev_pass
|| (Verbosity
&& response
))
3263 printf(" RECEIVED: %lu bytes in response\n",
3264 (unsigned long)ippLength(response
));
3265 printf(" status-code = %s (%s)\n", ippErrorString(cupsLastError()),
3266 cupsLastErrorString());
3268 if (Verbosity
&& response
)
3270 for (attrptr
= response
->attrs
;
3272 attrptr
= attrptr
->next
)
3273 print_attr(stdout
, _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
3277 else if (!prev_pass
&& Output
!= _CUPS_OUTPUT_QUIET
)
3278 fprintf(stderr
, "%s\n", cupsLastErrorString());
3280 if (prev_pass
&& Output
>= _CUPS_OUTPUT_LIST
&& !Verbosity
&&
3283 size_t width
; /* Length of value */
3285 for (i
= 0; i
< num_displayed
; i
++)
3287 widths
[i
] = strlen(displayed
[i
]);
3289 for (attrptr
= ippFindAttribute(response
, displayed
[i
], IPP_TAG_ZERO
);
3291 attrptr
= ippFindNextAttribute(response
, displayed
[i
],
3294 width
= ippAttributeString(attrptr
, NULL
, 0);
3295 if (width
> widths
[i
])
3300 if (Output
== _CUPS_OUTPUT_CSV
)
3301 print_csv(outfile
, NULL
, num_displayed
, displayed
, widths
);
3303 print_line(outfile
, NULL
, num_displayed
, displayed
, widths
);
3305 attrptr
= response
->attrs
;
3309 while (attrptr
&& attrptr
->group_tag
<= IPP_TAG_OPERATION
)
3310 attrptr
= attrptr
->next
;
3314 if (Output
== _CUPS_OUTPUT_CSV
)
3315 print_csv(outfile
, attrptr
, num_displayed
, displayed
, widths
);
3317 print_line(outfile
, attrptr
, num_displayed
, displayed
, widths
);
3319 while (attrptr
&& attrptr
->group_tag
> IPP_TAG_OPERATION
)
3320 attrptr
= attrptr
->next
;
3324 else if (!prev_pass
)
3326 if (Output
== _CUPS_OUTPUT_PLIST
)
3328 fputs("<key>Errors</key>\n", outfile
);
3329 fputs("<array>\n", outfile
);
3331 for (error
= (char *)cupsArrayFirst(errors
);
3333 error
= (char *)cupsArrayNext(errors
))
3334 print_xml_string(outfile
, "string", error
);
3336 fputs("</array>\n", outfile
);
3339 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
3341 for (error
= (char *)cupsArrayFirst(errors
);
3343 error
= (char *)cupsArrayNext(errors
))
3344 printf(" %s\n", error
);
3348 if (num_displayed
> 0 && !Verbosity
&& response
&& (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
)))
3350 for (attrptr
= response
->attrs
;
3352 attrptr
= attrptr
->next
)
3356 for (i
= 0; i
< num_displayed
; i
++)
3358 if (!strcmp(displayed
[i
], attrptr
->name
))
3360 print_attr(outfile
, Output
, attrptr
, NULL
);
3370 if (Output
== _CUPS_OUTPUT_PLIST
)
3371 fputs("</dict>\n", outfile
);
3375 ippDelete(response
);
3378 for (i
= 0; i
< num_statuses
; i
++)
3380 if (statuses
[i
].if_defined
)
3381 free(statuses
[i
].if_defined
);
3382 if (statuses
[i
].if_not_defined
)
3383 free(statuses
[i
].if_not_defined
);
3384 if (statuses
[i
].define_match
)
3385 free(statuses
[i
].define_match
);
3386 if (statuses
[i
].define_no_match
)
3387 free(statuses
[i
].define_no_match
);
3391 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3394 if (expect
->of_type
)
3395 free(expect
->of_type
);
3396 if (expect
->same_count_as
)
3397 free(expect
->same_count_as
);
3398 if (expect
->if_defined
)
3399 free(expect
->if_defined
);
3400 if (expect
->if_not_defined
)
3401 free(expect
->if_not_defined
);
3402 if (expect
->with_value
)
3403 free(expect
->with_value
);
3404 if (expect
->define_match
)
3405 free(expect
->define_match
);
3406 if (expect
->define_no_match
)
3407 free(expect
->define_no_match
);
3408 if (expect
->define_value
)
3409 free(expect
->define_value
);
3413 for (i
= 0; i
< num_displayed
; i
++)
3417 if (!ignore_errors
&& !prev_pass
)
3423 cupsArrayDelete(errors
);
3430 ippDelete(response
);
3432 for (i
= 0; i
< num_statuses
; i
++)
3434 if (statuses
[i
].if_defined
)
3435 free(statuses
[i
].if_defined
);
3436 if (statuses
[i
].if_not_defined
)
3437 free(statuses
[i
].if_not_defined
);
3438 if (statuses
[i
].define_match
)
3439 free(statuses
[i
].define_match
);
3440 if (statuses
[i
].define_no_match
)
3441 free(statuses
[i
].define_no_match
);
3444 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3447 if (expect
->of_type
)
3448 free(expect
->of_type
);
3449 if (expect
->same_count_as
)
3450 free(expect
->same_count_as
);
3451 if (expect
->if_defined
)
3452 free(expect
->if_defined
);
3453 if (expect
->if_not_defined
)
3454 free(expect
->if_not_defined
);
3455 if (expect
->with_value
)
3456 free(expect
->with_value
);
3457 if (expect
->define_match
)
3458 free(expect
->define_match
);
3459 if (expect
->define_no_match
)
3460 free(expect
->define_no_match
);
3461 if (expect
->define_value
)
3462 free(expect
->define_value
);
3465 for (i
= 0; i
< num_displayed
; i
++)
3473 * 'expand_variables()' - Expand variables in a string.
3477 expand_variables(_cups_vars_t
*vars
, /* I - Variables */
3478 char *dst
, /* I - Destination string buffer */
3479 const char *src
, /* I - Source string */
3480 size_t dstsize
) /* I - Size of destination buffer */
3482 char *dstptr
, /* Pointer into destination */
3483 *dstend
, /* End of destination */
3484 temp
[256], /* Temporary string */
3485 *tempptr
; /* Pointer into temporary string */
3486 const char *value
; /* Value to substitute */
3490 dstend
= dst
+ dstsize
- 1;
3492 while (*src
&& dstptr
< dstend
)
3497 * Substitute a string/number...
3500 if (!strncmp(src
, "$$", 2))
3505 else if (!strncmp(src
, "$ENV[", 5))
3507 strlcpy(temp
, src
+ 5, sizeof(temp
));
3509 for (tempptr
= temp
; *tempptr
; tempptr
++)
3510 if (*tempptr
== ']')
3516 value
= getenv(temp
);
3517 src
+= tempptr
- temp
+ 5;
3524 strlcpy(temp
, src
, sizeof(temp
));
3525 if ((tempptr
= strchr(temp
, '}')) != NULL
)
3528 tempptr
= temp
+ strlen(temp
);
3532 strlcpy(temp
, src
+ 1, sizeof(temp
));
3534 for (tempptr
= temp
; *tempptr
; tempptr
++)
3535 if (!isalnum(*tempptr
& 255) && *tempptr
!= '-' && *tempptr
!= '_')
3542 if (!strcmp(temp
, "uri"))
3544 else if (!strcmp(temp
, "filename"))
3545 value
= vars
->filename
;
3546 else if (!strcmp(temp
, "scheme") || !strcmp(temp
, "method"))
3547 value
= vars
->scheme
;
3548 else if (!strcmp(temp
, "username"))
3549 value
= vars
->userpass
;
3550 else if (!strcmp(temp
, "hostname"))
3551 value
= vars
->hostname
;
3552 else if (!strcmp(temp
, "port"))
3554 snprintf(temp
, sizeof(temp
), "%d", vars
->port
);
3557 else if (!strcmp(temp
, "resource"))
3558 value
= vars
->resource
;
3559 else if (!strcmp(temp
, "user"))
3562 value
= get_variable(vars
, temp
);
3564 src
+= tempptr
- temp
+ 1;
3569 strlcpy(dstptr
, value
, (size_t)(dstend
- dstptr
+ 1));
3570 dstptr
+= strlen(dstptr
);
3582 * 'expect_matches()' - Return true if the tag matches the specification.
3585 static int /* O - 1 if matches, 0 otherwise */
3587 _cups_expect_t
*expect
, /* I - Expected attribute */
3588 ipp_tag_t value_tag
) /* I - Value tag for attribute */
3590 int match
; /* Match? */
3591 char *of_type
, /* Type name to match */
3592 *next
, /* Next name to match */
3593 sep
; /* Separator character */
3597 * If we don't expect a particular type, return immediately...
3600 if (!expect
->of_type
)
3604 * Parse the "of_type" value since the string can contain multiple attribute
3605 * types separated by "," or "|"...
3608 for (of_type
= expect
->of_type
, match
= 0; !match
&& *of_type
; of_type
= next
)
3611 * Find the next separator, and set it (temporarily) to nul if present.
3614 for (next
= of_type
; *next
&& *next
!= '|' && *next
!= ','; next
++);
3616 if ((sep
= *next
) != '\0')
3620 * Support some meta-types to make it easier to write the test file.
3623 if (!strcmp(of_type
, "text"))
3624 match
= value_tag
== IPP_TAG_TEXTLANG
|| value_tag
== IPP_TAG_TEXT
;
3625 else if (!strcmp(of_type
, "name"))
3626 match
= value_tag
== IPP_TAG_NAMELANG
|| value_tag
== IPP_TAG_NAME
;
3627 else if (!strcmp(of_type
, "collection"))
3628 match
= value_tag
== IPP_TAG_BEGIN_COLLECTION
;
3630 match
= value_tag
== ippTagValue(of_type
);
3633 * Restore the separator if we have one...
3645 * 'get_collection()' - Get a collection value from the current test file.
3648 static ipp_t
* /* O - Collection value */
3649 get_collection(FILE *outfile
, /* I - Output file */
3650 _cups_vars_t
*vars
, /* I - Variables */
3651 FILE *fp
, /* I - File to read from */
3652 int *linenum
) /* IO - Line number */
3654 char token
[1024], /* Token from file */
3655 temp
[1024], /* Temporary string */
3656 attr
[128]; /* Attribute name */
3657 ipp_tag_t value
; /* Current value type */
3658 ipp_t
*col
= ippNew(); /* Collection value */
3659 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
3662 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
3664 if (!strcmp(token
, "}"))
3666 else if (!strcmp(token
, "{") && lastcol
)
3669 * Another collection value
3672 ipp_t
*subcol
= get_collection(outfile
, vars
, fp
, linenum
);
3673 /* Collection value */
3676 ippSetCollection(col
, &lastcol
, ippGetCount(lastcol
), subcol
);
3680 else if (!_cups_strcasecmp(token
, "MEMBER"))
3688 if (!get_token(fp
, token
, sizeof(token
), linenum
))
3690 print_fatal_error(outfile
, "Missing MEMBER value tag on line %d.", *linenum
);
3694 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
3696 print_fatal_error(outfile
, "Bad MEMBER value tag \"%s\" on line %d.", token
,
3701 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
3703 print_fatal_error(outfile
, "Missing MEMBER name on line %d.", *linenum
);
3707 if (!get_token(fp
, temp
, sizeof(temp
), linenum
))
3709 print_fatal_error(outfile
, "Missing MEMBER value on line %d.", *linenum
);
3713 expand_variables(vars
, token
, temp
, sizeof(token
));
3717 case IPP_TAG_BOOLEAN
:
3718 if (!_cups_strcasecmp(token
, "true"))
3719 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
3721 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, (char)atoi(token
));
3724 case IPP_TAG_INTEGER
:
3726 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
3729 case IPP_TAG_RESOLUTION
:
3731 int xres
, /* X resolution */
3732 yres
; /* Y resolution */
3733 char units
[6]; /* Units */
3735 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
3736 (_cups_strcasecmp(units
, "dpi") &&
3737 _cups_strcasecmp(units
, "dpc") &&
3738 _cups_strcasecmp(units
, "dpcm") &&
3739 _cups_strcasecmp(units
, "other")))
3741 print_fatal_error(outfile
, "Bad resolution value \"%s\" on line %d.",
3746 if (!_cups_strcasecmp(units
, "dpi"))
3747 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
3748 else if (!_cups_strcasecmp(units
, "dpc") ||
3749 !_cups_strcasecmp(units
, "dpcm"))
3750 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_CM
, xres
, yres
);
3752 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, (ipp_res_t
)0, xres
, yres
);
3756 case IPP_TAG_RANGE
:
3758 int lowers
[4], /* Lower value */
3759 uppers
[4], /* Upper values */
3760 num_vals
; /* Number of values */
3763 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
3764 lowers
+ 0, uppers
+ 0,
3765 lowers
+ 1, uppers
+ 1,
3766 lowers
+ 2, uppers
+ 2,
3767 lowers
+ 3, uppers
+ 3);
3769 if ((num_vals
& 1) || num_vals
== 0)
3771 print_fatal_error(outfile
, "Bad rangeOfInteger value \"%s\" on line %d.",
3776 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
3781 case IPP_TAG_BEGIN_COLLECTION
:
3782 if (!strcmp(token
, "{"))
3784 ipp_t
*subcol
= get_collection(outfile
, vars
, fp
, linenum
);
3785 /* Collection value */
3789 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
3797 print_fatal_error(outfile
, "Bad collection value on line %d.", *linenum
);
3801 case IPP_TAG_STRING
:
3802 ippAddOctetString(col
, IPP_TAG_ZERO
, attr
, token
, (int)strlen(token
));
3806 if (!strchr(token
, ','))
3807 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
3811 * Multiple string values...
3814 int num_values
; /* Number of values */
3815 char *values
[100], /* Values */
3816 *ptr
; /* Pointer to next value */
3822 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
3825 values
[num_values
] = ptr
;
3829 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
3830 NULL
, (const char **)values
);
3840 * If we get here there was a parse error; free memory and return.
3852 * 'get_filename()' - Get a filename based on the current test file.
3855 static char * /* O - Filename */
3856 get_filename(const char *testfile
, /* I - Current test file */
3857 char *dst
, /* I - Destination filename */
3858 const char *src
, /* I - Source filename */
3859 size_t dstsize
) /* I - Size of destination buffer */
3861 char *dstptr
; /* Pointer into destination */
3862 _cups_globals_t
*cg
= _cupsGlobals();
3866 if (*src
== '<' && src
[strlen(src
) - 1] == '>')
3869 * Map <filename> to CUPS_DATADIR/ipptool/filename...
3872 snprintf(dst
, dstsize
, "%s/ipptool/%s", cg
->cups_datadir
, src
+ 1);
3873 dstptr
= dst
+ strlen(dst
) - 1;
3877 else if (*src
== '/' || !strchr(testfile
, '/')
3879 || (isalpha(*src
& 255) && src
[1] == ':')
3884 * Use the path as-is...
3887 strlcpy(dst
, src
, dstsize
);
3892 * Make path relative to testfile...
3895 strlcpy(dst
, testfile
, dstsize
);
3896 if ((dstptr
= strrchr(dst
, '/')) != NULL
)
3899 dstptr
= dst
; /* Should never happen */
3901 strlcpy(dstptr
, src
, dstsize
- (size_t)(dstptr
- dst
));
3909 * 'get_string()' - Get a pointer to a string value or the portion of interest.
3912 static char * /* O - Pointer to string */
3913 get_string(ipp_attribute_t
*attr
, /* I - IPP attribute */
3914 int element
, /* I - Element to fetch */
3915 int flags
, /* I - Value ("with") flags */
3916 char *buffer
, /* I - Temporary buffer */
3917 size_t bufsize
) /* I - Size of temporary buffer */
3919 char *ptr
, /* Value */
3920 scheme
[256], /* URI scheme */
3921 userpass
[256], /* Username/password */
3922 hostname
[256], /* Hostname */
3923 resource
[1024]; /* Resource */
3924 int port
; /* Port number */
3927 ptr
= attr
->values
[element
].string
.text
;
3929 if (flags
& _CUPS_WITH_HOSTNAME
)
3931 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ptr
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), buffer
, (int)bufsize
, &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
3934 ptr
= buffer
+ strlen(buffer
) - 1;
3935 if (ptr
>= buffer
&& *ptr
== '.')
3936 *ptr
= '\0'; /* Drop trailing "." */
3940 else if (flags
& _CUPS_WITH_RESOURCE
)
3942 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ptr
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, buffer
, (int)bufsize
) < HTTP_URI_STATUS_OK
)
3947 else if (flags
& _CUPS_WITH_SCHEME
)
3949 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ptr
, buffer
, (int)bufsize
, userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
3960 * 'get_token()' - Get a token from a file.
3963 static char * /* O - Token from file or NULL on EOF */
3964 get_token(FILE *fp
, /* I - File to read from */
3965 char *buf
, /* I - Buffer to read into */
3966 int buflen
, /* I - Length of buffer */
3967 int *linenum
) /* IO - Current line number */
3969 int ch
, /* Character from file */
3970 quote
; /* Quoting character */
3971 char *bufptr
, /* Pointer into buffer */
3972 *bufend
; /* End of buffer */
3978 * Skip whitespace...
3981 while (isspace(ch
= getc(fp
)))
3993 else if (ch
== '\'' || ch
== '\"')
3996 * Quoted text or regular expression...
4001 bufend
= buf
+ buflen
- 1;
4003 while ((ch
= getc(fp
)) != EOF
)
4008 * Escape next character...
4011 if (bufptr
< bufend
)
4012 *bufptr
++ = (char)ch
;
4014 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
4015 *bufptr
++ = (char)ch
;
4017 else if (ch
== quote
)
4019 else if (bufptr
< bufend
)
4020 *bufptr
++ = (char)ch
;
4033 while ((ch
= getc(fp
)) != EOF
)
4039 else if (ch
== '{' || ch
== '}' || ch
== ',')
4049 * Whitespace delimited text...
4055 bufend
= buf
+ buflen
- 1;
4057 while ((ch
= getc(fp
)) != EOF
)
4058 if (isspace(ch
) || ch
== '#')
4060 else if (bufptr
< bufend
)
4061 *bufptr
++ = (char)ch
;
4065 else if (ch
== '\n')
4077 * 'get_variable()' - Get the value of a variable.
4080 static char * /* O - Value or NULL */
4081 get_variable(_cups_vars_t
*vars
, /* I - Variables */
4082 const char *name
) /* I - Variable name */
4084 _cups_var_t key
, /* Search key */
4085 *match
; /* Matching variable, if any */
4088 key
.name
= (char *)name
;
4089 match
= cupsArrayFind(vars
->vars
, &key
);
4091 return (match
? match
->value
: NULL
);
4096 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
4100 static char * /* O - ISO 8601 date/time string */
4101 iso_date(ipp_uchar_t
*date
) /* I - IPP (RFC 1903) date/time value */
4103 time_t utctime
; /* UTC time since 1970 */
4104 struct tm
*utcdate
; /* UTC date/time */
4105 static char buffer
[255]; /* String buffer */
4108 utctime
= ippDateToTime(date
);
4109 utcdate
= gmtime(&utctime
);
4111 snprintf(buffer
, sizeof(buffer
), "%04d-%02d-%02dT%02d:%02d:%02dZ",
4112 utcdate
->tm_year
+ 1900, utcdate
->tm_mon
+ 1, utcdate
->tm_mday
,
4113 utcdate
->tm_hour
, utcdate
->tm_min
, utcdate
->tm_sec
);
4120 * 'password_cb()' - Password callback for authenticated tests.
4123 static const char * /* O - Password */
4124 password_cb(const char *prompt
) /* I - Prompt (unused) */
4128 if (PasswordTries
< 3)
4132 cupsSetUser(Username
);
4142 * 'pause_message()' - Display the message and pause until the user presses a key.
4146 pause_message(const char *message
) /* I - Message */
4149 HANDLE tty
; /* Console handle */
4150 DWORD mode
; /* Console mode */
4151 char key
; /* Key press */
4152 DWORD bytes
; /* Bytes read for key press */
4156 * Disable input echo and set raw input...
4159 if ((tty
= GetStdHandle(STD_INPUT_HANDLE
)) == INVALID_HANDLE_VALUE
)
4162 if (!GetConsoleMode(tty
, &mode
))
4165 if (!SetConsoleMode(tty
, 0))
4169 int tty
; /* /dev/tty - never read from stdin */
4170 struct termios original
, /* Original input mode */
4171 noecho
; /* No echo input mode */
4172 char key
; /* Current key press */
4176 * Disable input echo and set raw input...
4179 if ((tty
= open("/dev/tty", O_RDONLY
)) < 0)
4182 if (tcgetattr(tty
, &original
))
4189 noecho
.c_lflag
&= (tcflag_t
)~(ICANON
| ECHO
| ECHOE
| ISIG
);
4191 if (tcsetattr(tty
, TCSAFLUSH
, &noecho
))
4199 * Display the prompt...
4202 printf("%s\n---- PRESS ANY KEY ----", message
);
4210 ReadFile(tty
, &key
, 1, &bytes
, NULL
);
4216 SetConsoleMode(tty
, mode
);
4229 tcsetattr(tty
, TCSAFLUSH
, &original
);
4234 * Erase the "press any key" prompt...
4237 fputs("\r \r", stdout
);
4243 * 'print_attr()' - Print an attribute on the screen.
4247 print_attr(FILE *outfile
, /* I - Output file */
4248 int format
, /* I - Output format */
4249 ipp_attribute_t
*attr
, /* I - Attribute to print */
4250 ipp_tag_t
*group
) /* IO - Current group */
4252 int i
; /* Looping var */
4253 ipp_attribute_t
*colattr
; /* Collection attribute */
4256 if (format
== _CUPS_OUTPUT_PLIST
)
4258 if (!attr
->name
|| (group
&& *group
!= attr
->group_tag
))
4260 if (attr
->group_tag
!= IPP_TAG_ZERO
)
4262 fputs("</dict>\n", outfile
);
4263 fputs("<dict>\n", outfile
);
4267 *group
= attr
->group_tag
;
4273 print_xml_string(outfile
, "key", attr
->name
);
4274 if (attr
->num_values
> 1)
4275 fputs("<array>\n", outfile
);
4277 switch (attr
->value_tag
)
4279 case IPP_TAG_INTEGER
:
4281 for (i
= 0; i
< attr
->num_values
; i
++)
4282 fprintf(outfile
, "<integer>%d</integer>\n", attr
->values
[i
].integer
);
4285 case IPP_TAG_BOOLEAN
:
4286 for (i
= 0; i
< attr
->num_values
; i
++)
4287 fputs(attr
->values
[i
].boolean
? "<true />\n" : "<false />\n", outfile
);
4290 case IPP_TAG_RANGE
:
4291 for (i
= 0; i
< attr
->num_values
; i
++)
4292 fprintf(outfile
, "<dict><key>lower</key><integer>%d</integer>"
4293 "<key>upper</key><integer>%d</integer></dict>\n",
4294 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
4297 case IPP_TAG_RESOLUTION
:
4298 for (i
= 0; i
< attr
->num_values
; i
++)
4299 fprintf(outfile
, "<dict><key>xres</key><integer>%d</integer>"
4300 "<key>yres</key><integer>%d</integer>"
4301 "<key>units</key><string>%s</string></dict>\n",
4302 attr
->values
[i
].resolution
.xres
,
4303 attr
->values
[i
].resolution
.yres
,
4304 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
4309 for (i
= 0; i
< attr
->num_values
; i
++)
4310 fprintf(outfile
, "<date>%s</date>\n", iso_date(attr
->values
[i
].date
));
4313 case IPP_TAG_STRING
:
4314 for (i
= 0; i
< attr
->num_values
; i
++)
4316 char buffer
[IPP_MAX_LENGTH
* 5 / 4 + 1];
4319 fprintf(outfile
, "<data>%s</data>\n",
4320 httpEncode64_2(buffer
, sizeof(buffer
),
4321 attr
->values
[i
].unknown
.data
,
4322 attr
->values
[i
].unknown
.length
));
4328 case IPP_TAG_KEYWORD
:
4329 case IPP_TAG_CHARSET
:
4331 case IPP_TAG_MIMETYPE
:
4332 case IPP_TAG_LANGUAGE
:
4333 for (i
= 0; i
< attr
->num_values
; i
++)
4334 print_xml_string(outfile
, "string", attr
->values
[i
].string
.text
);
4337 case IPP_TAG_TEXTLANG
:
4338 case IPP_TAG_NAMELANG
:
4339 for (i
= 0; i
< attr
->num_values
; i
++)
4341 fputs("<dict><key>language</key><string>", outfile
);
4342 print_xml_string(outfile
, NULL
, attr
->values
[i
].string
.language
);
4343 fputs("</string><key>string</key><string>", outfile
);
4344 print_xml_string(outfile
, NULL
, attr
->values
[i
].string
.text
);
4345 fputs("</string></dict>\n", outfile
);
4349 case IPP_TAG_BEGIN_COLLECTION
:
4350 for (i
= 0; i
< attr
->num_values
; i
++)
4352 fputs("<dict>\n", outfile
);
4353 for (colattr
= attr
->values
[i
].collection
->attrs
;
4355 colattr
= colattr
->next
)
4356 print_attr(outfile
, format
, colattr
, NULL
);
4357 fputs("</dict>\n", outfile
);
4362 fprintf(outfile
, "<string><<%s>></string>\n", ippTagString(attr
->value_tag
));
4366 if (attr
->num_values
> 1)
4367 fputs("</array>\n", outfile
);
4371 char buffer
[8192]; /* Value buffer */
4373 if (format
== _CUPS_OUTPUT_TEST
)
4377 fputs(" -- separator --\n", outfile
);
4381 fprintf(outfile
, " %s (%s%s) = ", attr
->name
, attr
->num_values
> 1 ? "1setOf " : "", ippTagString(attr
->value_tag
));
4384 ippAttributeString(attr
, buffer
, sizeof(buffer
));
4385 fprintf(outfile
, "%s\n", buffer
);
4391 * 'print_csv()' - Print a line of CSV text.
4396 FILE *outfile
, /* I - Output file */
4397 ipp_attribute_t
*attr
, /* I - First attribute for line */
4398 int num_displayed
, /* I - Number of attributes to display */
4399 char **displayed
, /* I - Attributes to display */
4400 size_t *widths
) /* I - Column widths */
4402 int i
; /* Looping var */
4403 size_t maxlength
; /* Max length of all columns */
4404 char *buffer
, /* String buffer */
4405 *bufptr
; /* Pointer into buffer */
4406 ipp_attribute_t
*current
; /* Current attribute */
4410 * Get the maximum string length we have to show and allocate...
4413 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4414 if (widths
[i
] > maxlength
)
4415 maxlength
= widths
[i
];
4419 if ((buffer
= malloc(maxlength
)) == NULL
)
4423 * Loop through the attributes to display...
4428 for (i
= 0; i
< num_displayed
; i
++)
4431 fputc(',', outfile
);
4435 for (current
= attr
; current
; current
= current
->next
)
4439 else if (!strcmp(current
->name
, displayed
[i
]))
4441 ippAttributeString(current
, buffer
, maxlength
);
4446 if (strchr(buffer
, ',') != NULL
|| strchr(buffer
, '\"') != NULL
||
4447 strchr(buffer
, '\\') != NULL
)
4449 putc('\"', outfile
);
4450 for (bufptr
= buffer
; *bufptr
; bufptr
++)
4452 if (*bufptr
== '\\' || *bufptr
== '\"')
4453 putc('\\', outfile
);
4454 putc(*bufptr
, outfile
);
4456 putc('\"', outfile
);
4459 fputs(buffer
, outfile
);
4461 putc('\n', outfile
);
4465 for (i
= 0; i
< num_displayed
; i
++)
4470 fputs(displayed
[i
], outfile
);
4472 putc('\n', outfile
);
4480 * 'print_fatal_error()' - Print a fatal error message.
4484 print_fatal_error(FILE *outfile
, /* I - Output file */
4485 const char *s
, /* I - Printf-style format string */
4486 ...) /* I - Additional arguments as needed */
4488 char buffer
[10240]; /* Format buffer */
4489 va_list ap
; /* Pointer to arguments */
4493 * Format the error message...
4497 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
4504 if (Output
== _CUPS_OUTPUT_PLIST
)
4506 print_xml_header(outfile
);
4507 print_xml_trailer(outfile
, 0, buffer
);
4510 _cupsLangPrintf(stderr
, "ipptool: %s", buffer
);
4515 * 'print_line()' - Print a line of formatted or CSV text.
4520 FILE *outfile
, /* I - Output file */
4521 ipp_attribute_t
*attr
, /* I - First attribute for line */
4522 int num_displayed
, /* I - Number of attributes to display */
4523 char **displayed
, /* I - Attributes to display */
4524 size_t *widths
) /* I - Column widths */
4526 int i
; /* Looping var */
4527 size_t maxlength
; /* Max length of all columns */
4528 char *buffer
; /* String buffer */
4529 ipp_attribute_t
*current
; /* Current attribute */
4533 * Get the maximum string length we have to show and allocate...
4536 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4537 if (widths
[i
] > maxlength
)
4538 maxlength
= widths
[i
];
4542 if ((buffer
= malloc(maxlength
)) == NULL
)
4546 * Loop through the attributes to display...
4551 for (i
= 0; i
< num_displayed
; i
++)
4558 for (current
= attr
; current
; current
= current
->next
)
4562 else if (!strcmp(current
->name
, displayed
[i
]))
4564 ippAttributeString(current
, buffer
, maxlength
);
4569 fprintf(outfile
, "%*s", (int)-widths
[i
], buffer
);
4571 putc('\n', outfile
);
4575 for (i
= 0; i
< num_displayed
; i
++)
4580 fprintf(outfile
, "%*s", (int)-widths
[i
], displayed
[i
]);
4582 putc('\n', outfile
);
4584 for (i
= 0; i
< num_displayed
; i
++)
4589 memset(buffer
, '-', widths
[i
]);
4590 buffer
[widths
[i
]] = '\0';
4591 fputs(buffer
, outfile
);
4593 putc('\n', outfile
);
4601 * 'print_xml_header()' - Print a standard XML plist header.
4605 print_xml_header(FILE *outfile
) /* I - Output file */
4609 fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", outfile
);
4610 fputs("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
4611 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n", outfile
);
4612 fputs("<plist version=\"1.0\">\n", outfile
);
4613 fputs("<dict>\n", outfile
);
4614 fputs("<key>ipptoolVersion</key>\n", outfile
);
4615 fputs("<string>" CUPS_SVERSION
"</string>\n", outfile
);
4616 fputs("<key>Transfer</key>\n", outfile
);
4617 fprintf(outfile
, "<string>%s</string>\n",
4618 Transfer
== _CUPS_TRANSFER_AUTO
? "auto" :
4619 Transfer
== _CUPS_TRANSFER_CHUNKED
? "chunked" : "length");
4620 fputs("<key>Tests</key>\n", outfile
);
4621 fputs("<array>\n", outfile
);
4629 * 'print_xml_string()' - Print an XML string with escaping.
4633 print_xml_string(FILE *outfile
, /* I - Output file */
4634 const char *element
, /* I - Element name or NULL */
4635 const char *s
) /* I - String to print */
4638 fprintf(outfile
, "<%s>", element
);
4643 fputs("&", outfile
);
4645 fputs("<", outfile
);
4647 fputs(">", outfile
);
4648 else if ((*s
& 0xe0) == 0xc0)
4651 * Validate UTF-8 two-byte sequence...
4654 if ((s
[1] & 0xc0) != 0x80)
4661 putc(*s
++, outfile
);
4665 else if ((*s
& 0xf0) == 0xe0)
4668 * Validate UTF-8 three-byte sequence...
4671 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80)
4678 putc(*s
++, outfile
);
4679 putc(*s
++, outfile
);
4683 else if ((*s
& 0xf8) == 0xf0)
4686 * Validate UTF-8 four-byte sequence...
4689 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80 ||
4690 (s
[3] & 0xc0) != 0x80)
4697 putc(*s
++, outfile
);
4698 putc(*s
++, outfile
);
4699 putc(*s
++, outfile
);
4703 else if ((*s
& 0x80) || (*s
< ' ' && !isspace(*s
& 255)))
4706 * Invalid control character...
4718 fprintf(outfile
, "</%s>\n", element
);
4723 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
4727 print_xml_trailer(FILE *outfile
, /* I - Output file */
4728 int success
, /* I - 1 on success, 0 on failure */
4729 const char *message
) /* I - Error message or NULL */
4733 fputs("</array>\n", outfile
);
4734 fputs("<key>Successful</key>\n", outfile
);
4735 fputs(success
? "<true />\n" : "<false />\n", outfile
);
4738 fputs("<key>ErrorMessage</key>\n", outfile
);
4739 print_xml_string(outfile
, "string", message
);
4741 fputs("</dict>\n", outfile
);
4742 fputs("</plist>\n", outfile
);
4750 * 'set_variable()' - Set a variable value.
4754 set_variable(FILE *outfile
, /* I - Output file */
4755 _cups_vars_t
*vars
, /* I - Variables */
4756 const char *name
, /* I - Variable name */
4757 const char *value
) /* I - Value string */
4759 _cups_var_t key
, /* Search key */
4760 *var
; /* New variable */
4763 if (!_cups_strcasecmp(name
, "filename"))
4766 free(vars
->filename
);
4768 vars
->filename
= strdup(value
);
4772 key
.name
= (char *)name
;
4773 if ((var
= cupsArrayFind(vars
->vars
, &key
)) != NULL
)
4776 var
->value
= strdup(value
);
4778 else if ((var
= malloc(sizeof(_cups_var_t
))) == NULL
)
4780 print_fatal_error(outfile
, "Unable to allocate memory for variable \"%s\".", name
);
4785 var
->name
= strdup(name
);
4786 var
->value
= strdup(value
);
4788 cupsArrayAdd(vars
->vars
, var
);
4795 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
4799 sigterm_handler(int sig
) /* I - Signal number (unused) */
4805 signal(SIGINT
, SIG_DFL
);
4806 signal(SIGTERM
, SIG_DFL
);
4812 * 'timeout_cb()' - Handle HTTP timeouts.
4815 static int /* O - 1 to continue, 0 to cancel */
4816 timeout_cb(http_t
*http
, /* I - Connection to server */
4817 void *user_data
) /* I - User data (unused) */
4819 int buffered
= 0; /* Bytes buffered but not yet sent */
4825 * If the socket still have data waiting to be sent to the printer (as can
4826 * happen if the printer runs out of paper), continue to wait until the output
4827 * buffer is empty...
4830 #ifdef SO_NWRITE /* OS X and some versions of Linux */
4831 socklen_t len
= sizeof(buffered
); /* Size of return value */
4833 if (getsockopt(httpGetFd(http
), SOL_SOCKET
, SO_NWRITE
, &buffered
, &len
))
4836 #elif defined(SIOCOUTQ) /* Others except Windows */
4837 if (ioctl(httpGetFd(http
), SIOCOUTQ
, &buffered
))
4840 #else /* Windows (not possible) */
4842 #endif /* SO_NWRITE */
4844 return (buffered
> 0);
4849 * 'usage()' - Show program usage.
4855 _cupsLangPuts(stderr
, _("Usage: ipptool [options] URI filename [ ... "
4857 _cupsLangPuts(stderr
, _("Options:"));
4858 _cupsLangPuts(stderr
, _(" --help Show help."));
4859 _cupsLangPuts(stderr
, _(" --stop-after-include-error\n"
4860 " Stop tests after a failed INCLUDE."));
4861 _cupsLangPuts(stderr
, _(" --version Show version."));
4862 _cupsLangPuts(stderr
, _(" -4 Connect using IPv4."));
4863 _cupsLangPuts(stderr
, _(" -6 Connect using IPv6."));
4864 _cupsLangPuts(stderr
, _(" -C Send requests using "
4865 "chunking (default)."));
4866 _cupsLangPuts(stdout
, _(" -E Test with HTTP Upgrade to "
4868 _cupsLangPuts(stderr
, _(" -I Ignore errors."));
4869 _cupsLangPuts(stderr
, _(" -L Send requests using "
4870 "content-length."));
4871 _cupsLangPuts(stderr
, _(" -P filename.plist Produce XML plist to a file and test report to standard output."));
4872 _cupsLangPuts(stderr
, _(" -S Test with SSL "
4874 _cupsLangPuts(stderr
, _(" -T seconds Set the receive/send "
4875 "timeout in seconds."));
4876 _cupsLangPuts(stderr
, _(" -V version Set default IPP "
4878 _cupsLangPuts(stderr
, _(" -X Produce XML plist instead "
4880 _cupsLangPuts(stderr
, _(" -c Produce CSV output."));
4881 _cupsLangPuts(stderr
, _(" -d name=value Set named variable to "
4883 _cupsLangPuts(stderr
, _(" -f filename Set default request "
4885 _cupsLangPuts(stderr
, _(" -i seconds Repeat the last file with "
4886 "the given time interval."));
4887 _cupsLangPuts(stderr
, _(" -l Produce plain text output."));
4888 _cupsLangPuts(stderr
, _(" -n count Repeat the last file the "
4889 "given number of times."));
4890 _cupsLangPuts(stderr
, _(" -q Run silently."));
4891 _cupsLangPuts(stderr
, _(" -t Produce a test report."));
4892 _cupsLangPuts(stderr
, _(" -v Be verbose."));
4899 * 'validate_attr()' - Determine whether an attribute is valid.
4902 static int /* O - 1 if valid, 0 otherwise */
4903 validate_attr(FILE *outfile
, /* I - Output file */
4904 cups_array_t
*errors
, /* I - Errors array */
4905 ipp_attribute_t
*attr
) /* I - Attribute to validate */
4907 int i
; /* Looping var */
4908 char scheme
[64], /* Scheme from URI */
4909 userpass
[256], /* Username/password from URI */
4910 hostname
[256], /* Hostname from URI */
4911 resource
[1024]; /* Resource from URI */
4912 int port
, /* Port number from URI */
4913 uri_status
, /* URI separation status */
4914 valid
= 1; /* Is the attribute valid? */
4915 const char *ptr
; /* Pointer into string */
4916 ipp_attribute_t
*colattr
; /* Collection attribute */
4917 regex_t re
; /* Regular expression */
4918 ipp_uchar_t
*date
; /* Current date value */
4929 * Validate the attribute name.
4932 for (ptr
= attr
->name
; *ptr
; ptr
++)
4933 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' && *ptr
!= '_')
4936 if (*ptr
|| ptr
== attr
->name
)
4941 "\"%s\": Bad attribute name - invalid character "
4942 "(RFC 2911 section 4.1.3).", attr
->name
);
4945 if ((ptr
- attr
->name
) > 255)
4950 "\"%s\": Bad attribute name - bad length "
4951 "(RFC 2911 section 4.1.3).", attr
->name
);
4954 switch (attr
->value_tag
)
4956 case IPP_TAG_INTEGER
:
4959 case IPP_TAG_BOOLEAN
:
4960 for (i
= 0; i
< attr
->num_values
; i
++)
4962 if (attr
->values
[i
].boolean
!= 0 &&
4963 attr
->values
[i
].boolean
!= 1)
4968 "\"%s\": Bad boolen value %d "
4969 "(RFC 2911 section 4.1.11).", attr
->name
,
4970 attr
->values
[i
].boolean
);
4976 for (i
= 0; i
< attr
->num_values
; i
++)
4978 if (attr
->values
[i
].integer
< 1)
4983 "\"%s\": Bad enum value %d - out of range "
4984 "(RFC 2911 section 4.1.4).", attr
->name
,
4985 attr
->values
[i
].integer
);
4990 case IPP_TAG_STRING
:
4991 for (i
= 0; i
< attr
->num_values
; i
++)
4993 if (attr
->values
[i
].unknown
.length
> IPP_MAX_OCTETSTRING
)
4998 "\"%s\": Bad octetString value - bad length %d "
4999 "(RFC 2911 section 4.1.10).", attr
->name
,
5000 attr
->values
[i
].unknown
.length
);
5006 for (i
= 0; i
< attr
->num_values
; i
++)
5008 date
= attr
->values
[i
].date
;
5010 if (date
[2] < 1 || date
[2] > 12)
5015 "\"%s\": Bad dateTime month %u "
5016 "(RFC 2911 section 4.1.14).", attr
->name
, date
[2]);
5019 if (date
[3] < 1 || date
[3] > 31)
5024 "\"%s\": Bad dateTime day %u "
5025 "(RFC 2911 section 4.1.14).", attr
->name
, date
[3]);
5033 "\"%s\": Bad dateTime hours %u "
5034 "(RFC 2911 section 4.1.14).", attr
->name
, date
[4]);
5042 "\"%s\": Bad dateTime minutes %u "
5043 "(RFC 2911 section 4.1.14).", attr
->name
, date
[5]);
5051 "\"%s\": Bad dateTime seconds %u "
5052 "(RFC 2911 section 4.1.14).", attr
->name
, date
[6]);
5060 "\"%s\": Bad dateTime deciseconds %u "
5061 "(RFC 2911 section 4.1.14).", attr
->name
, date
[7]);
5064 if (date
[8] != '-' && date
[8] != '+')
5069 "\"%s\": Bad dateTime UTC sign '%c' "
5070 "(RFC 2911 section 4.1.14).", attr
->name
, date
[8]);
5078 "\"%s\": Bad dateTime UTC hours %u "
5079 "(RFC 2911 section 4.1.14).", attr
->name
, date
[9]);
5087 "\"%s\": Bad dateTime UTC minutes %u "
5088 "(RFC 2911 section 4.1.14).", attr
->name
, date
[10]);
5093 case IPP_TAG_RESOLUTION
:
5094 for (i
= 0; i
< attr
->num_values
; i
++)
5096 if (attr
->values
[i
].resolution
.xres
<= 0)
5101 "\"%s\": Bad resolution value %dx%d%s - cross "
5102 "feed resolution must be positive "
5103 "(RFC 2911 section 4.1.15).", attr
->name
,
5104 attr
->values
[i
].resolution
.xres
,
5105 attr
->values
[i
].resolution
.yres
,
5106 attr
->values
[i
].resolution
.units
==
5107 IPP_RES_PER_INCH
? "dpi" :
5108 attr
->values
[i
].resolution
.units
==
5109 IPP_RES_PER_CM
? "dpcm" : "unknown");
5112 if (attr
->values
[i
].resolution
.yres
<= 0)
5117 "\"%s\": Bad resolution value %dx%d%s - feed "
5118 "resolution must be positive "
5119 "(RFC 2911 section 4.1.15).", attr
->name
,
5120 attr
->values
[i
].resolution
.xres
,
5121 attr
->values
[i
].resolution
.yres
,
5122 attr
->values
[i
].resolution
.units
==
5123 IPP_RES_PER_INCH
? "dpi" :
5124 attr
->values
[i
].resolution
.units
==
5125 IPP_RES_PER_CM
? "dpcm" : "unknown");
5128 if (attr
->values
[i
].resolution
.units
!= IPP_RES_PER_INCH
&&
5129 attr
->values
[i
].resolution
.units
!= IPP_RES_PER_CM
)
5134 "\"%s\": Bad resolution value %dx%d%s - bad "
5135 "units value (RFC 2911 section 4.1.15).",
5136 attr
->name
, attr
->values
[i
].resolution
.xres
,
5137 attr
->values
[i
].resolution
.yres
,
5138 attr
->values
[i
].resolution
.units
==
5139 IPP_RES_PER_INCH
? "dpi" :
5140 attr
->values
[i
].resolution
.units
==
5141 IPP_RES_PER_CM
? "dpcm" : "unknown");
5146 case IPP_TAG_RANGE
:
5147 for (i
= 0; i
< attr
->num_values
; i
++)
5149 if (attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
5154 "\"%s\": Bad rangeOfInteger value %d-%d - lower "
5155 "greater than upper (RFC 2911 section 4.1.13).",
5156 attr
->name
, attr
->values
[i
].range
.lower
,
5157 attr
->values
[i
].range
.upper
);
5162 case IPP_TAG_BEGIN_COLLECTION
:
5163 for (i
= 0; i
< attr
->num_values
; i
++)
5165 for (colattr
= attr
->values
[i
].collection
->attrs
;
5167 colattr
= colattr
->next
)
5169 if (!validate_attr(outfile
, NULL
, colattr
))
5176 if (colattr
&& errors
)
5178 add_stringf(errors
, "\"%s\": Bad collection value.", attr
->name
);
5182 validate_attr(outfile
, errors
, colattr
);
5183 colattr
= colattr
->next
;
5190 case IPP_TAG_TEXTLANG
:
5191 for (i
= 0; i
< attr
->num_values
; i
++)
5193 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5195 if ((*ptr
& 0xe0) == 0xc0)
5198 if ((*ptr
& 0xc0) != 0x80)
5201 else if ((*ptr
& 0xf0) == 0xe0)
5204 if ((*ptr
& 0xc0) != 0x80)
5207 if ((*ptr
& 0xc0) != 0x80)
5210 else if ((*ptr
& 0xf8) == 0xf0)
5213 if ((*ptr
& 0xc0) != 0x80)
5216 if ((*ptr
& 0xc0) != 0x80)
5219 if ((*ptr
& 0xc0) != 0x80)
5222 else if (*ptr
& 0x80)
5231 "\"%s\": Bad text value \"%s\" - bad UTF-8 "
5232 "sequence (RFC 2911 section 4.1.1).", attr
->name
,
5233 attr
->values
[i
].string
.text
);
5236 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_TEXT
- 1))
5241 "\"%s\": Bad text value \"%s\" - bad length %d "
5242 "(RFC 2911 section 4.1.1).", attr
->name
,
5243 attr
->values
[i
].string
.text
,
5244 (int)strlen(attr
->values
[i
].string
.text
));
5250 case IPP_TAG_NAMELANG
:
5251 for (i
= 0; i
< attr
->num_values
; i
++)
5253 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5255 if ((*ptr
& 0xe0) == 0xc0)
5258 if ((*ptr
& 0xc0) != 0x80)
5261 else if ((*ptr
& 0xf0) == 0xe0)
5264 if ((*ptr
& 0xc0) != 0x80)
5267 if ((*ptr
& 0xc0) != 0x80)
5270 else if ((*ptr
& 0xf8) == 0xf0)
5273 if ((*ptr
& 0xc0) != 0x80)
5276 if ((*ptr
& 0xc0) != 0x80)
5279 if ((*ptr
& 0xc0) != 0x80)
5282 else if (*ptr
& 0x80)
5291 "\"%s\": Bad name value \"%s\" - bad UTF-8 "
5292 "sequence (RFC 2911 section 4.1.2).", attr
->name
,
5293 attr
->values
[i
].string
.text
);
5296 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_NAME
- 1))
5301 "\"%s\": Bad name value \"%s\" - bad length %d "
5302 "(RFC 2911 section 4.1.2).", attr
->name
,
5303 attr
->values
[i
].string
.text
,
5304 (int)strlen(attr
->values
[i
].string
.text
));
5309 case IPP_TAG_KEYWORD
:
5310 for (i
= 0; i
< attr
->num_values
; i
++)
5312 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5313 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' &&
5317 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5322 "\"%s\": Bad keyword value \"%s\" - invalid "
5323 "character (RFC 2911 section 4.1.3).",
5324 attr
->name
, attr
->values
[i
].string
.text
);
5327 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_KEYWORD
- 1))
5332 "\"%s\": Bad keyword value \"%s\" - bad "
5333 "length %d (RFC 2911 section 4.1.3).",
5334 attr
->name
, attr
->values
[i
].string
.text
,
5335 (int)strlen(attr
->values
[i
].string
.text
));
5341 for (i
= 0; i
< attr
->num_values
; i
++)
5343 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
,
5344 attr
->values
[i
].string
.text
,
5345 scheme
, sizeof(scheme
),
5346 userpass
, sizeof(userpass
),
5347 hostname
, sizeof(hostname
),
5348 &port
, resource
, sizeof(resource
));
5350 if (uri_status
< HTTP_URI_OK
)
5355 "\"%s\": Bad URI value \"%s\" - %s "
5356 "(RFC 2911 section 4.1.5).", attr
->name
,
5357 attr
->values
[i
].string
.text
,
5358 httpURIStatusString(uri_status
));
5361 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_URI
- 1))
5366 "\"%s\": Bad URI value \"%s\" - bad length %d "
5367 "(RFC 2911 section 4.1.5).", attr
->name
,
5368 attr
->values
[i
].string
.text
,
5369 (int)strlen(attr
->values
[i
].string
.text
));
5374 case IPP_TAG_URISCHEME
:
5375 for (i
= 0; i
< attr
->num_values
; i
++)
5377 ptr
= attr
->values
[i
].string
.text
;
5378 if (islower(*ptr
& 255))
5380 for (ptr
++; *ptr
; ptr
++)
5381 if (!islower(*ptr
& 255) && !isdigit(*ptr
& 255) &&
5382 *ptr
!= '+' && *ptr
!= '-' && *ptr
!= '.')
5386 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5391 "\"%s\": Bad uriScheme value \"%s\" - bad "
5392 "characters (RFC 2911 section 4.1.6).",
5393 attr
->name
, attr
->values
[i
].string
.text
);
5396 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_URISCHEME
- 1))
5401 "\"%s\": Bad uriScheme value \"%s\" - bad "
5402 "length %d (RFC 2911 section 4.1.6).",
5403 attr
->name
, attr
->values
[i
].string
.text
,
5404 (int)strlen(attr
->values
[i
].string
.text
));
5409 case IPP_TAG_CHARSET
:
5410 for (i
= 0; i
< attr
->num_values
; i
++)
5412 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5413 if (!isprint(*ptr
& 255) || isupper(*ptr
& 255) ||
5414 isspace(*ptr
& 255))
5417 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5422 "\"%s\": Bad charset value \"%s\" - bad "
5423 "characters (RFC 2911 section 4.1.7).",
5424 attr
->name
, attr
->values
[i
].string
.text
);
5427 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_CHARSET
- 1))
5432 "\"%s\": Bad charset value \"%s\" - bad "
5433 "length %d (RFC 2911 section 4.1.7).",
5434 attr
->name
, attr
->values
[i
].string
.text
,
5435 (int)strlen(attr
->values
[i
].string
.text
));
5440 case IPP_TAG_LANGUAGE
:
5442 * The following regular expression is derived from the ABNF for
5443 * language tags in RFC 4646. All I can say is that this is the
5444 * easiest way to check the values...
5447 if ((i
= regcomp(&re
,
5449 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
5451 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
5452 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
5453 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
5454 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
5455 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
5457 "x(-[a-z0-9]{1,8})+" /* privateuse */
5459 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
5461 REG_NOSUB
| REG_EXTENDED
)) != 0)
5463 char temp
[256]; /* Temporary error string */
5465 regerror(i
, &re
, temp
, sizeof(temp
));
5466 print_fatal_error(outfile
, "Unable to compile naturalLanguage regular "
5467 "expression: %s.", temp
);
5471 for (i
= 0; i
< attr
->num_values
; i
++)
5473 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5478 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5479 "characters (RFC 2911 section 4.1.8).",
5480 attr
->name
, attr
->values
[i
].string
.text
);
5483 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_LANGUAGE
- 1))
5488 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5489 "length %d (RFC 2911 section 4.1.8).",
5490 attr
->name
, attr
->values
[i
].string
.text
,
5491 (int)strlen(attr
->values
[i
].string
.text
));
5498 case IPP_TAG_MIMETYPE
:
5500 * The following regular expression is derived from the ABNF for
5501 * language tags in RFC 2045 and 4288. All I can say is that this is
5502 * the easiest way to check the values...
5505 if ((i
= regcomp(&re
,
5507 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
5509 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
5510 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
5511 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
5514 REG_NOSUB
| REG_EXTENDED
)) != 0)
5516 char temp
[256]; /* Temporary error string */
5518 regerror(i
, &re
, temp
, sizeof(temp
));
5519 print_fatal_error(outfile
, "Unable to compile mimeMediaType regular "
5520 "expression: %s.", temp
);
5524 for (i
= 0; i
< attr
->num_values
; i
++)
5526 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5531 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5532 "characters (RFC 2911 section 4.1.9).",
5533 attr
->name
, attr
->values
[i
].string
.text
);
5536 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_MIMETYPE
- 1))
5541 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5542 "length %d (RFC 2911 section 4.1.9).",
5543 attr
->name
, attr
->values
[i
].string
.text
,
5544 (int)strlen(attr
->values
[i
].string
.text
));
5560 * 'with_value()' - Test a WITH-VALUE predicate.
5563 static int /* O - 1 on match, 0 on non-match */
5564 with_value(FILE *outfile
, /* I - Output file */
5565 cups_array_t
*errors
, /* I - Errors array */
5566 char *value
, /* I - Value string */
5567 int flags
, /* I - Flags for match */
5568 ipp_attribute_t
*attr
, /* I - Attribute to compare */
5569 char *matchbuf
, /* I - Buffer to hold matching value */
5570 size_t matchlen
) /* I - Length of match buffer */
5572 int i
, /* Looping var */
5574 char temp
[1024], /* Temporary value string */
5575 *valptr
; /* Pointer into value */
5579 match
= (flags
& _CUPS_WITH_ALL
) ? 1 : 0;
5582 * NULL matches everything.
5585 if (!value
|| !*value
)
5589 * Compare the value string to the attribute value.
5592 switch (attr
->value_tag
)
5594 case IPP_TAG_INTEGER
:
5596 for (i
= 0; i
< attr
->num_values
; i
++)
5598 char op
, /* Comparison operator */
5599 *nextptr
; /* Next pointer */
5600 int intvalue
, /* Integer value */
5601 valmatch
= 0; /* Does the current value match? */
5605 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5606 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5607 *valptr
== '=' || *valptr
== '>')
5610 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5612 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5620 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
5621 if (nextptr
== valptr
)
5625 if ((op
== '=' && attr
->values
[i
].integer
== intvalue
) ||
5626 (op
== '<' && attr
->values
[i
].integer
< intvalue
) ||
5627 (op
== '>' && attr
->values
[i
].integer
> intvalue
))
5630 snprintf(matchbuf
, matchlen
, "%d", attr
->values
[i
].integer
);
5637 if (flags
& _CUPS_WITH_ALL
)
5652 if (!match
&& errors
)
5654 for (i
= 0; i
< attr
->num_values
; i
++)
5655 add_stringf(errors
, "GOT: %s=%d", attr
->name
,
5656 attr
->values
[i
].integer
);
5660 case IPP_TAG_RANGE
:
5661 for (i
= 0; i
< attr
->num_values
; i
++)
5663 char op
, /* Comparison operator */
5664 *nextptr
; /* Next pointer */
5665 int intvalue
, /* Integer value */
5666 valmatch
= 0; /* Does the current value match? */
5670 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5671 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5672 *valptr
== '=' || *valptr
== '>')
5675 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5677 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5685 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
5686 if (nextptr
== valptr
)
5690 if ((op
== '=' && (attr
->values
[i
].range
.lower
== intvalue
||
5691 attr
->values
[i
].range
.upper
== intvalue
)) ||
5692 (op
== '<' && attr
->values
[i
].range
.upper
< intvalue
) ||
5693 (op
== '>' && attr
->values
[i
].range
.upper
> intvalue
))
5696 snprintf(matchbuf
, matchlen
, "%d-%d",
5697 attr
->values
[0].range
.lower
,
5698 attr
->values
[0].range
.upper
);
5705 if (flags
& _CUPS_WITH_ALL
)
5720 if (!match
&& errors
)
5722 for (i
= 0; i
< attr
->num_values
; i
++)
5723 add_stringf(errors
, "GOT: %s=%d-%d", attr
->name
,
5724 attr
->values
[i
].range
.lower
,
5725 attr
->values
[i
].range
.upper
);
5729 case IPP_TAG_BOOLEAN
:
5730 for (i
= 0; i
< attr
->num_values
; i
++)
5732 if (!strcmp(value
, "true") == attr
->values
[i
].boolean
)
5735 strlcpy(matchbuf
, value
, matchlen
);
5737 if (!(flags
& _CUPS_WITH_ALL
))
5743 else if (flags
& _CUPS_WITH_ALL
)
5750 if (!match
&& errors
)
5752 for (i
= 0; i
< attr
->num_values
; i
++)
5753 add_stringf(errors
, "GOT: %s=%s", attr
->name
,
5754 attr
->values
[i
].boolean
? "true" : "false");
5758 case IPP_TAG_RESOLUTION
:
5759 for (i
= 0; i
< attr
->num_values
; i
++)
5761 if (attr
->values
[i
].resolution
.xres
==
5762 attr
->values
[i
].resolution
.yres
)
5763 snprintf(temp
, sizeof(temp
), "%d%s",
5764 attr
->values
[i
].resolution
.xres
,
5765 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5768 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5769 attr
->values
[i
].resolution
.xres
,
5770 attr
->values
[i
].resolution
.yres
,
5771 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5774 if (!strcmp(value
, temp
))
5777 strlcpy(matchbuf
, value
, matchlen
);
5779 if (!(flags
& _CUPS_WITH_ALL
))
5785 else if (flags
& _CUPS_WITH_ALL
)
5792 if (!match
&& errors
)
5794 for (i
= 0; i
< attr
->num_values
; i
++)
5796 if (attr
->values
[i
].resolution
.xres
==
5797 attr
->values
[i
].resolution
.yres
)
5798 snprintf(temp
, sizeof(temp
), "%d%s",
5799 attr
->values
[i
].resolution
.xres
,
5800 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5803 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5804 attr
->values
[i
].resolution
.xres
,
5805 attr
->values
[i
].resolution
.yres
,
5806 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5809 if (strcmp(value
, temp
))
5810 add_stringf(errors
, "GOT: %s=%s", attr
->name
, temp
);
5815 case IPP_TAG_NOVALUE
:
5816 case IPP_TAG_UNKNOWN
:
5819 case IPP_TAG_CHARSET
:
5820 case IPP_TAG_KEYWORD
:
5821 case IPP_TAG_LANGUAGE
:
5822 case IPP_TAG_MIMETYPE
:
5824 case IPP_TAG_NAMELANG
:
5826 case IPP_TAG_TEXTLANG
:
5828 case IPP_TAG_URISCHEME
:
5829 if (flags
& _CUPS_WITH_REGEX
)
5832 * Value is an extended, case-sensitive POSIX regular expression...
5835 regex_t re
; /* Regular expression */
5837 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
5839 regerror(i
, &re
, temp
, sizeof(temp
));
5841 print_fatal_error(outfile
, "Unable to compile WITH-VALUE regular expression "
5842 "\"%s\" - %s", value
, temp
);
5847 * See if ALL of the values match the given regular expression.
5850 for (i
= 0; i
< attr
->num_values
; i
++)
5852 if (!regexec(&re
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5857 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5860 if (!(flags
& _CUPS_WITH_ALL
))
5866 else if (flags
& _CUPS_WITH_ALL
)
5878 * Value is a literal string, see if the value(s) match...
5881 for (i
= 0; i
< attr
->num_values
; i
++)
5883 if (!strcmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
))))
5887 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5890 if (!(flags
& _CUPS_WITH_ALL
))
5896 else if (flags
& _CUPS_WITH_ALL
)
5904 if (!match
&& errors
)
5906 for (i
= 0; i
< attr
->num_values
; i
++)
5907 add_stringf(errors
, "GOT: %s=\"%s\"", attr
->name
,
5908 attr
->values
[i
].string
.text
);
5921 * 'with_value_from()' - Test a WITH-VALUE-FROM predicate.
5924 static int /* O - 1 on match, 0 on non-match */
5926 cups_array_t
*errors
, /* I - Errors array */
5927 ipp_attribute_t
*fromattr
, /* I - "From" attribute */
5928 ipp_attribute_t
*attr
, /* I - Attribute to compare */
5929 char *matchbuf
, /* I - Buffer to hold matching value */
5930 size_t matchlen
) /* I - Length of match buffer */
5932 int i
, j
, /* Looping vars */
5933 count
= ippGetCount(attr
), /* Number of attribute values */
5934 match
= 1; /* Match? */
5940 * Compare the from value(s) to the attribute value(s)...
5943 switch (ippGetValueTag(attr
))
5945 case IPP_TAG_INTEGER
:
5946 if (ippGetValueTag(fromattr
) != IPP_TAG_INTEGER
&& ippGetValueTag(fromattr
) != IPP_TAG_RANGE
)
5947 goto wrong_value_tag
;
5949 for (i
= 0; i
< count
; i
++)
5951 int value
= ippGetInteger(attr
, i
);
5952 /* Current integer value */
5954 if (ippContainsInteger(fromattr
, value
))
5957 snprintf(matchbuf
, matchlen
, "%d", value
);
5961 add_stringf(errors
, "GOT: %s=%d", ippGetName(attr
), value
);
5968 if (ippGetValueTag(fromattr
) != IPP_TAG_ENUM
)
5969 goto wrong_value_tag
;
5971 for (i
= 0; i
< count
; i
++)
5973 int value
= ippGetInteger(attr
, i
);
5974 /* Current integer value */
5976 if (ippContainsInteger(fromattr
, value
))
5979 snprintf(matchbuf
, matchlen
, "%d", value
);
5983 add_stringf(errors
, "GOT: %s=%d", ippGetName(attr
), value
);
5989 case IPP_TAG_RESOLUTION
:
5990 if (ippGetValueTag(fromattr
) != IPP_TAG_RESOLUTION
)
5991 goto wrong_value_tag
;
5993 for (i
= 0; i
< count
; i
++)
5997 int fromcount
= ippGetCount(fromattr
);
5998 int fromxres
, fromyres
;
5999 ipp_res_t fromunits
;
6001 xres
= ippGetResolution(attr
, i
, &yres
, &units
);
6003 for (j
= 0; j
< fromcount
; j
++)
6005 fromxres
= ippGetResolution(fromattr
, j
, &fromyres
, &fromunits
);
6006 if (fromxres
== xres
&& fromyres
== yres
&& fromunits
== units
)
6015 snprintf(matchbuf
, matchlen
, "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6017 snprintf(matchbuf
, matchlen
, "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6023 add_stringf(errors
, "GOT: %s=%d%s", ippGetName(attr
), xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6025 add_stringf(errors
, "GOT: %s=%dx%d%s", ippGetName(attr
), xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6032 case IPP_TAG_NOVALUE
:
6033 case IPP_TAG_UNKNOWN
:
6036 case IPP_TAG_CHARSET
:
6037 case IPP_TAG_KEYWORD
:
6038 case IPP_TAG_LANGUAGE
:
6039 case IPP_TAG_MIMETYPE
:
6041 case IPP_TAG_NAMELANG
:
6043 case IPP_TAG_TEXTLANG
:
6045 case IPP_TAG_URISCHEME
:
6046 for (i
= 0; i
< count
; i
++)
6048 const char *value
= ippGetString(attr
, i
, NULL
);
6049 /* Current string value */
6051 if (ippContainsString(fromattr
, value
))
6054 strlcpy(matchbuf
, value
, matchlen
);
6058 add_stringf(errors
, "GOT: %s='%s'", ippGetName(attr
), value
);
6071 /* value tag mismatch between fromattr and attr */
6074 add_stringf(errors
, "GOT: %s OF-TYPE %s", ippGetName(attr
), ippTagString(ippGetValueTag(attr
)));