2 * ipptool command for CUPS.
4 * Copyright © 2021 by OpenPrinting.
5 * Copyright © 2020 by The Printer Working Group.
6 * Copyright © 2007-2021 by Apple Inc.
7 * Copyright © 1997-2007 by Easy Software Products.
9 * Licensed under Apache License v2.0. See the file "LICENSE" for more
14 * Include necessary headers...
17 #include <cups/cups-private.h>
31 #endif /* !O_BINARY */
38 #define MAX_EXPECT 200 // Maximum number of EXPECT directives
39 #define MAX_DISPLAY 200 // Maximum number of DISPLAY directives
40 #define MAX_MONITOR 10 // Maximum number of MONITOR-PRINTER-STATE EXPECT directives
47 typedef enum ipptool_transfer_e
/**** How to send request data ****/
49 IPPTOOL_TRANSFER_AUTO
, /* Chunk for files, length for static */
50 IPPTOOL_TRANSFER_CHUNKED
, /* Chunk always */
51 IPPTOOL_TRANSFER_LENGTH
/* Length always */
54 typedef enum ipptool_output_e
/**** Output mode ****/
56 IPPTOOL_OUTPUT_QUIET
, /* No output */
57 IPPTOOL_OUTPUT_TEST
, /* Traditional CUPS test output */
58 IPPTOOL_OUTPUT_PLIST
, /* XML plist test output */
59 IPPTOOL_OUTPUT_IPPSERVER
, /* ippserver attribute file output */
60 IPPTOOL_OUTPUT_LIST
, /* Tabular list output */
61 IPPTOOL_OUTPUT_CSV
/* Comma-separated values output */
64 typedef enum ipptool_with_e
/**** WITH flags ****/
66 IPPTOOL_WITH_LITERAL
= 0, /* Match string is a literal value */
67 IPPTOOL_WITH_ALL
= 1, /* Must match all values */
68 IPPTOOL_WITH_REGEX
= 2, /* Match string is a regular expression */
69 IPPTOOL_WITH_HOSTNAME
= 4, /* Match string is a URI hostname */
70 IPPTOOL_WITH_RESOURCE
= 8, /* Match string is a URI resource */
71 IPPTOOL_WITH_SCHEME
= 16 /* Match string is a URI scheme */
74 typedef struct ipptool_expect_s
/**** Expected attribute info ****/
76 int optional
, /* Optional attribute? */
77 not_expect
, /* Don't expect attribute? */
78 expect_all
; /* Expect all attributes to match/not match */
79 char *name
, /* Attribute name */
80 *of_type
, /* Type name */
81 *same_count_as
, /* Parallel attribute name */
82 *if_defined
, /* Only required if variable defined */
83 *if_not_defined
, /* Only required if variable is not defined */
84 *with_value
, /* Attribute must include this value */
85 *with_value_from
, /* Attribute must have one of the values in this attribute */
86 *define_match
, /* Variable to define on match */
87 *define_no_match
, /* Variable to define on no-match */
88 *define_value
; /* Variable to define with value */
89 int repeat_limit
, /* Maximum number of times to repeat */
90 repeat_match
, /* Repeat test on match */
91 repeat_no_match
, /* Repeat test on no match */
92 with_distinct
, /* WITH-DISTINCT-VALUES? */
93 with_flags
, /* WITH flags */
94 count
; /* Expected count if > 0 */
95 ipp_tag_t in_group
; /* IN-GROUP value */
98 typedef struct ipptool_status_s
/**** Status info ****/
100 ipp_status_t status
; /* Expected status code */
101 char *if_defined
, /* Only if variable is defined */
102 *if_not_defined
, /* Only if variable is not defined */
103 *define_match
, /* Variable to define on match */
104 *define_no_match
, /* Variable to define on no-match */
105 *define_value
; /* Variable to define with value */
106 int repeat_limit
, /* Maximum number of times to repeat */
107 repeat_match
, /* Repeat the test when it does not match */
108 repeat_no_match
; /* Repeat the test when it matches */
111 typedef struct ipptool_test_s
/**** Test Data ****/
114 _ipp_vars_t
*vars
; /* Variables */
115 http_encryption_t encryption
; /* Encryption for connection */
116 int family
; /* Address family */
117 ipptool_output_t output
; /* Output mode */
118 int repeat_on_busy
; /* Repeat tests on server-error-busy */
119 int stop_after_include_error
;
120 /* Stop after include errors? */
121 double timeout
; /* Timeout for connection */
122 int validate_headers
, /* Validate HTTP headers in response? */
123 verbosity
; /* Show all attributes? */
126 int def_ignore_errors
; /* Default IGNORE-ERRORS value */
127 ipptool_transfer_t def_transfer
; /* Default TRANSFER value */
128 int def_version
; /* Default IPP version */
131 http_t
*http
; /* HTTP connection to printer/server */
132 cups_file_t
*outfile
; /* Output file */
133 int show_header
, /* Show the test header? */
134 xml_header
; /* 1 if XML plist header was written */
135 int pass
, /* Have we passed all tests? */
136 test_count
, /* Number of tests (total) */
137 pass_count
, /* Number of tests that passed */
138 fail_count
, /* Number of tests that failed */
139 skip_count
; /* Number of tests that were skipped */
142 cups_array_t
*errors
; /* Errors array */
143 int prev_pass
, /* Result of previous test */
144 skip_previous
; /* Skip on previous test failure? */
145 char compression
[16]; /* COMPRESSION value */
146 useconds_t delay
; /* Initial delay */
147 int num_displayed
; /* Number of displayed attributes */
148 char *displayed
[MAX_DISPLAY
];/* Displayed attributes */
149 int num_expects
; /* Number of expected attributes */
150 ipptool_expect_t expects
[MAX_EXPECT
], /* Expected attributes */
151 *expect
, /* Current expected attribute */
152 *last_expect
; /* Last EXPECT (for predicates) */
153 char file
[1024], /* Data filename */
154 file_id
[1024]; /* File identifier */
155 int ignore_errors
; /* Ignore test failures? */
156 char name
[1024]; /* Test name */
157 char pause
[1024]; /* PAUSE value */
158 useconds_t repeat_interval
; /* Repeat interval (delay) */
159 int request_id
; /* Current request ID */
160 char resource
[512]; /* Resource for request */
161 int pass_test
, /* Pass this test? */
162 skip_test
, /* Skip this test? */
163 num_statuses
; /* Number of valid status codes */
164 ipptool_status_t statuses
[100], /* Valid status codes */
165 *last_status
; /* Last STATUS (for predicates) */
166 char test_id
[1024]; /* Test identifier */
167 ipptool_transfer_t transfer
; /* To chunk or not to chunk */
168 int version
; /* IPP version number to use */
169 _cups_thread_t monitor_thread
; /* Monitoring thread ID */
170 int monitor_done
; /* Set to 1 to stop monitor thread */
171 char *monitor_uri
; /* MONITOR-PRINTER-STATE URI */
172 useconds_t monitor_delay
, /* MONITOR-PRINTER-STATE DELAY value, if any */
173 monitor_interval
; /* MONITOR-PRINTER-STATE DELAY interval */
174 int num_monitor_expects
; /* Number MONITOR-PRINTER-STATE EXPECTs */
175 ipptool_expect_t monitor_expects
[MAX_MONITOR
];
176 /* MONITOR-PRINTER-STATE EXPECTs */
184 static int Cancel
= 0; /* Cancel test? */
191 static void add_stringf(cups_array_t
*a
, const char *s
, ...) _CUPS_FORMAT(2, 3);
192 static int compare_uris(const char *a
, const char *b
);
193 static void copy_hex_string(char *buffer
, unsigned char *data
, int datalen
, size_t bufsize
);
194 static void *do_monitor_printer_state(ipptool_test_t
*data
);
195 static int do_test(_ipp_file_t
*f
, ipptool_test_t
*data
);
196 static int do_tests(const char *testfile
, ipptool_test_t
*data
);
197 static int error_cb(_ipp_file_t
*f
, ipptool_test_t
*data
, const char *error
);
198 static int expect_matches(ipptool_expect_t
*expect
, ipp_attribute_t
*attr
);
199 static char *get_filename(const char *testfile
, char *dst
, const char *src
, size_t dstsize
);
200 static const char *get_string(ipp_attribute_t
*attr
, int element
, int flags
, char *buffer
, size_t bufsize
);
201 static void init_data(ipptool_test_t
*data
);
202 static char *iso_date(const ipp_uchar_t
*date
);
203 static int parse_monitor_printer_state(_ipp_file_t
*f
, ipptool_test_t
*data
);
204 static void pause_message(const char *message
);
205 static void print_attr(cups_file_t
*outfile
, ipptool_output_t output
, ipp_attribute_t
*attr
, ipp_tag_t
*group
);
206 static ipp_attribute_t
*print_csv(ipptool_test_t
*data
, ipp_t
*ipp
, ipp_attribute_t
*attr
, int num_displayed
, char **displayed
, size_t *widths
);
207 static void print_fatal_error(ipptool_test_t
*data
, const char *s
, ...) _CUPS_FORMAT(2, 3);
208 static void print_ippserver_attr(ipptool_test_t
*data
, ipp_attribute_t
*attr
, int indent
);
209 static void print_ippserver_string(ipptool_test_t
*data
, const char *s
, size_t len
);
210 static ipp_attribute_t
*print_line(ipptool_test_t
*data
, ipp_t
*ipp
, ipp_attribute_t
*attr
, int num_displayed
, char **displayed
, size_t *widths
);
211 static void print_xml_header(ipptool_test_t
*data
);
212 static void print_xml_string(cups_file_t
*outfile
, const char *element
, const char *s
);
213 static void print_xml_trailer(ipptool_test_t
*data
, int success
, const char *message
);
215 static void sigterm_handler(int sig
);
217 static int timeout_cb(http_t
*http
, void *user_data
);
218 static int token_cb(_ipp_file_t
*f
, _ipp_vars_t
*vars
, ipptool_test_t
*data
, const char *token
);
219 static void usage(void) _CUPS_NORETURN
;
220 static int with_distinct_values(cups_array_t
*errors
, ipp_attribute_t
*attr
);
221 static const char *with_flags_string(int flags
);
222 static int with_value(ipptool_test_t
*data
, cups_array_t
*errors
, char *value
, int flags
, ipp_attribute_t
*attr
, char *matchbuf
, size_t matchlen
);
223 static int with_value_from(cups_array_t
*errors
, ipp_attribute_t
*fromattr
, ipp_attribute_t
*attr
, char *matchbuf
, size_t matchlen
);
227 * 'main()' - Parse options and do tests.
230 int /* O - Exit status */
231 main(int argc
, /* I - Number of command-line args */
232 char *argv
[]) /* I - Command-line arguments */
234 int i
; /* Looping var */
235 int status
; /* Status of tests... */
236 char *opt
, /* Current option */
237 name
[1024], /* Name/value buffer */
238 *value
, /* Pointer to value */
239 filename
[1024], /* Real filename */
240 testname
[1024]; /* Real test filename */
241 const char *ext
, /* Extension on filename */
242 *testfile
; /* Test file to use */
243 int interval
, /* Test interval in microseconds */
244 repeat
; /* Repeat count */
245 _ipp_vars_t vars
; /* Variables */
246 ipptool_test_t data
; /* Test data */
247 _cups_globals_t
*cg
= _cupsGlobals();
253 * Catch SIGINT and SIGTERM...
256 signal(SIGINT
, sigterm_handler
);
257 signal(SIGTERM
, sigterm_handler
);
261 * Initialize the locale and variables...
264 _cupsSetLocale(argv
);
268 _ippVarsInit(&vars
, NULL
, (_ipp_ferror_cb_t
)error_cb
, (_ipp_ftoken_cb_t
)token_cb
);
271 _ippVarsSet(data
.vars
, "date-start", iso_date(ippTimeToDate(time(NULL
))));
276 * ipptool URI testfile
284 for (i
= 1; i
< argc
; i
++)
286 if (!strcmp(argv
[i
], "--help"))
290 else if (!strcmp(argv
[i
], "--ippserver"))
296 _cupsLangPuts(stderr
, _("ipptool: Missing filename for \"--ippserver\"."));
300 if (data
.outfile
!= cupsFileStdout())
303 if ((data
.outfile
= cupsFileOpen(argv
[i
], "w")) == NULL
)
305 _cupsLangPrintf(stderr
, _("%s: Unable to open \"%s\": %s"), "ipptool", argv
[i
], strerror(errno
));
309 data
.output
= IPPTOOL_OUTPUT_IPPSERVER
;
311 else if (!strcmp(argv
[i
], "--stop-after-include-error"))
313 data
.stop_after_include_error
= 1;
315 else if (!strcmp(argv
[i
], "--version"))
320 else if (argv
[i
][0] == '-')
322 for (opt
= argv
[i
] + 1; *opt
; opt
++)
326 case '4' : /* Connect using IPv4 only */
327 data
.family
= AF_INET
;
331 case '6' : /* Connect using IPv6 only */
332 data
.family
= AF_INET6
;
334 #endif /* AF_INET6 */
336 case 'C' : /* Enable HTTP chunking */
337 data
.def_transfer
= IPPTOOL_TRANSFER_CHUNKED
;
340 case 'E' : /* Encrypt with TLS */
342 data
.encryption
= HTTP_ENCRYPT_REQUIRED
;
344 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
346 #endif /* HAVE_TLS */
349 case 'I' : /* Ignore errors */
350 data
.def_ignore_errors
= 1;
353 case 'L' : /* Disable HTTP chunking */
354 data
.def_transfer
= IPPTOOL_TRANSFER_LENGTH
;
357 case 'P' : /* Output to plist file */
362 _cupsLangPrintf(stderr
, _("%s: Missing filename for \"-P\"."), "ipptool");
366 if (data
.outfile
!= cupsFileStdout())
369 if ((data
.outfile
= cupsFileOpen(argv
[i
], "w")) == NULL
)
371 _cupsLangPrintf(stderr
, _("%s: Unable to open \"%s\": %s"), "ipptool", argv
[i
], strerror(errno
));
375 data
.output
= IPPTOOL_OUTPUT_PLIST
;
377 if (interval
|| repeat
)
379 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
384 case 'R' : /* Repeat on server-error-busy */
385 data
.repeat_on_busy
= 1;
388 case 'S' : /* Encrypt with SSL */
390 data
.encryption
= HTTP_ENCRYPT_ALWAYS
;
392 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."), "ipptool");
393 #endif /* HAVE_TLS */
396 case 'T' : /* Set timeout */
401 _cupsLangPrintf(stderr
, _("%s: Missing timeout for \"-T\"."), "ipptool");
405 data
.timeout
= _cupsStrScand(argv
[i
], NULL
, localeconv());
408 case 'V' : /* Set IPP version */
413 _cupsLangPrintf(stderr
, _("%s: Missing version for \"-V\"."), "ipptool");
417 if (!strcmp(argv
[i
], "1.0"))
419 data
.def_version
= 10;
421 else if (!strcmp(argv
[i
], "1.1"))
423 data
.def_version
= 11;
425 else if (!strcmp(argv
[i
], "2.0"))
427 data
.def_version
= 20;
429 else if (!strcmp(argv
[i
], "2.1"))
431 data
.def_version
= 21;
433 else if (!strcmp(argv
[i
], "2.2"))
435 data
.def_version
= 22;
439 _cupsLangPrintf(stderr
, _("%s: Bad version %s for \"-V\"."), "ipptool", argv
[i
]);
444 case 'X' : /* Produce XML output */
445 data
.output
= IPPTOOL_OUTPUT_PLIST
;
447 if (interval
|| repeat
)
449 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
454 case 'c' : /* CSV output */
455 data
.output
= IPPTOOL_OUTPUT_CSV
;
458 case 'd' : /* Define a variable */
463 _cupsLangPuts(stderr
, _("ipptool: Missing name=value for \"-d\"."));
467 strlcpy(name
, argv
[i
], sizeof(name
));
468 if ((value
= strchr(name
, '=')) != NULL
)
471 value
= name
+ strlen(name
);
473 _ippVarsSet(data
.vars
, name
, value
);
476 case 'f' : /* Set the default test filename */
481 _cupsLangPuts(stderr
, _("ipptool: Missing filename for \"-f\"."));
485 if (access(argv
[i
], 0))
491 snprintf(filename
, sizeof(filename
), "%s.gz", argv
[i
]);
492 if (access(filename
, 0) && filename
[0] != '/'
494 && (!isalpha(filename
[0] & 255) || filename
[1] != ':')
498 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s", cg
->cups_datadir
, argv
[i
]);
499 if (access(filename
, 0))
501 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s.gz", cg
->cups_datadir
, argv
[i
]);
502 if (access(filename
, 0))
503 strlcpy(filename
, argv
[i
], sizeof(filename
));
508 strlcpy(filename
, argv
[i
], sizeof(filename
));
510 _ippVarsSet(data
.vars
, "filename", filename
);
512 if ((ext
= strrchr(filename
, '.')) != NULL
)
515 * Guess the MIME media type based on the extension...
518 if (!_cups_strcasecmp(ext
, ".gif"))
519 _ippVarsSet(data
.vars
, "filetype", "image/gif");
520 else if (!_cups_strcasecmp(ext
, ".htm") ||
521 !_cups_strcasecmp(ext
, ".htm.gz") ||
522 !_cups_strcasecmp(ext
, ".html") ||
523 !_cups_strcasecmp(ext
, ".html.gz"))
524 _ippVarsSet(data
.vars
, "filetype", "text/html");
525 else if (!_cups_strcasecmp(ext
, ".jpg") ||
526 !_cups_strcasecmp(ext
, ".jpeg"))
527 _ippVarsSet(data
.vars
, "filetype", "image/jpeg");
528 else if (!_cups_strcasecmp(ext
, ".pcl") ||
529 !_cups_strcasecmp(ext
, ".pcl.gz"))
530 _ippVarsSet(data
.vars
, "filetype", "application/vnd.hp-PCL");
531 else if (!_cups_strcasecmp(ext
, ".pdf"))
532 _ippVarsSet(data
.vars
, "filetype", "application/pdf");
533 else if (!_cups_strcasecmp(ext
, ".png"))
534 _ippVarsSet(data
.vars
, "filetype", "image/png");
535 else if (!_cups_strcasecmp(ext
, ".ps") ||
536 !_cups_strcasecmp(ext
, ".ps.gz"))
537 _ippVarsSet(data
.vars
, "filetype", "application/postscript");
538 else if (!_cups_strcasecmp(ext
, ".pwg") ||
539 !_cups_strcasecmp(ext
, ".pwg.gz") ||
540 !_cups_strcasecmp(ext
, ".ras") ||
541 !_cups_strcasecmp(ext
, ".ras.gz"))
542 _ippVarsSet(data
.vars
, "filetype", "image/pwg-raster");
543 else if (!_cups_strcasecmp(ext
, ".tif") ||
544 !_cups_strcasecmp(ext
, ".tiff"))
545 _ippVarsSet(data
.vars
, "filetype", "image/tiff");
546 else if (!_cups_strcasecmp(ext
, ".txt") ||
547 !_cups_strcasecmp(ext
, ".txt.gz"))
548 _ippVarsSet(data
.vars
, "filetype", "text/plain");
549 else if (!_cups_strcasecmp(ext
, ".urf") ||
550 !_cups_strcasecmp(ext
, ".urf.gz"))
551 _ippVarsSet(data
.vars
, "filetype", "image/urf");
552 else if (!_cups_strcasecmp(ext
, ".xps"))
553 _ippVarsSet(data
.vars
, "filetype", "application/openxps");
555 _ippVarsSet(data
.vars
, "filetype", "application/octet-stream");
560 * Use the "auto-type" MIME media type...
563 _ippVarsSet(data
.vars
, "filetype", "application/octet-stream");
567 case 'h' : /* Validate response headers */
568 data
.validate_headers
= 1;
571 case 'i' : /* Test every N seconds */
576 _cupsLangPuts(stderr
, _("ipptool: Missing seconds for \"-i\"."));
581 interval
= (int)(_cupsStrScand(argv
[i
], NULL
, localeconv()) * 1000000.0);
584 _cupsLangPuts(stderr
, _("ipptool: Invalid seconds for \"-i\"."));
589 if ((data
.output
== IPPTOOL_OUTPUT_PLIST
|| data
.output
== IPPTOOL_OUTPUT_IPPSERVER
) && interval
)
591 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"--ippserver\", \"-P\", and \"-X\"."));
596 case 'l' : /* List as a table */
597 data
.output
= IPPTOOL_OUTPUT_LIST
;
600 case 'n' : /* Repeat count */
605 _cupsLangPuts(stderr
, _("ipptool: Missing count for \"-n\"."));
609 repeat
= atoi(argv
[i
]);
611 if ((data
.output
== IPPTOOL_OUTPUT_PLIST
|| data
.output
== IPPTOOL_OUTPUT_IPPSERVER
) && repeat
)
613 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"--ippserver\", \"-P\", and \"-X\"."));
618 case 'q' : /* Be quiet */
619 data
.output
= IPPTOOL_OUTPUT_QUIET
;
622 case 't' : /* CUPS test output */
623 data
.output
= IPPTOOL_OUTPUT_TEST
;
626 case 'v' : /* Be verbose */
631 _cupsLangPrintf(stderr
, _("%s: Unknown option \"-%c\"."), "ipptool", *opt
);
636 else if (!strncmp(argv
[i
], "ipp://", 6) || !strncmp(argv
[i
], "http://", 7)
638 || !strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8)
639 #endif /* HAVE_TLS */
648 _cupsLangPuts(stderr
, _("ipptool: May only specify a single URI."));
653 if (!strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8))
654 data
.encryption
= HTTP_ENCRYPT_ALWAYS
;
655 #endif /* HAVE_TLS */
657 if (!_ippVarsSet(data
.vars
, "uri", argv
[i
]))
659 _cupsLangPrintf(stderr
, _("ipptool: Bad URI \"%s\"."), argv
[i
]);
663 if (data
.vars
->username
[0] && data
.vars
->password
)
664 cupsSetPasswordCB2(_ippVarsPasswordCB
, data
.vars
);
674 _cupsLangPuts(stderr
, _("ipptool: URI required before test file."));
675 _cupsLangPuts(stderr
, argv
[i
]);
679 if (access(argv
[i
], 0) && argv
[i
][0] != '/'
681 && (!isalpha(argv
[i
][0] & 255) || argv
[i
][1] != ':')
685 snprintf(testname
, sizeof(testname
), "%s/ipptool/%s", cg
->cups_datadir
, argv
[i
]);
686 if (access(testname
, 0))
694 if (access(testfile
, 0))
696 _cupsLangPrintf(stderr
, _("%s: Unable to open \"%s\": %s"), "ipptool", testfile
, strerror(errno
));
699 else if (!do_tests(testfile
, &data
))
704 if (!data
.vars
->uri
|| !testfile
)
708 * Loop if the interval is set...
711 if (data
.output
== IPPTOOL_OUTPUT_PLIST
)
712 print_xml_trailer(&data
, !status
, NULL
);
713 else if (interval
> 0 && repeat
> 0)
717 usleep((useconds_t
)interval
);
718 do_tests(testfile
, &data
);
722 else if (interval
> 0)
726 usleep((useconds_t
)interval
);
727 do_tests(testfile
, &data
);
731 if ((data
.output
== IPPTOOL_OUTPUT_TEST
|| (data
.output
== IPPTOOL_OUTPUT_PLIST
&& data
.outfile
)) && data
.test_count
> 1)
734 * Show a summary report if there were multiple tests...
737 cupsFilePrintf(cupsFileStdout(), "\nSummary: %d tests, %d passed, %d failed, %d skipped\nScore: %d%%\n", data
.test_count
, data
.pass_count
, data
.fail_count
, data
.skip_count
, 100 * (data
.pass_count
+ data
.skip_count
) / data
.test_count
);
740 cupsFileClose(data
.outfile
);
751 * 'add_stringf()' - Add a formatted string to an array.
755 add_stringf(cups_array_t
*a
, /* I - Array */
756 const char *s
, /* I - Printf-style format string */
757 ...) /* I - Additional args as needed */
759 char buffer
[10240]; /* Format buffer */
760 va_list ap
; /* Argument pointer */
764 * Don't bother is the array is NULL...
771 * Format the message...
775 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
779 * Add it to the array...
782 cupsArrayAdd(a
, buffer
);
787 * 'compare_uris()' - Compare two URIs...
790 static int /* O - Result of comparison */
791 compare_uris(const char *a
, /* I - First URI */
792 const char *b
) /* I - Second URI */
794 char ascheme
[32], /* Components of first URI */
799 char bscheme
[32], /* Components of second URI */
804 char *ptr
; /* Pointer into string */
805 int result
; /* Result of comparison */
809 * Separate the URIs into their components...
812 if (httpSeparateURI(HTTP_URI_CODING_ALL
, a
, ascheme
, sizeof(ascheme
), auserpass
, sizeof(auserpass
), ahost
, sizeof(ahost
), &aport
, aresource
, sizeof(aresource
)) < HTTP_URI_STATUS_OK
)
815 if (httpSeparateURI(HTTP_URI_CODING_ALL
, b
, bscheme
, sizeof(bscheme
), buserpass
, sizeof(buserpass
), bhost
, sizeof(bhost
), &bport
, bresource
, sizeof(bresource
)) < HTTP_URI_STATUS_OK
)
819 * Strip trailing dots from the host components, if present...
822 if ((ptr
= ahost
+ strlen(ahost
) - 1) > ahost
&& *ptr
== '.')
825 if ((ptr
= bhost
+ strlen(bhost
) - 1) > bhost
&& *ptr
== '.')
829 * Compare each component...
832 if ((result
= _cups_strcasecmp(ascheme
, bscheme
)) != 0)
835 if ((result
= strcmp(auserpass
, buserpass
)) != 0)
838 if ((result
= _cups_strcasecmp(ahost
, bhost
)) != 0)
842 return (aport
- bport
);
844 if (!_cups_strcasecmp(ascheme
, "mailto") || !_cups_strcasecmp(ascheme
, "urn"))
845 return (_cups_strcasecmp(aresource
, bresource
));
847 return (strcmp(aresource
, bresource
));
852 * 'copy_hex_string()' - Copy an octetString to a C string and encode as hex if
857 copy_hex_string(char *buffer
, /* I - String buffer */
858 unsigned char *data
, /* I - octetString data */
859 int datalen
, /* I - octetString length */
860 size_t bufsize
) /* I - Size of string buffer */
862 char *bufptr
, /* Pointer into string buffer */
863 *bufend
= buffer
+ bufsize
- 2;
864 /* End of string buffer */
865 unsigned char *dataptr
, /* Pointer into octetString data */
866 *dataend
= data
+ datalen
;
867 /* End of octetString data */
868 static const char *hexdigits
= "0123456789ABCDEF";
873 * First see if there are any non-ASCII bytes in the octetString...
876 for (dataptr
= data
; dataptr
< dataend
; dataptr
++)
877 if (*dataptr
< 0x20 || *dataptr
>= 0x7f)
880 if (dataptr
< dataend
)
883 * Yes, encode as hex...
888 for (bufptr
= buffer
+ 1, dataptr
= data
; bufptr
< bufend
&& dataptr
< dataend
; dataptr
++)
890 *bufptr
++ = hexdigits
[*dataptr
>> 4];
891 *bufptr
++ = hexdigits
[*dataptr
& 15];
902 * No, copy as a string...
905 if ((size_t)datalen
> bufsize
)
906 datalen
= (int)bufsize
- 1;
908 memcpy(buffer
, data
, (size_t)datalen
);
909 buffer
[datalen
] = '\0';
915 * 'do_monitor_printer_state()' - Do the MONITOR-PRINTER-STATE tests in the background.
918 static void * // O - Thread exit status
919 do_monitor_printer_state(
920 ipptool_test_t
*data
) // I - Test data
922 int i
, j
; // Looping vars
923 char scheme
[32], // URI scheme
924 userpass
[32], // URI username:password
925 host
[256], // URI hostname/IP address
926 resource
[256]; // URI resource path
927 int port
; // URI port number
928 http_encryption_t encryption
; // Encryption to use
929 http_t
*http
; // Connection to printer
930 ipp_t
*request
, // IPP request
931 *response
= NULL
; // IPP response
932 http_status_t status
; // Request status
933 ipp_attribute_t
*found
; // Found attribute
934 ipptool_expect_t
*expect
; // Current EXPECT test
935 char buffer
[131072]; // Copy buffer
936 int num_pattrs
; // Number of printer attributes
937 const char *pattrs
[100]; // Printer attributes we care about
940 // Connect to the printer...
941 if (httpSeparateURI(HTTP_URI_CODING_ALL
, data
->monitor_uri
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), host
, sizeof(host
), &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
943 print_fatal_error(data
, "Bad printer URI \"%s\".", data
->monitor_uri
);
947 if (!_cups_strcasecmp(scheme
, "https") || !_cups_strcasecmp(scheme
, "ipps") || port
== 443)
948 encryption
= HTTP_ENCRYPTION_ALWAYS
;
950 encryption
= data
->encryption
;
952 if ((http
= httpConnect2(host
, port
, NULL
, data
->family
, encryption
, 1, 30000, NULL
)) == NULL
)
954 print_fatal_error(data
, "Unable to connect to \"%s\" on port %d - %s", host
, port
, cupsLastErrorString());
959 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
, "deflate, gzip, identity");
961 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
, "identity");
962 #endif /* HAVE_LIBZ */
964 if (data
->timeout
> 0.0)
965 httpSetTimeout(http
, data
->timeout
, timeout_cb
, NULL
);
967 // Wait for the initial delay as needed...
968 if (data
->monitor_delay
)
969 usleep(data
->monitor_delay
);
971 // Create a query request that we'll reuse...
972 request
= ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES
);
973 ippSetRequestId(request
, data
->request_id
* 100 - 1);
974 ippSetVersion(request
, data
->version
/ 10, data
->version
% 10);
975 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
, data
->monitor_uri
);
976 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name", NULL
, cupsUser());
978 for (i
= data
->num_monitor_expects
, expect
= data
->monitor_expects
, num_pattrs
= 0; i
> 0; i
--, expect
++)
980 // Add EXPECT attribute names...
981 for (j
= 0; j
< num_pattrs
; j
++)
983 if (!strcmp(expect
->name
, pattrs
[j
]))
987 if (j
>= num_pattrs
&& num_pattrs
< (int)(sizeof(pattrs
) / sizeof(pattrs
[0])))
988 pattrs
[num_pattrs
++] = expect
->name
;
992 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "requested-attributes", num_pattrs
, NULL
, pattrs
);
994 // Loop until we need to stop...
995 while (!data
->monitor_done
&& !Cancel
)
997 // Poll the printer state...
998 ippSetRequestId(request
, ippGetRequestId(request
) + 1);
1000 if ((status
= cupsSendRequest(http
, request
, resource
, ippLength(request
))) != HTTP_STATUS_ERROR
)
1002 response
= cupsGetResponse(http
, resource
);
1003 status
= httpGetStatus(http
);
1006 if (!data
->monitor_done
&& !Cancel
&& status
== HTTP_STATUS_ERROR
&& httpError(data
->http
) != EINVAL
&&
1008 httpError(data
->http
) != WSAETIMEDOUT
)
1010 httpError(data
->http
) != ETIMEDOUT
)
1013 if (httpReconnect2(http
, 30000, NULL
))
1016 else if (status
== HTTP_STATUS_ERROR
|| status
== HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED
)
1020 else if (status
!= HTTP_STATUS_OK
)
1024 if (status
== HTTP_STATUS_UNAUTHORIZED
)
1030 for (i
= data
->num_monitor_expects
, expect
= data
->monitor_expects
; i
> 0; i
--, expect
++)
1032 if (expect
->if_defined
&& !_ippVarsGet(data
->vars
, expect
->if_defined
))
1035 if (expect
->if_not_defined
&& _ippVarsGet(data
->vars
, expect
->if_not_defined
))
1038 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
1040 if ((found
&& expect
->not_expect
) ||
1041 (!found
&& !(expect
->not_expect
|| expect
->optional
)) ||
1042 (found
&& !expect_matches(expect
, found
)) ||
1043 (expect
->in_group
&& ippGetGroupTag(found
) != expect
->in_group
) ||
1044 (expect
->with_distinct
&& !with_distinct_values(NULL
, found
)))
1046 if (expect
->define_no_match
)
1048 _ippVarsSet(data
->vars
, expect
->define_no_match
, "1");
1049 data
->monitor_done
= 1;
1055 ippAttributeString(found
, buffer
, sizeof(buffer
));
1057 if (found
&& !with_value(data
, NULL
, expect
->with_value
, expect
->with_flags
, found
, buffer
, sizeof(buffer
)))
1059 if (expect
->define_no_match
)
1061 _ippVarsSet(data
->vars
, expect
->define_no_match
, "1");
1062 data
->monitor_done
= 1;
1067 if (found
&& expect
->count
> 0 && ippGetCount(found
) != expect
->count
)
1069 if (expect
->define_no_match
)
1071 _ippVarsSet(data
->vars
, expect
->define_no_match
, "1");
1072 data
->monitor_done
= 1;
1077 if (found
&& expect
->define_match
)
1079 _ippVarsSet(data
->vars
, expect
->define_match
, "1");
1080 data
->monitor_done
= 1;
1083 if (found
&& expect
->define_value
)
1085 if (!expect
->with_value
)
1087 int last
= ippGetCount(found
) - 1;
1088 // Last element in attribute
1090 switch (ippGetValueTag(found
))
1093 case IPP_TAG_INTEGER
:
1094 snprintf(buffer
, sizeof(buffer
), "%d", ippGetInteger(found
, last
));
1097 case IPP_TAG_BOOLEAN
:
1098 if (ippGetBoolean(found
, last
))
1099 strlcpy(buffer
, "true", sizeof(buffer
));
1101 strlcpy(buffer
, "false", sizeof(buffer
));
1104 case IPP_TAG_CHARSET
:
1105 case IPP_TAG_KEYWORD
:
1106 case IPP_TAG_LANGUAGE
:
1107 case IPP_TAG_MIMETYPE
:
1109 case IPP_TAG_NAMELANG
:
1111 case IPP_TAG_TEXTLANG
:
1113 case IPP_TAG_URISCHEME
:
1114 strlcpy(buffer
, ippGetString(found
, last
, NULL
), sizeof(buffer
));
1118 ippAttributeString(found
, buffer
, sizeof(buffer
));
1123 _ippVarsSet(data
->vars
, expect
->define_value
, buffer
);
1124 data
->monitor_done
= 1;
1129 data
->monitor_done
= 1; // All tests passed
1131 ippDelete(response
);
1134 // Sleep between requests...
1135 if (data
->monitor_done
|| Cancel
)
1138 usleep(data
->monitor_interval
);
1141 // Close the connection to the printer and return...
1144 ippDelete(response
);
1151 * 'do_test()' - Do a single test from the test file.
1154 static int /* O - 1 on success, 0 on failure */
1155 do_test(_ipp_file_t
*f
, /* I - IPP data file */
1156 ipptool_test_t
*data
) /* I - Test data */
1159 int i
, /* Looping var */
1160 status_ok
, /* Did we get a matching status? */
1161 repeat_count
= 0, /* Repeat count */
1162 repeat_test
; /* Repeat the test? */
1163 ipptool_expect_t
*expect
; /* Current expected attribute */
1164 ipp_t
*request
, /* IPP request */
1165 *response
; /* IPP response */
1166 size_t length
; /* Length of IPP request */
1167 http_status_t status
; /* HTTP status */
1168 cups_array_t
*a
; /* Duplicate attribute array */
1169 ipp_tag_t group
; /* Current group */
1170 ipp_attribute_t
*attrptr
, /* Attribute pointer */
1171 *found
; /* Found attribute */
1172 char temp
[1024]; /* Temporary string */
1173 cups_file_t
*reqfile
; /* File to send */
1174 ssize_t bytes
; /* Bytes read/written */
1175 char buffer
[131072]; /* Copy buffer */
1176 size_t widths
[200]; /* Width of columns */
1177 const char *error
; /* Current error */
1184 * Show any PAUSE message, as needed...
1189 if (!data
->skip_test
&& !data
->pass_test
)
1190 pause_message(data
->pause
);
1192 data
->pause
[0] = '\0';
1196 * Start the background thread as needed...
1199 if (data
->monitor_uri
)
1200 data
->monitor_thread
= _cupsThreadCreate((_cups_thread_func_t
)do_monitor_printer_state
, data
);
1203 * Take over control of the attributes in the request...
1210 * Submit the IPP request...
1213 data
->test_count
++;
1215 ippSetVersion(request
, data
->version
/ 10, data
->version
% 10);
1216 ippSetRequestId(request
, data
->request_id
);
1218 if (data
->output
== IPPTOOL_OUTPUT_PLIST
)
1220 cupsFilePuts(data
->outfile
, "<dict>\n");
1221 cupsFilePuts(data
->outfile
, "<key>Name</key>\n");
1222 print_xml_string(data
->outfile
, "string", data
->name
);
1223 if (data
->file_id
[0])
1225 cupsFilePuts(data
->outfile
, "<key>FileId</key>\n");
1226 print_xml_string(data
->outfile
, "string", data
->file_id
);
1228 if (data
->test_id
[0])
1230 cupsFilePuts(data
->outfile
, "<key>TestId</key>\n");
1231 print_xml_string(data
->outfile
, "string", data
->test_id
);
1233 cupsFilePuts(data
->outfile
, "<key>Version</key>\n");
1234 cupsFilePrintf(data
->outfile
, "<string>%d.%d</string>\n", data
->version
/ 10, data
->version
% 10);
1235 cupsFilePuts(data
->outfile
, "<key>Operation</key>\n");
1236 print_xml_string(data
->outfile
, "string", ippOpString(ippGetOperation(request
)));
1237 cupsFilePuts(data
->outfile
, "<key>RequestId</key>\n");
1238 cupsFilePrintf(data
->outfile
, "<integer>%d</integer>\n", data
->request_id
);
1239 cupsFilePuts(data
->outfile
, "<key>RequestAttributes</key>\n");
1240 cupsFilePuts(data
->outfile
, "<array>\n");
1241 if (ippFirstAttribute(request
))
1243 cupsFilePuts(data
->outfile
, "<dict>\n");
1244 for (attrptr
= ippFirstAttribute(request
), group
= ippGetGroupTag(attrptr
); attrptr
; attrptr
= ippNextAttribute(request
))
1245 print_attr(data
->outfile
, data
->output
, attrptr
, &group
);
1246 cupsFilePuts(data
->outfile
, "</dict>\n");
1248 cupsFilePuts(data
->outfile
, "</array>\n");
1251 if (data
->output
== IPPTOOL_OUTPUT_TEST
|| (data
->output
== IPPTOOL_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
1253 if (data
->verbosity
)
1255 cupsFilePrintf(cupsFileStdout(), " %s:\n", ippOpString(ippGetOperation(request
)));
1257 for (attrptr
= ippFirstAttribute(request
); attrptr
; attrptr
= ippNextAttribute(request
))
1258 print_attr(cupsFileStdout(), IPPTOOL_OUTPUT_TEST
, attrptr
, NULL
);
1261 cupsFilePrintf(cupsFileStdout(), " %-68.68s [", data
->name
);
1264 if ((data
->skip_previous
&& !data
->prev_pass
) || data
->skip_test
|| data
->pass_test
)
1266 if (!data
->pass_test
)
1267 data
->skip_count
++;
1273 if (data
->output
== IPPTOOL_OUTPUT_PLIST
)
1275 cupsFilePuts(data
->outfile
, "<key>Successful</key>\n");
1276 cupsFilePuts(data
->outfile
, "<true />\n");
1277 cupsFilePuts(data
->outfile
, "<key>Skipped</key>\n");
1278 if (data
->pass_test
)
1279 cupsFilePuts(data
->outfile
, "<false />\n");
1281 cupsFilePuts(data
->outfile
, "<true />\n");
1282 cupsFilePuts(data
->outfile
, "<key>StatusCode</key>\n");
1283 if (data
->pass_test
)
1284 print_xml_string(data
->outfile
, "string", "pass");
1286 print_xml_string(data
->outfile
, "string", "skip");
1287 cupsFilePuts(data
->outfile
, "<key>ResponseAttributes</key>\n");
1288 cupsFilePuts(data
->outfile
, "<dict />\n");
1291 if (data
->output
== IPPTOOL_OUTPUT_TEST
|| (data
->output
== IPPTOOL_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
1293 if (data
->pass_test
)
1294 cupsFilePuts(cupsFileStdout(), "PASS]\n");
1296 cupsFilePuts(cupsFileStdout(), "SKIP]\n");
1302 data
->vars
->password_tries
= 0;
1306 if (data
->delay
> 0)
1307 usleep(data
->delay
);
1309 data
->delay
= data
->repeat_interval
;
1312 status
= HTTP_STATUS_OK
;
1314 if (data
->transfer
== IPPTOOL_TRANSFER_CHUNKED
|| (data
->transfer
== IPPTOOL_TRANSFER_AUTO
&& data
->file
[0]))
1317 * Send request using chunking - a 0 length means "chunk".
1325 * Send request using content length...
1328 length
= ippLength(request
);
1330 if (data
->file
[0] && (reqfile
= cupsFileOpen(data
->file
, "r")) != NULL
)
1333 * Read the file to get the uncompressed file size...
1336 while ((bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
1337 length
+= (size_t)bytes
;
1339 cupsFileClose(reqfile
);
1344 * Send the request...
1347 data
->prev_pass
= 1;
1351 if (status
!= HTTP_STATUS_ERROR
)
1353 while (!response
&& !Cancel
&& data
->prev_pass
)
1355 ippSetRequestId(request
, ++ data
->request_id
);
1357 status
= cupsSendRequest(data
->http
, request
, data
->resource
, length
);
1360 if (data
->compression
[0])
1361 httpSetField(data
->http
, HTTP_FIELD_CONTENT_ENCODING
, data
->compression
);
1362 #endif /* HAVE_LIBZ */
1364 if (!Cancel
&& status
== HTTP_STATUS_CONTINUE
&& ippGetState(request
) == IPP_DATA
&& data
->file
[0])
1366 if ((reqfile
= cupsFileOpen(data
->file
, "r")) != NULL
)
1368 while (!Cancel
&& (bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
1370 if ((status
= cupsWriteRequestData(data
->http
, buffer
, (size_t)bytes
)) != HTTP_STATUS_CONTINUE
)
1374 cupsFileClose(reqfile
);
1378 snprintf(buffer
, sizeof(buffer
), "%s: %s", data
->file
, strerror(errno
));
1379 _cupsSetError(IPP_INTERNAL_ERROR
, buffer
, 0);
1381 status
= HTTP_STATUS_ERROR
;
1386 * Get the server's response...
1389 if (!Cancel
&& status
!= HTTP_STATUS_ERROR
)
1391 response
= cupsGetResponse(data
->http
, data
->resource
);
1392 status
= httpGetStatus(data
->http
);
1395 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& httpError(data
->http
) != EINVAL
&&
1397 httpError(data
->http
) != WSAETIMEDOUT
)
1399 httpError(data
->http
) != ETIMEDOUT
)
1402 if (httpReconnect2(data
->http
, 30000, NULL
))
1403 data
->prev_pass
= 0;
1405 else if (status
== HTTP_STATUS_ERROR
|| status
== HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED
)
1407 data
->prev_pass
= 0;
1410 else if (status
!= HTTP_STATUS_OK
)
1412 httpFlush(data
->http
);
1414 if (status
== HTTP_STATUS_UNAUTHORIZED
)
1422 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& httpError(data
->http
) != EINVAL
&&
1424 httpError(data
->http
) != WSAETIMEDOUT
)
1426 httpError(data
->http
) != ETIMEDOUT
)
1429 if (httpReconnect2(data
->http
, 30000, NULL
))
1430 data
->prev_pass
= 0;
1432 else if (status
== HTTP_STATUS_ERROR
)
1435 httpReconnect2(data
->http
, 30000, NULL
);
1437 data
->prev_pass
= 0;
1439 else if (status
!= HTTP_STATUS_OK
)
1441 httpFlush(data
->http
);
1442 data
->prev_pass
= 0;
1446 * Check results of request...
1449 cupsArrayClear(data
->errors
);
1451 if (httpGetVersion(data
->http
) != HTTP_1_1
)
1453 int version
= (int)httpGetVersion(data
->http
);
1455 add_stringf(data
->errors
, "Bad HTTP version (%d.%d)", version
/ 100, version
% 100);
1458 if (data
->validate_headers
)
1460 const char *header
; /* HTTP header value */
1462 if ((header
= httpGetField(data
->http
, HTTP_FIELD_CONTENT_TYPE
)) == NULL
|| _cups_strcasecmp(header
, "application/ipp"))
1463 add_stringf(data
->errors
, "Bad HTTP Content-Type in response (%s)", header
&& *header
? header
: "<missing>");
1465 if ((header
= httpGetField(data
->http
, HTTP_FIELD_DATE
)) != NULL
&& *header
&& httpGetDateTime(header
) == 0)
1466 add_stringf(data
->errors
, "Bad HTTP Date in response (%s)", header
);
1472 * No response, log error...
1475 add_stringf(data
->errors
, "IPP request failed with status %s (%s)", ippErrorString(cupsLastError()), cupsLastErrorString());
1480 * Collect common attribute values...
1483 if ((attrptr
= ippFindAttribute(response
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
1485 snprintf(temp
, sizeof(temp
), "%d", ippGetInteger(attrptr
, 0));
1486 _ippVarsSet(data
->vars
, "job-id", temp
);
1489 if ((attrptr
= ippFindAttribute(response
, "job-uri", IPP_TAG_URI
)) != NULL
)
1490 _ippVarsSet(data
->vars
, "job-uri", ippGetString(attrptr
, 0, NULL
));
1492 if ((attrptr
= ippFindAttribute(response
, "notify-subscription-id", IPP_TAG_INTEGER
)) != NULL
)
1494 snprintf(temp
, sizeof(temp
), "%d", ippGetInteger(attrptr
, 0));
1495 _ippVarsSet(data
->vars
, "notify-subscription-id", temp
);
1499 * Check response, validating groups and attributes and logging errors
1503 if (ippGetState(response
) != IPP_DATA
)
1504 add_stringf(data
->errors
, "Missing end-of-attributes-tag in response (RFC 2910 section 3.5.1)");
1508 int major
, minor
; /* IPP version */
1510 major
= ippGetVersion(response
, &minor
);
1512 if (major
!= (data
->version
/ 10) || minor
!= (data
->version
% 10))
1513 add_stringf(data
->errors
, "Bad version %d.%d in response - expected %d.%d (RFC 8011 section 4.1.8).", major
, minor
, data
->version
/ 10, data
->version
% 10);
1516 if (ippGetRequestId(response
) != data
->request_id
)
1517 add_stringf(data
->errors
, "Bad request ID %d in response - expected %d (RFC 8011 section 4.1.1)", ippGetRequestId(response
), data
->request_id
);
1519 attrptr
= ippFirstAttribute(response
);
1522 add_stringf(data
->errors
, "Missing first attribute \"attributes-charset (charset)\" in group operation-attributes-tag (RFC 8011 section 4.1.4).");
1526 if (!ippGetName(attrptr
) || ippGetValueTag(attrptr
) != IPP_TAG_CHARSET
|| ippGetGroupTag(attrptr
) != IPP_TAG_OPERATION
|| ippGetCount(attrptr
) != 1 ||strcmp(ippGetName(attrptr
), "attributes-charset"))
1527 add_stringf(data
->errors
, "Bad first attribute \"%s (%s%s)\" in group %s, expected \"attributes-charset (charset)\" in group operation-attributes-tag (RFC 8011 section 4.1.4).", ippGetName(attrptr
) ? ippGetName(attrptr
) : "(null)", ippGetCount(attrptr
) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attrptr
)), ippTagString(ippGetGroupTag(attrptr
)));
1529 attrptr
= ippNextAttribute(response
);
1531 add_stringf(data
->errors
, "Missing second attribute \"attributes-natural-language (naturalLanguage)\" in group operation-attributes-tag (RFC 8011 section 4.1.4).");
1532 else if (!ippGetName(attrptr
) || ippGetValueTag(attrptr
) != IPP_TAG_LANGUAGE
|| ippGetGroupTag(attrptr
) != IPP_TAG_OPERATION
|| ippGetCount(attrptr
) != 1 || strcmp(ippGetName(attrptr
), "attributes-natural-language"))
1533 add_stringf(data
->errors
, "Bad first attribute \"%s (%s%s)\" in group %s, expected \"attributes-natural-language (naturalLanguage)\" in group operation-attributes-tag (RFC 8011 section 4.1.4).", ippGetName(attrptr
) ? ippGetName(attrptr
) : "(null)", ippGetCount(attrptr
) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attrptr
)), ippTagString(ippGetGroupTag(attrptr
)));
1536 if ((attrptr
= ippFindAttribute(response
, "status-message", IPP_TAG_ZERO
)) != NULL
)
1538 const char *status_message
= ippGetString(attrptr
, 0, NULL
);
1541 if (ippGetValueTag(attrptr
) != IPP_TAG_TEXT
)
1542 add_stringf(data
->errors
, "status-message (text(255)) has wrong value tag %s (RFC 8011 section 4.1.6.2).", ippTagString(ippGetValueTag(attrptr
)));
1543 if (ippGetGroupTag(attrptr
) != IPP_TAG_OPERATION
)
1544 add_stringf(data
->errors
, "status-message (text(255)) has wrong group tag %s (RFC 8011 section 4.1.6.2).", ippTagString(ippGetGroupTag(attrptr
)));
1545 if (ippGetCount(attrptr
) != 1)
1546 add_stringf(data
->errors
, "status-message (text(255)) has %d values (RFC 8011 section 4.1.6.2).", ippGetCount(attrptr
));
1547 if (status_message
&& strlen(status_message
) > 255)
1548 add_stringf(data
->errors
, "status-message (text(255)) has bad length %d (RFC 8011 section 4.1.6.2).", (int)strlen(status_message
));
1551 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
1552 IPP_TAG_ZERO
)) != NULL
)
1554 const char *detailed_status_message
= ippGetString(attrptr
, 0, NULL
);
1557 if (ippGetValueTag(attrptr
) != IPP_TAG_TEXT
)
1558 add_stringf(data
->errors
, "detailed-status-message (text(MAX)) has wrong value tag %s (RFC 8011 section 4.1.6.3).", ippTagString(ippGetValueTag(attrptr
)));
1559 if (ippGetGroupTag(attrptr
) != IPP_TAG_OPERATION
)
1560 add_stringf(data
->errors
, "detailed-status-message (text(MAX)) has wrong group tag %s (RFC 8011 section 4.1.6.3).", ippTagString(ippGetGroupTag(attrptr
)));
1561 if (ippGetCount(attrptr
) != 1)
1562 add_stringf(data
->errors
, "detailed-status-message (text(MAX)) has %d values (RFC 8011 section 4.1.6.3).", ippGetCount(attrptr
));
1563 if (detailed_status_message
&& strlen(detailed_status_message
) > 1023)
1564 add_stringf(data
->errors
, "detailed-status-message (text(MAX)) has bad length %d (RFC 8011 section 4.1.6.3).", (int)strlen(detailed_status_message
));
1567 a
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
1569 for (attrptr
= ippFirstAttribute(response
), group
= ippGetGroupTag(attrptr
);
1571 attrptr
= ippNextAttribute(response
))
1573 if (ippGetGroupTag(attrptr
) != group
)
1575 int out_of_order
= 0; /* Are attribute groups out-of-order? */
1578 switch (ippGetGroupTag(attrptr
))
1583 case IPP_TAG_OPERATION
:
1587 case IPP_TAG_UNSUPPORTED_GROUP
:
1588 if (group
!= IPP_TAG_OPERATION
)
1593 case IPP_TAG_PRINTER
:
1594 if (group
!= IPP_TAG_OPERATION
&& group
!= IPP_TAG_UNSUPPORTED_GROUP
)
1598 case IPP_TAG_SUBSCRIPTION
:
1599 if (group
> ippGetGroupTag(attrptr
) && group
!= IPP_TAG_DOCUMENT
)
1604 if (group
> ippGetGroupTag(attrptr
))
1610 add_stringf(data
->errors
, "Attribute groups out of order (%s < %s)", ippTagString(ippGetGroupTag(attrptr
)), ippTagString(group
));
1612 if (ippGetGroupTag(attrptr
) != IPP_TAG_ZERO
)
1613 group
= ippGetGroupTag(attrptr
);
1616 if (!ippValidateAttribute(attrptr
))
1617 cupsArrayAdd(data
->errors
, (void *)cupsLastErrorString());
1619 if (ippGetName(attrptr
))
1621 if (cupsArrayFind(a
, (void *)ippGetName(attrptr
)) && data
->output
< IPPTOOL_OUTPUT_LIST
)
1622 add_stringf(data
->errors
, "Duplicate \"%s\" attribute in %s group", ippGetName(attrptr
), ippTagString(group
));
1624 cupsArrayAdd(a
, (void *)ippGetName(attrptr
));
1631 * Now check the test-defined expected status-code and attribute
1635 if (ippGetStatusCode(response
) == IPP_STATUS_ERROR_BUSY
&& data
->repeat_on_busy
)
1637 // Repeat on a server-error-busy status code...
1642 for (i
= 0, status_ok
= 0; i
< data
->num_statuses
; i
++)
1644 if (data
->statuses
[i
].if_defined
&&
1645 !_ippVarsGet(data
->vars
, data
->statuses
[i
].if_defined
))
1648 if (data
->statuses
[i
].if_not_defined
&&
1649 _ippVarsGet(data
->vars
, data
->statuses
[i
].if_not_defined
))
1652 if (ippGetStatusCode(response
) == data
->statuses
[i
].status
)
1656 if (data
->statuses
[i
].repeat_match
&& repeat_count
< data
->statuses
[i
].repeat_limit
)
1659 if (data
->statuses
[i
].define_match
)
1660 _ippVarsSet(data
->vars
, data
->statuses
[i
].define_match
, "1");
1664 if (data
->statuses
[i
].repeat_no_match
&& repeat_count
< data
->statuses
[i
].repeat_limit
)
1667 if (data
->statuses
[i
].define_no_match
)
1669 _ippVarsSet(data
->vars
, data
->statuses
[i
].define_no_match
, "1");
1675 if (!status_ok
&& data
->num_statuses
> 0)
1677 for (i
= 0; i
< data
->num_statuses
; i
++)
1679 if (data
->statuses
[i
].if_defined
&&
1680 !_ippVarsGet(data
->vars
, data
->statuses
[i
].if_defined
))
1683 if (data
->statuses
[i
].if_not_defined
&&
1684 _ippVarsGet(data
->vars
, data
->statuses
[i
].if_not_defined
))
1687 if (!data
->statuses
[i
].repeat_match
|| repeat_count
>= data
->statuses
[i
].repeat_limit
)
1688 add_stringf(data
->errors
, "EXPECTED: STATUS %s (got %s)", ippErrorString(data
->statuses
[i
].status
), ippErrorString(cupsLastError()));
1691 if ((attrptr
= ippFindAttribute(response
, "status-message", IPP_TAG_TEXT
)) != NULL
)
1692 add_stringf(data
->errors
, "status-message=\"%s\"", ippGetString(attrptr
, 0, NULL
));
1695 for (i
= data
->num_expects
, expect
= data
->expects
; i
> 0; i
--, expect
++)
1697 ipp_attribute_t
*group_found
; /* Found parent attribute for group tests */
1699 if (expect
->if_defined
&& !_ippVarsGet(data
->vars
, expect
->if_defined
))
1702 if (expect
->if_not_defined
&&
1703 _ippVarsGet(data
->vars
, expect
->if_not_defined
))
1706 if ((found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
)) != NULL
&& expect
->in_group
&& expect
->in_group
!= ippGetGroupTag(found
))
1708 while ((found
= ippFindNextAttribute(response
, expect
->name
, IPP_TAG_ZERO
)) != NULL
)
1709 if (expect
->in_group
== ippGetGroupTag(found
))
1715 group_found
= found
;
1717 if (expect
->in_group
&& strchr(expect
->name
, '/'))
1719 char group_name
[256],/* Parent attribute name */
1720 *group_ptr
; /* Pointer into parent attribute name */
1722 strlcpy(group_name
, expect
->name
, sizeof(group_name
));
1723 if ((group_ptr
= strchr(group_name
, '/')) != NULL
)
1726 group_found
= ippFindAttribute(response
, group_name
, IPP_TAG_ZERO
);
1729 if ((found
&& expect
->not_expect
) ||
1730 (!found
&& !(expect
->not_expect
|| expect
->optional
)) ||
1731 (found
&& !expect_matches(expect
, found
)) ||
1732 (group_found
&& expect
->in_group
&& ippGetGroupTag(group_found
) != expect
->in_group
) ||
1733 (expect
->with_distinct
&& !with_distinct_values(NULL
, found
)))
1735 if (expect
->define_no_match
)
1736 _ippVarsSet(data
->vars
, expect
->define_no_match
, "1");
1737 else if (!expect
->define_match
&& !expect
->define_value
)
1739 if (found
&& expect
->not_expect
&& !expect
->with_value
&& !expect
->with_value_from
)
1740 add_stringf(data
->errors
, "NOT EXPECTED: %s", expect
->name
);
1741 else if (!found
&& !(expect
->not_expect
|| expect
->optional
))
1742 add_stringf(data
->errors
, "EXPECTED: %s", expect
->name
);
1745 if (!expect_matches(expect
, found
))
1746 add_stringf(data
->errors
, "EXPECTED: %s OF-TYPE %s (got %s)",
1747 expect
->name
, expect
->of_type
,
1748 ippTagString(ippGetValueTag(found
)));
1750 if (expect
->in_group
&& ippGetGroupTag(group_found
) != expect
->in_group
)
1751 add_stringf(data
->errors
, "EXPECTED: %s IN-GROUP %s (got %s).",
1752 expect
->name
, ippTagString(expect
->in_group
),
1753 ippTagString(ippGetGroupTag(group_found
)));
1755 if (expect
->with_distinct
)
1756 with_distinct_values(data
->errors
, found
);
1760 if (expect
->repeat_no_match
&& repeat_count
< expect
->repeat_limit
)
1766 ippAttributeString(found
, buffer
, sizeof(buffer
));
1768 if (found
&& expect
->with_value_from
&& !with_value_from(NULL
, ippFindAttribute(response
, expect
->with_value_from
, IPP_TAG_ZERO
), found
, buffer
, sizeof(buffer
)))
1770 if (expect
->define_no_match
)
1771 _ippVarsSet(data
->vars
, expect
->define_no_match
, "1");
1772 else if (!expect
->define_match
&& !expect
->define_value
&& ((!expect
->repeat_match
&& !expect
->repeat_no_match
) || repeat_count
>= expect
->repeat_limit
))
1774 add_stringf(data
->errors
, "EXPECTED: %s WITH-VALUES-FROM %s", expect
->name
, expect
->with_value_from
);
1776 with_value_from(data
->errors
, ippFindAttribute(response
, expect
->with_value_from
, IPP_TAG_ZERO
), found
, buffer
, sizeof(buffer
));
1779 if (expect
->repeat_no_match
&& repeat_count
< expect
->repeat_limit
)
1784 else if (found
&& !with_value(data
, NULL
, expect
->with_value
, expect
->with_flags
, found
, buffer
, sizeof(buffer
)))
1786 if (expect
->define_no_match
)
1787 _ippVarsSet(data
->vars
, expect
->define_no_match
, "1");
1788 else if (!expect
->define_match
&& !expect
->define_value
&&
1789 !expect
->repeat_match
&& (!expect
->repeat_no_match
|| repeat_count
>= expect
->repeat_limit
))
1791 if (expect
->with_flags
& IPPTOOL_WITH_REGEX
)
1792 add_stringf(data
->errors
, "EXPECTED: %s %s /%s/", expect
->name
, with_flags_string(expect
->with_flags
), expect
->with_value
);
1794 add_stringf(data
->errors
, "EXPECTED: %s %s \"%s\"", expect
->name
, with_flags_string(expect
->with_flags
), expect
->with_value
);
1796 with_value(data
, data
->errors
, expect
->with_value
, expect
->with_flags
, found
, buffer
, sizeof(buffer
));
1799 if (expect
->repeat_no_match
&&
1800 repeat_count
< expect
->repeat_limit
)
1806 if (found
&& expect
->count
> 0 && ippGetCount(found
) != expect
->count
)
1808 if (expect
->define_no_match
)
1809 _ippVarsSet(data
->vars
, expect
->define_no_match
, "1");
1810 else if (!expect
->define_match
&& !expect
->define_value
)
1812 add_stringf(data
->errors
, "EXPECTED: %s COUNT %d (got %d)", expect
->name
, expect
->count
, ippGetCount(found
));
1815 if (expect
->repeat_no_match
&&
1816 repeat_count
< expect
->repeat_limit
)
1822 if (found
&& expect
->same_count_as
)
1824 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
1827 if (!attrptr
|| ippGetCount(attrptr
) != ippGetCount(found
))
1829 if (expect
->define_no_match
)
1830 _ippVarsSet(data
->vars
, expect
->define_no_match
, "1");
1831 else if (!expect
->define_match
&& !expect
->define_value
)
1834 add_stringf(data
->errors
, "EXPECTED: %s (%d values) SAME-COUNT-AS %s (not returned)", expect
->name
, ippGetCount(found
), expect
->same_count_as
);
1835 else if (ippGetCount(attrptr
) != ippGetCount(found
))
1836 add_stringf(data
->errors
, "EXPECTED: %s (%d values) SAME-COUNT-AS %s (%d values)", expect
->name
, ippGetCount(found
), expect
->same_count_as
, ippGetCount(attrptr
));
1839 if (expect
->repeat_no_match
&&
1840 repeat_count
< expect
->repeat_limit
)
1847 if (found
&& expect
->define_match
)
1848 _ippVarsSet(data
->vars
, expect
->define_match
, "1");
1850 if (found
&& expect
->define_value
)
1852 if (!expect
->with_value
)
1854 int last
= ippGetCount(found
) - 1;
1855 /* Last element in attribute */
1857 switch (ippGetValueTag(found
))
1860 case IPP_TAG_INTEGER
:
1861 snprintf(buffer
, sizeof(buffer
), "%d", ippGetInteger(found
, last
));
1864 case IPP_TAG_BOOLEAN
:
1865 if (ippGetBoolean(found
, last
))
1866 strlcpy(buffer
, "true", sizeof(buffer
));
1868 strlcpy(buffer
, "false", sizeof(buffer
));
1871 case IPP_TAG_RESOLUTION
:
1873 int xres
, /* Horizontal resolution */
1874 yres
; /* Vertical resolution */
1875 ipp_res_t units
; /* Resolution units */
1877 xres
= ippGetResolution(found
, last
, &yres
, &units
);
1880 snprintf(buffer
, sizeof(buffer
), "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
1882 snprintf(buffer
, sizeof(buffer
), "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
1886 case IPP_TAG_CHARSET
:
1887 case IPP_TAG_KEYWORD
:
1888 case IPP_TAG_LANGUAGE
:
1889 case IPP_TAG_MIMETYPE
:
1891 case IPP_TAG_NAMELANG
:
1893 case IPP_TAG_TEXTLANG
:
1895 case IPP_TAG_URISCHEME
:
1896 strlcpy(buffer
, ippGetString(found
, last
, NULL
), sizeof(buffer
));
1900 ippAttributeString(found
, buffer
, sizeof(buffer
));
1905 _ippVarsSet(data
->vars
, expect
->define_value
, buffer
);
1908 if (found
&& expect
->repeat_match
&&
1909 repeat_count
< expect
->repeat_limit
)
1912 while (expect
->expect_all
&& (found
= ippFindNextAttribute(response
, expect
->name
, IPP_TAG_ZERO
)) != NULL
);
1917 * If we are going to repeat this test, display intermediate results...
1922 if (data
->output
== IPPTOOL_OUTPUT_TEST
|| (data
->output
== IPPTOOL_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
1924 cupsFilePrintf(cupsFileStdout(), "%04d]\n", repeat_count
);
1926 if (data
->num_displayed
> 0)
1928 for (attrptr
= ippFirstAttribute(response
); attrptr
; attrptr
= ippNextAttribute(response
))
1930 const char *attrname
= ippGetName(attrptr
);
1933 for (i
= 0; i
< data
->num_displayed
; i
++)
1935 if (!strcmp(data
->displayed
[i
], attrname
))
1937 print_attr(cupsFileStdout(), IPPTOOL_OUTPUT_TEST
, attrptr
, NULL
);
1946 if (data
->output
== IPPTOOL_OUTPUT_TEST
|| (data
->output
== IPPTOOL_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
1948 cupsFilePrintf(cupsFileStdout(), " %-68.68s [", data
->name
);
1951 ippDelete(response
);
1955 while (repeat_test
);
1961 if (cupsArrayCount(data
->errors
) > 0)
1962 data
->prev_pass
= data
->pass
= 0;
1964 if (data
->prev_pass
)
1965 data
->pass_count
++;
1967 data
->fail_count
++;
1969 if (data
->output
== IPPTOOL_OUTPUT_PLIST
)
1971 cupsFilePuts(data
->outfile
, "<key>Successful</key>\n");
1972 cupsFilePuts(data
->outfile
, data
->prev_pass
? "<true />\n" : "<false />\n");
1973 cupsFilePuts(data
->outfile
, "<key>StatusCode</key>\n");
1974 print_xml_string(data
->outfile
, "string", ippErrorString(cupsLastError()));
1975 cupsFilePuts(data
->outfile
, "<key>ResponseAttributes</key>\n");
1976 cupsFilePuts(data
->outfile
, "<array>\n");
1977 cupsFilePuts(data
->outfile
, "<dict>\n");
1978 for (attrptr
= ippFirstAttribute(response
), group
= ippGetGroupTag(attrptr
);
1980 attrptr
= ippNextAttribute(response
))
1981 print_attr(data
->outfile
, data
->output
, attrptr
, &group
);
1982 cupsFilePuts(data
->outfile
, "</dict>\n");
1983 cupsFilePuts(data
->outfile
, "</array>\n");
1985 else if (data
->output
== IPPTOOL_OUTPUT_IPPSERVER
&& response
)
1987 for (attrptr
= ippFirstAttribute(response
); attrptr
; attrptr
= ippNextAttribute(response
))
1989 if (!ippGetName(attrptr
) || ippGetGroupTag(attrptr
) != IPP_TAG_PRINTER
)
1992 print_ippserver_attr(data
, attrptr
, 0);
1996 if (data
->output
== IPPTOOL_OUTPUT_TEST
|| (data
->output
== IPPTOOL_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
1998 cupsFilePuts(cupsFileStdout(), data
->prev_pass
? "PASS]\n" : "FAIL]\n");
2000 if (!data
->prev_pass
|| (data
->verbosity
&& response
))
2002 cupsFilePrintf(cupsFileStdout(), " RECEIVED: %lu bytes in response\n", (unsigned long)ippLength(response
));
2003 cupsFilePrintf(cupsFileStdout(), " status-code = %s (%s)\n", ippErrorString(cupsLastError()), cupsLastErrorString());
2005 if (data
->verbosity
&& response
)
2007 for (attrptr
= ippFirstAttribute(response
); attrptr
; attrptr
= ippNextAttribute(response
))
2008 print_attr(cupsFileStdout(), IPPTOOL_OUTPUT_TEST
, attrptr
, NULL
);
2012 else if (!data
->prev_pass
&& data
->output
!= IPPTOOL_OUTPUT_QUIET
)
2013 fprintf(stderr
, "%s\n", cupsLastErrorString());
2015 if (data
->prev_pass
&& data
->output
>= IPPTOOL_OUTPUT_LIST
&& !data
->verbosity
&& data
->num_displayed
> 0)
2017 size_t width
; /* Length of value */
2019 for (i
= 0; i
< data
->num_displayed
; i
++)
2021 widths
[i
] = strlen(data
->displayed
[i
]);
2023 for (attrptr
= ippFindAttribute(response
, data
->displayed
[i
], IPP_TAG_ZERO
);
2025 attrptr
= ippFindNextAttribute(response
, data
->displayed
[i
], IPP_TAG_ZERO
))
2027 width
= ippAttributeString(attrptr
, NULL
, 0);
2028 if (width
> widths
[i
])
2033 if (data
->output
== IPPTOOL_OUTPUT_CSV
)
2034 print_csv(data
, NULL
, NULL
, data
->num_displayed
, data
->displayed
, widths
);
2036 print_line(data
, NULL
, NULL
, data
->num_displayed
, data
->displayed
, widths
);
2038 attrptr
= ippFirstAttribute(response
);
2042 while (attrptr
&& ippGetGroupTag(attrptr
) <= IPP_TAG_OPERATION
)
2043 attrptr
= ippNextAttribute(response
);
2047 if (data
->output
== IPPTOOL_OUTPUT_CSV
)
2048 attrptr
= print_csv(data
, response
, attrptr
, data
->num_displayed
, data
->displayed
, widths
);
2050 attrptr
= print_line(data
, response
, attrptr
, data
->num_displayed
, data
->displayed
, widths
);
2052 while (attrptr
&& ippGetGroupTag(attrptr
) > IPP_TAG_OPERATION
)
2053 attrptr
= ippNextAttribute(response
);
2057 else if (!data
->prev_pass
)
2059 if (data
->output
== IPPTOOL_OUTPUT_PLIST
)
2061 cupsFilePuts(data
->outfile
, "<key>Errors</key>\n");
2062 cupsFilePuts(data
->outfile
, "<array>\n");
2064 for (error
= (char *)cupsArrayFirst(data
->errors
);
2066 error
= (char *)cupsArrayNext(data
->errors
))
2067 print_xml_string(data
->outfile
, "string", error
);
2069 cupsFilePuts(data
->outfile
, "</array>\n");
2072 if (data
->output
== IPPTOOL_OUTPUT_TEST
|| (data
->output
== IPPTOOL_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
2074 for (error
= (char *)cupsArrayFirst(data
->errors
);
2076 error
= (char *)cupsArrayNext(data
->errors
))
2077 cupsFilePrintf(cupsFileStdout(), " %s\n", error
);
2081 if (data
->num_displayed
> 0 && !data
->verbosity
&& response
&& (data
->output
== IPPTOOL_OUTPUT_TEST
|| (data
->output
== IPPTOOL_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout())))
2083 for (attrptr
= ippFirstAttribute(response
); attrptr
; attrptr
= ippNextAttribute(response
))
2085 if (ippGetName(attrptr
))
2087 for (i
= 0; i
< data
->num_displayed
; i
++)
2089 if (!strcmp(data
->displayed
[i
], ippGetName(attrptr
)))
2091 print_attr(data
->outfile
, data
->output
, attrptr
, NULL
);
2101 if (data
->monitor_thread
)
2103 data
->monitor_done
= 1;
2104 _cupsThreadWait(data
->monitor_thread
);
2107 if (data
->output
== IPPTOOL_OUTPUT_PLIST
)
2108 cupsFilePuts(data
->outfile
, "</dict>\n");
2110 ippDelete(response
);
2113 for (i
= 0; i
< data
->num_statuses
; i
++)
2115 if (data
->statuses
[i
].if_defined
)
2116 free(data
->statuses
[i
].if_defined
);
2117 if (data
->statuses
[i
].if_not_defined
)
2118 free(data
->statuses
[i
].if_not_defined
);
2119 if (data
->statuses
[i
].define_match
)
2120 free(data
->statuses
[i
].define_match
);
2121 if (data
->statuses
[i
].define_no_match
)
2122 free(data
->statuses
[i
].define_no_match
);
2124 data
->num_statuses
= 0;
2126 for (i
= data
->num_expects
, expect
= data
->expects
; i
> 0; i
--, expect
++)
2129 if (expect
->of_type
)
2130 free(expect
->of_type
);
2131 if (expect
->same_count_as
)
2132 free(expect
->same_count_as
);
2133 if (expect
->if_defined
)
2134 free(expect
->if_defined
);
2135 if (expect
->if_not_defined
)
2136 free(expect
->if_not_defined
);
2137 if (expect
->with_value
)
2138 free(expect
->with_value
);
2139 if (expect
->define_match
)
2140 free(expect
->define_match
);
2141 if (expect
->define_no_match
)
2142 free(expect
->define_no_match
);
2143 if (expect
->define_value
)
2144 free(expect
->define_value
);
2146 data
->num_expects
= 0;
2148 for (i
= 0; i
< data
->num_displayed
; i
++)
2149 free(data
->displayed
[i
]);
2150 data
->num_displayed
= 0;
2152 free(data
->monitor_uri
);
2153 data
->monitor_uri
= NULL
;
2155 for (i
= data
->num_monitor_expects
, expect
= data
->monitor_expects
; i
> 0; i
--, expect
++)
2158 if (expect
->of_type
)
2159 free(expect
->of_type
);
2160 if (expect
->same_count_as
)
2161 free(expect
->same_count_as
);
2162 if (expect
->if_defined
)
2163 free(expect
->if_defined
);
2164 if (expect
->if_not_defined
)
2165 free(expect
->if_not_defined
);
2166 if (expect
->with_value
)
2167 free(expect
->with_value
);
2168 if (expect
->define_match
)
2169 free(expect
->define_match
);
2170 if (expect
->define_no_match
)
2171 free(expect
->define_no_match
);
2172 if (expect
->define_value
)
2173 free(expect
->define_value
);
2175 data
->num_monitor_expects
= 0;
2177 return (data
->ignore_errors
|| data
->prev_pass
);
2182 * 'do_tests()' - Do tests as specified in the test file.
2185 static int /* O - 1 on success, 0 on failure */
2186 do_tests(const char *testfile
, /* I - Test file to use */
2187 ipptool_test_t
*data
) /* I - Test data */
2189 http_encryption_t encryption
; /* Encryption mode */
2193 * Connect to the printer/server...
2196 if (!_cups_strcasecmp(data
->vars
->scheme
, "https") || !_cups_strcasecmp(data
->vars
->scheme
, "ipps") || data
->vars
->port
== 443)
2197 encryption
= HTTP_ENCRYPTION_ALWAYS
;
2199 encryption
= data
->encryption
;
2201 if ((data
->http
= httpConnect2(data
->vars
->host
, data
->vars
->port
, NULL
, data
->family
, encryption
, 1, 30000, NULL
)) == NULL
)
2203 print_fatal_error(data
, "Unable to connect to \"%s\" on port %d - %s", data
->vars
->host
, data
->vars
->port
, cupsLastErrorString());
2208 httpSetDefaultField(data
->http
, HTTP_FIELD_ACCEPT_ENCODING
, "deflate, gzip, identity");
2210 httpSetDefaultField(data
->http
, HTTP_FIELD_ACCEPT_ENCODING
, "identity");
2211 #endif /* HAVE_LIBZ */
2213 if (data
->timeout
> 0.0)
2214 httpSetTimeout(data
->http
, data
->timeout
, timeout_cb
, NULL
);
2220 _ippFileParse(data
->vars
, testfile
, (void *)data
);
2223 * Close connection and return...
2226 httpClose(data
->http
);
2229 return (data
->pass
);
2234 * 'error_cb()' - Print/add an error message.
2237 static int /* O - 1 to continue, 0 to stop */
2238 error_cb(_ipp_file_t
*f
, /* I - IPP file data */
2239 ipptool_test_t
*data
, /* I - Test data */
2240 const char *error
) /* I - Error message */
2244 print_fatal_error(data
, "%s", error
);
2251 * 'expect_matches()' - Return true if the tag matches the specification.
2254 static int /* O - 1 if matches, 0 otherwise */
2256 ipptool_expect_t
*expect
, /* I - Expected attribute */
2257 ipp_attribute_t
*attr
) /* I - Attribute */
2259 int i
, /* Looping var */
2260 count
, /* Number of values */
2262 char *of_type
, /* Type name to match */
2263 *paren
, /* Pointer to opening parenthesis */
2264 *next
, /* Next name to match */
2265 sep
; /* Separator character */
2266 ipp_tag_t value_tag
; /* Syntax/value tag */
2267 int lower
, upper
; /* Lower and upper bounds for syntax */
2271 * If we don't expect a particular type, return immediately...
2274 if (!expect
->of_type
)
2278 * Parse the "of_type" value since the string can contain multiple attribute
2279 * types separated by "," or "|"...
2282 value_tag
= ippGetValueTag(attr
);
2283 count
= ippGetCount(attr
);
2285 for (of_type
= expect
->of_type
, match
= 0; !match
&& *of_type
; of_type
= next
)
2288 * Find the next separator, and set it (temporarily) to nul if present.
2291 for (next
= of_type
; *next
&& *next
!= '|' && *next
!= ','; next
++);
2293 if ((sep
= *next
) != '\0')
2297 * Support some meta-types to make it easier to write the test file.
2300 if ((paren
= strchr(of_type
, '(')) != NULL
)
2302 char *ptr
; // Pointer into syntax string
2306 if (!strncmp(paren
+ 1, "MIN:", 4))
2311 else if ((ptr
= strchr(paren
+ 1, ':')) != NULL
)
2313 lower
= atoi(paren
+ 1);
2321 if (!strcmp(ptr
, "MAX)"))
2332 if (!strcmp(of_type
, "text"))
2334 if (upper
== INT_MAX
)
2337 if (value_tag
== IPP_TAG_TEXTLANG
|| value_tag
== IPP_TAG_TEXT
)
2339 for (i
= 0; i
< count
; i
++)
2341 if (strlen(ippGetString(attr
, i
, NULL
)) > (size_t)upper
)
2345 match
= (i
== count
);
2348 else if (!strcmp(of_type
, "name"))
2350 if (upper
== INT_MAX
)
2353 if (value_tag
== IPP_TAG_NAMELANG
|| value_tag
== IPP_TAG_NAME
)
2355 for (i
= 0; i
< count
; i
++)
2357 if (strlen(ippGetString(attr
, i
, NULL
)) > (size_t)upper
)
2361 match
= (i
== count
);
2364 else if (!strcmp(of_type
, "collection"))
2366 match
= value_tag
== IPP_TAG_BEGIN_COLLECTION
;
2368 else if (value_tag
== ippTagValue(of_type
))
2372 case IPP_TAG_KEYWORD
:
2374 if (upper
== INT_MAX
)
2376 if (value_tag
== IPP_TAG_KEYWORD
)
2382 for (i
= 0; i
< count
; i
++)
2384 if (strlen(ippGetString(attr
, i
, NULL
)) > (size_t)upper
)
2388 match
= (i
== count
);
2391 case IPP_TAG_STRING
:
2392 if (upper
== INT_MAX
)
2395 for (i
= 0; i
< count
; i
++)
2397 int datalen
; // Length of octetString value
2399 ippGetOctetString(attr
, i
, &datalen
);
2401 if (datalen
> upper
)
2405 match
= (i
== count
);
2408 case IPP_TAG_INTEGER
:
2409 for (i
= 0; i
< count
; i
++)
2411 int value
= ippGetInteger(attr
, i
);
2414 if (value
< lower
|| value
> upper
)
2418 match
= (i
== count
);
2421 case IPP_TAG_RANGE
:
2422 for (i
= 0; i
< count
; i
++)
2424 int vupper
, vlower
= ippGetRange(attr
, i
, &vupper
);
2427 if (vlower
< lower
|| vlower
> upper
|| vupper
< lower
|| vupper
> upper
)
2431 match
= (i
== count
);
2435 // No other constraints, so this is a match
2442 * Restore the separators if we have them...
2457 * 'get_filename()' - Get a filename based on the current test file.
2460 static char * /* O - Filename */
2461 get_filename(const char *testfile
, /* I - Current test file */
2462 char *dst
, /* I - Destination filename */
2463 const char *src
, /* I - Source filename */
2464 size_t dstsize
) /* I - Size of destination buffer */
2466 char *dstptr
; /* Pointer into destination */
2467 _cups_globals_t
*cg
= _cupsGlobals();
2471 if (*src
== '<' && src
[strlen(src
) - 1] == '>')
2474 * Map <filename> to CUPS_DATADIR/ipptool/filename...
2477 snprintf(dst
, dstsize
, "%s/ipptool/%s", cg
->cups_datadir
, src
+ 1);
2478 dstptr
= dst
+ strlen(dst
) - 1;
2482 else if (!access(src
, R_OK
) || *src
== '/'
2484 || (isalpha(*src
& 255) && src
[1] == ':')
2489 * Use the path as-is...
2492 strlcpy(dst
, src
, dstsize
);
2497 * Make path relative to testfile...
2500 strlcpy(dst
, testfile
, dstsize
);
2501 if ((dstptr
= strrchr(dst
, '/')) != NULL
)
2504 dstptr
= dst
; /* Should never happen */
2506 strlcpy(dstptr
, src
, dstsize
- (size_t)(dstptr
- dst
));
2509 if (_access(dst
, 0))
2512 * Not available relative to the testfile, see if it can be found on the
2515 const char *userprofile
= getenv("USERPROFILE");
2516 /* User home directory */
2519 snprintf(dst
, dstsize
, "%s/Desktop/%s", userprofile
, src
);
2529 * 'get_string()' - Get a pointer to a string value or the portion of interest.
2532 static const char * /* O - Pointer to string */
2533 get_string(ipp_attribute_t
*attr
, /* I - IPP attribute */
2534 int element
, /* I - Element to fetch */
2535 int flags
, /* I - Value ("with") flags */
2536 char *buffer
, /* I - Temporary buffer */
2537 size_t bufsize
) /* I - Size of temporary buffer */
2539 const char *value
; /* Value */
2540 char *ptr
, /* Pointer into value */
2541 scheme
[256], /* URI scheme */
2542 userpass
[256], /* Username/password */
2543 hostname
[256], /* Hostname */
2544 resource
[1024]; /* Resource */
2545 int port
; /* Port number */
2548 value
= ippGetString(attr
, element
, NULL
);
2550 if (flags
& IPPTOOL_WITH_HOSTNAME
)
2552 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), buffer
, (int)bufsize
, &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
2555 ptr
= buffer
+ strlen(buffer
) - 1;
2556 if (ptr
>= buffer
&& *ptr
== '.')
2557 *ptr
= '\0'; /* Drop trailing "." */
2561 else if (flags
& IPPTOOL_WITH_RESOURCE
)
2563 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, buffer
, (int)bufsize
) < HTTP_URI_STATUS_OK
)
2568 else if (flags
& IPPTOOL_WITH_SCHEME
)
2570 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, buffer
, (int)bufsize
, userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
2575 else if (ippGetValueTag(attr
) == IPP_TAG_URI
&& (!strncmp(value
, "ipp://", 6) || !strncmp(value
, "http://", 7) || !strncmp(value
, "ipps://", 7) || !strncmp(value
, "https://", 8)))
2577 http_uri_status_t status
= httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
));
2579 if (status
< HTTP_URI_STATUS_OK
)
2590 * Normalize URI with no trailing dot...
2593 if ((ptr
= hostname
+ strlen(hostname
) - 1) >= hostname
&& *ptr
== '.')
2596 httpAssembleURI(HTTP_URI_CODING_ALL
, buffer
, (int)bufsize
, scheme
, userpass
, hostname
, port
, resource
);
2607 * 'init_data()' - Initialize test data.
2611 init_data(ipptool_test_t
*data
) /* I - Data */
2613 memset(data
, 0, sizeof(ipptool_test_t
));
2615 data
->output
= IPPTOOL_OUTPUT_LIST
;
2616 data
->outfile
= cupsFileStdout();
2617 data
->family
= AF_UNSPEC
;
2618 data
->def_transfer
= IPPTOOL_TRANSFER_AUTO
;
2619 data
->def_version
= 11;
2620 data
->errors
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
, (cups_afree_func_t
)free
);
2622 data
->prev_pass
= 1;
2623 data
->request_id
= (CUPS_RAND() % 1000) * 137;
2624 data
->show_header
= 1;
2629 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
2633 static char * /* O - ISO 8601 date/time string */
2634 iso_date(const ipp_uchar_t
*date
) /* I - IPP (RFC 1903) date/time value */
2636 time_t utctime
; /* UTC time since 1970 */
2637 struct tm utcdate
; /* UTC date/time */
2638 static char buffer
[255]; /* String buffer */
2641 utctime
= ippDateToTime(date
);
2642 gmtime_r(&utctime
, &utcdate
);
2644 snprintf(buffer
, sizeof(buffer
), "%04d-%02d-%02dT%02d:%02d:%02dZ",
2645 utcdate
.tm_year
+ 1900, utcdate
.tm_mon
+ 1, utcdate
.tm_mday
,
2646 utcdate
.tm_hour
, utcdate
.tm_min
, utcdate
.tm_sec
);
2653 * 'parse_monitor_printer_state()' - Parse the MONITOR-PRINTER-STATE directive.
2655 * MONITOR-PRINTER-STATE [printer-uri] {
2657 * EXPECT attribute-name ...
2661 static int /* O - 1 to continue, 0 to stop */
2662 parse_monitor_printer_state(
2663 _ipp_file_t
*f
, /* I - IPP file data */
2664 ipptool_test_t
*data
) /* I - Test data */
2666 char token
[256], /* Token string */
2667 name
[1024], /* Name string */
2668 temp
[1024], /* Temporary string */
2669 value
[1024], /* Value string */
2670 *ptr
; /* Pointer into value */
2673 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
2675 print_fatal_error(data
, "Missing printer URI on line %d of \"%s\".", f
->linenum
, f
->filename
);
2679 if (strcmp(temp
, "{"))
2681 // Got a printer URI so copy it...
2682 _ippVarsExpand(data
->vars
, value
, temp
, sizeof(value
));
2683 data
->monitor_uri
= strdup(value
);
2685 // Then see if we have an opening brace...
2686 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)) || strcmp(temp
, "{"))
2688 print_fatal_error(data
, "Missing opening brace on line %d of \"%s\".", f
->linenum
, f
->filename
);
2694 // Use the default printer URI...
2695 data
->monitor_uri
= strdup(data
->vars
->uri
);
2698 // Loop until we get a closing brace...
2699 while (_ippFileReadToken(f
, token
, sizeof(token
)))
2701 if (_cups_strcasecmp(token
, "COUNT") &&
2702 _cups_strcasecmp(token
, "DEFINE-MATCH") &&
2703 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
2704 _cups_strcasecmp(token
, "DEFINE-VALUE") &&
2705 _cups_strcasecmp(token
, "IF-DEFINED") &&
2706 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
2707 _cups_strcasecmp(token
, "IN-GROUP") &&
2708 _cups_strcasecmp(token
, "OF-TYPE") &&
2709 _cups_strcasecmp(token
, "WITH-DISTINCT-VALUES") &&
2710 _cups_strcasecmp(token
, "WITH-VALUE"))
2711 data
->last_expect
= NULL
;
2713 if (!strcmp(token
, "}"))
2715 else if (!_cups_strcasecmp(token
, "EXPECT"))
2718 * Expected attributes...
2721 if (data
->num_monitor_expects
>= (int)(sizeof(data
->monitor_expects
) / sizeof(data
->monitor_expects
[0])))
2723 print_fatal_error(data
, "Too many EXPECT's on line %d of \"%s\".", f
->linenum
, f
->filename
);
2727 if (!_ippFileReadToken(f
, name
, sizeof(name
)))
2729 print_fatal_error(data
, "Missing EXPECT name on line %d of \"%s\".", f
->linenum
, f
->filename
);
2733 data
->last_expect
= data
->monitor_expects
+ data
->num_monitor_expects
;
2734 data
->num_monitor_expects
++;
2736 memset(data
->last_expect
, 0, sizeof(ipptool_expect_t
));
2737 data
->last_expect
->repeat_limit
= 1000;
2741 data
->last_expect
->not_expect
= 1;
2742 data
->last_expect
->name
= strdup(name
+ 1);
2744 else if (name
[0] == '?')
2746 data
->last_expect
->optional
= 1;
2747 data
->last_expect
->name
= strdup(name
+ 1);
2750 data
->last_expect
->name
= strdup(name
);
2752 else if (!_cups_strcasecmp(token
, "COUNT"))
2754 int count
; /* Count value */
2756 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
2758 print_fatal_error(data
, "Missing COUNT number on line %d of \"%s\".", f
->linenum
, f
->filename
);
2762 if ((count
= atoi(temp
)) <= 0)
2764 print_fatal_error(data
, "Bad COUNT \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
2768 if (data
->last_expect
)
2770 data
->last_expect
->count
= count
;
2774 print_fatal_error(data
, "COUNT without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
2778 else if (!_cups_strcasecmp(token
, "DEFINE-MATCH"))
2780 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
2782 print_fatal_error(data
, "Missing DEFINE-MATCH variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
2786 if (data
->last_expect
)
2788 data
->last_expect
->define_match
= strdup(temp
);
2792 print_fatal_error(data
, "DEFINE-MATCH without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
2796 else if (!_cups_strcasecmp(token
, "DEFINE-NO-MATCH"))
2798 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
2800 print_fatal_error(data
, "Missing DEFINE-NO-MATCH variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
2804 if (data
->last_expect
)
2806 data
->last_expect
->define_no_match
= strdup(temp
);
2810 print_fatal_error(data
, "DEFINE-NO-MATCH without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
2814 else if (!_cups_strcasecmp(token
, "DEFINE-VALUE"))
2816 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
2818 print_fatal_error(data
, "Missing DEFINE-VALUE variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
2822 if (data
->last_expect
)
2824 data
->last_expect
->define_value
= strdup(temp
);
2828 print_fatal_error(data
, "DEFINE-VALUE without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
2832 else if (!_cups_strcasecmp(token
, "DELAY"))
2835 * Delay before operation...
2838 double dval
; /* Delay value */
2840 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
2842 print_fatal_error(data
, "Missing DELAY value on line %d of \"%s\".", f
->linenum
, f
->filename
);
2846 _ippVarsExpand(data
->vars
, value
, temp
, sizeof(value
));
2848 if ((dval
= _cupsStrScand(value
, &ptr
, localeconv())) < 0.0 || (*ptr
&& *ptr
!= ','))
2850 print_fatal_error(data
, "Bad DELAY value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
2854 data
->monitor_delay
= (useconds_t
)(1000000.0 * dval
);
2858 if ((dval
= _cupsStrScand(ptr
+ 1, &ptr
, localeconv())) <= 0.0 || *ptr
)
2860 print_fatal_error(data
, "Bad DELAY value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
2864 data
->monitor_interval
= (useconds_t
)(1000000.0 * dval
);
2867 data
->monitor_interval
= data
->monitor_delay
;
2869 else if (!_cups_strcasecmp(token
, "OF-TYPE"))
2871 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
2873 print_fatal_error(data
, "Missing OF-TYPE value tag(s) on line %d of \"%s\".", f
->linenum
, f
->filename
);
2877 if (data
->last_expect
)
2879 data
->last_expect
->of_type
= strdup(temp
);
2883 print_fatal_error(data
, "OF-TYPE without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
2887 else if (!_cups_strcasecmp(token
, "IN-GROUP"))
2889 ipp_tag_t in_group
; /* IN-GROUP value */
2891 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
2893 print_fatal_error(data
, "Missing IN-GROUP group tag on line %d of \"%s\".", f
->linenum
, f
->filename
);
2897 if ((in_group
= ippTagValue(temp
)) == IPP_TAG_ZERO
|| in_group
>= IPP_TAG_UNSUPPORTED_VALUE
)
2899 print_fatal_error(data
, "Bad IN-GROUP group tag \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
2902 else if (data
->last_expect
)
2904 data
->last_expect
->in_group
= in_group
;
2908 print_fatal_error(data
, "IN-GROUP without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
2912 else if (!_cups_strcasecmp(token
, "IF-DEFINED"))
2914 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
2916 print_fatal_error(data
, "Missing IF-DEFINED name on line %d of \"%s\".", f
->linenum
, f
->filename
);
2920 if (data
->last_expect
)
2922 data
->last_expect
->if_defined
= strdup(temp
);
2926 print_fatal_error(data
, "IF-DEFINED without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
2930 else if (!_cups_strcasecmp(token
, "IF-NOT-DEFINED"))
2932 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
2934 print_fatal_error(data
, "Missing IF-NOT-DEFINED name on line %d of \"%s\".", f
->linenum
, f
->filename
);
2938 if (data
->last_expect
)
2940 data
->last_expect
->if_not_defined
= strdup(temp
);
2944 print_fatal_error(data
, "IF-NOT-DEFINED without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
2948 else if (!_cups_strcasecmp(token
, "WITH-DISTINCT-VALUES"))
2950 if (data
->last_expect
)
2952 data
->last_expect
->with_distinct
= 1;
2956 print_fatal_error(data
, "%s without a preceding EXPECT on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
2960 else if (!_cups_strcasecmp(token
, "WITH-VALUE"))
2962 off_t lastpos
; /* Last file position */
2963 int lastline
; /* Last line number */
2965 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
2967 print_fatal_error(data
, "Missing %s value on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
2972 * Read additional comma-delimited values - needed since legacy test files
2973 * will have unquoted WITH-VALUE values with commas...
2976 ptr
= temp
+ strlen(temp
);
2980 lastpos
= cupsFileTell(f
->fp
);
2981 lastline
= f
->linenum
;
2984 if (!_ippFileReadToken(f
, ptr
, (sizeof(temp
) - (size_t)(ptr
- temp
))))
2987 if (!strcmp(ptr
, ","))
2995 if (!_ippFileReadToken(f
, ptr
, (sizeof(temp
) - (size_t)(ptr
- temp
))))
3001 * Not another value, stop here...
3004 cupsFileSeek(f
->fp
, lastpos
);
3005 f
->linenum
= lastline
;
3011 if (data
->last_expect
)
3014 * Expand any variables in the value and then save it.
3017 _ippVarsExpand(data
->vars
, value
, temp
, sizeof(value
));
3019 ptr
= value
+ strlen(value
) - 1;
3021 if (value
[0] == '/' && ptr
> value
&& *ptr
== '/')
3024 * WITH-VALUE is a POSIX extended regular expression.
3027 data
->last_expect
->with_value
= calloc(1, (size_t)(ptr
- value
));
3028 data
->last_expect
->with_flags
|= IPPTOOL_WITH_REGEX
;
3030 if (data
->last_expect
->with_value
)
3031 memcpy(data
->last_expect
->with_value
, value
+ 1, (size_t)(ptr
- value
- 1));
3036 * WITH-VALUE is a literal value...
3039 for (ptr
= value
; *ptr
; ptr
++)
3041 if (*ptr
== '\\' && ptr
[1])
3044 * Remove \ from \foo...
3047 _cups_strcpy(ptr
, ptr
+ 1);
3051 data
->last_expect
->with_value
= strdup(value
);
3052 data
->last_expect
->with_flags
|= IPPTOOL_WITH_LITERAL
;
3057 print_fatal_error(data
, "%s without a preceding EXPECT on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
3063 print_fatal_error(data
, "Missing closing brace on line %d of \"%s\".", f
->linenum
, f
->filename
);
3070 * 'pause_message()' - Display the message and pause until the user presses a key.
3074 pause_message(const char *message
) /* I - Message */
3077 HANDLE tty
; /* Console handle */
3078 DWORD mode
; /* Console mode */
3079 char key
; /* Key press */
3080 DWORD bytes
; /* Bytes read for key press */
3084 * Disable input echo and set raw input...
3087 if ((tty
= GetStdHandle(STD_INPUT_HANDLE
)) == INVALID_HANDLE_VALUE
)
3090 if (!GetConsoleMode(tty
, &mode
))
3093 if (!SetConsoleMode(tty
, 0))
3097 int tty
; /* /dev/tty - never read from stdin */
3098 struct termios original
, /* Original input mode */
3099 noecho
; /* No echo input mode */
3100 char key
; /* Current key press */
3104 * Disable input echo and set raw input...
3107 if ((tty
= open("/dev/tty", O_RDONLY
)) < 0)
3110 if (tcgetattr(tty
, &original
))
3117 noecho
.c_lflag
&= (tcflag_t
)~(ICANON
| ECHO
| ECHOE
| ISIG
);
3119 if (tcsetattr(tty
, TCSAFLUSH
, &noecho
))
3127 * Display the prompt...
3130 cupsFilePrintf(cupsFileStdout(), "%s\n---- PRESS ANY KEY ----", message
);
3137 ReadFile(tty
, &key
, 1, &bytes
, NULL
);
3143 SetConsoleMode(tty
, mode
);
3156 tcsetattr(tty
, TCSAFLUSH
, &original
);
3161 * Erase the "press any key" prompt...
3164 cupsFilePuts(cupsFileStdout(), "\r \r");
3169 * 'print_attr()' - Print an attribute on the screen.
3173 print_attr(cups_file_t
*outfile
, /* I - Output file */
3174 ipptool_output_t output
, /* I - Output format */
3175 ipp_attribute_t
*attr
, /* I - Attribute to print */
3176 ipp_tag_t
*group
) /* IO - Current group */
3178 int i
, /* Looping var */
3179 count
; /* Number of values */
3180 ipp_attribute_t
*colattr
; /* Collection attribute */
3183 if (output
== IPPTOOL_OUTPUT_PLIST
)
3185 if (!ippGetName(attr
) || (group
&& *group
!= ippGetGroupTag(attr
)))
3187 if (ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
3189 cupsFilePuts(outfile
, "</dict>\n");
3190 cupsFilePuts(outfile
, "<dict>\n");
3194 *group
= ippGetGroupTag(attr
);
3197 if (!ippGetName(attr
))
3200 print_xml_string(outfile
, "key", ippGetName(attr
));
3201 if ((count
= ippGetCount(attr
)) > 1)
3202 cupsFilePuts(outfile
, "<array>\n");
3204 switch (ippGetValueTag(attr
))
3206 case IPP_TAG_INTEGER
:
3208 for (i
= 0; i
< count
; i
++)
3209 cupsFilePrintf(outfile
, "<integer>%d</integer>\n", ippGetInteger(attr
, i
));
3212 case IPP_TAG_BOOLEAN
:
3213 for (i
= 0; i
< count
; i
++)
3214 cupsFilePuts(outfile
, ippGetBoolean(attr
, i
) ? "<true />\n" : "<false />\n");
3217 case IPP_TAG_RANGE
:
3218 for (i
= 0; i
< count
; i
++)
3220 int lower
, upper
; /* Lower and upper ranges */
3222 lower
= ippGetRange(attr
, i
, &upper
);
3223 cupsFilePrintf(outfile
, "<dict><key>lower</key><integer>%d</integer><key>upper</key><integer>%d</integer></dict>\n", lower
, upper
);
3227 case IPP_TAG_RESOLUTION
:
3228 for (i
= 0; i
< count
; i
++)
3230 int xres
, yres
; /* Resolution values */
3231 ipp_res_t units
; /* Resolution units */
3233 xres
= ippGetResolution(attr
, i
, &yres
, &units
);
3234 cupsFilePrintf(outfile
, "<dict><key>xres</key><integer>%d</integer><key>yres</key><integer>%d</integer><key>units</key><string>%s</string></dict>\n", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
3239 for (i
= 0; i
< count
; i
++)
3240 cupsFilePrintf(outfile
, "<date>%s</date>\n", iso_date(ippGetDate(attr
, i
)));
3243 case IPP_TAG_STRING
:
3244 for (i
= 0; i
< count
; i
++)
3246 int datalen
; /* Length of data */
3247 void *data
= ippGetOctetString(attr
, i
, &datalen
);
3249 char buffer
[IPP_MAX_LENGTH
* 5 / 4 + 1];
3250 /* Base64 output buffer */
3252 cupsFilePrintf(outfile
, "<data>%s</data>\n", httpEncode64_2(buffer
, sizeof(buffer
), data
, datalen
));
3258 case IPP_TAG_KEYWORD
:
3260 case IPP_TAG_URISCHEME
:
3261 case IPP_TAG_CHARSET
:
3262 case IPP_TAG_LANGUAGE
:
3263 case IPP_TAG_MIMETYPE
:
3264 for (i
= 0; i
< count
; i
++)
3265 print_xml_string(outfile
, "string", ippGetString(attr
, i
, NULL
));
3268 case IPP_TAG_TEXTLANG
:
3269 case IPP_TAG_NAMELANG
:
3270 for (i
= 0; i
< count
; i
++)
3272 const char *s
, /* String */
3273 *lang
; /* Language */
3275 s
= ippGetString(attr
, i
, &lang
);
3276 cupsFilePuts(outfile
, "<dict><key>language</key><string>");
3277 print_xml_string(outfile
, NULL
, lang
);
3278 cupsFilePuts(outfile
, "</string><key>string</key><string>");
3279 print_xml_string(outfile
, NULL
, s
);
3280 cupsFilePuts(outfile
, "</string></dict>\n");
3284 case IPP_TAG_BEGIN_COLLECTION
:
3285 for (i
= 0; i
< count
; i
++)
3287 ipp_t
*col
= ippGetCollection(attr
, i
);
3288 /* Collection value */
3290 cupsFilePuts(outfile
, "<dict>\n");
3291 for (colattr
= ippFirstAttribute(col
); colattr
; colattr
= ippNextAttribute(col
))
3292 print_attr(outfile
, output
, colattr
, NULL
);
3293 cupsFilePuts(outfile
, "</dict>\n");
3298 cupsFilePrintf(outfile
, "<string><<%s>></string>\n", ippTagString(ippGetValueTag(attr
)));
3303 cupsFilePuts(outfile
, "</array>\n");
3307 char buffer
[131072]; /* Value buffer */
3309 if (output
== IPPTOOL_OUTPUT_TEST
)
3311 if (!ippGetName(attr
))
3313 cupsFilePuts(outfile
, " -- separator --\n");
3317 cupsFilePrintf(outfile
, " %s (%s%s) = ", ippGetName(attr
), ippGetCount(attr
) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr
)));
3320 ippAttributeString(attr
, buffer
, sizeof(buffer
));
3321 cupsFilePrintf(outfile
, "%s\n", buffer
);
3327 * 'print_csv()' - Print a line of CSV text.
3330 static ipp_attribute_t
* /* O - Next attribute */
3332 ipptool_test_t
*data
, /* I - Test data */
3333 ipp_t
*ipp
, /* I - Response message */
3334 ipp_attribute_t
*attr
, /* I - First attribute for line */
3335 int num_displayed
, /* I - Number of attributes to display */
3336 char **displayed
, /* I - Attributes to display */
3337 size_t *widths
) /* I - Column widths */
3339 int i
; /* Looping var */
3340 size_t maxlength
; /* Max length of all columns */
3341 ipp_attribute_t
*current
= attr
; /* Current attribute */
3342 char *values
[MAX_DISPLAY
], /* Strings to display */
3343 *valptr
; /* Pointer into value */
3346 * Get the maximum string length we have to show and allocate...
3349 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
3350 if (widths
[i
] > maxlength
)
3351 maxlength
= widths
[i
];
3356 * Loop through the attributes to display...
3361 // Collect the values...
3362 memset(values
, 0, sizeof(values
));
3364 for (; current
; current
= ippNextAttribute(ipp
))
3366 if (!ippGetName(current
))
3369 for (i
= 0; i
< num_displayed
; i
++)
3371 if (!strcmp(ippGetName(current
), displayed
[i
]))
3373 if ((values
[i
] = (char *)calloc(1, maxlength
)) != NULL
)
3374 ippAttributeString(current
, values
[i
], maxlength
);
3380 // Output the line...
3381 for (i
= 0; i
< num_displayed
; i
++)
3384 cupsFilePutChar(data
->outfile
, ',');
3389 if (strchr(values
[i
], ',') != NULL
|| strchr(values
[i
], '\"') != NULL
|| strchr(values
[i
], '\\') != NULL
)
3392 cupsFilePutChar(data
->outfile
, '\"');
3393 for (valptr
= values
[i
]; *valptr
; valptr
++)
3395 if (*valptr
== '\\' || *valptr
== '\"')
3396 cupsFilePutChar(data
->outfile
, '\\');
3397 cupsFilePutChar(data
->outfile
, *valptr
);
3399 cupsFilePutChar(data
->outfile
, '\"');
3403 // Unquoted value...
3404 cupsFilePuts(data
->outfile
, values
[i
]);
3409 cupsFilePutChar(data
->outfile
, '\n');
3413 // Show column headings...
3414 for (i
= 0; i
< num_displayed
; i
++)
3417 cupsFilePutChar(data
->outfile
, ',');
3419 cupsFilePuts(data
->outfile
, displayed
[i
]);
3421 cupsFilePutChar(data
->outfile
, '\n');
3429 * 'print_fatal_error()' - Print a fatal error message.
3434 ipptool_test_t
*data
, /* I - Test data */
3435 const char *s
, /* I - Printf-style format string */
3436 ...) /* I - Additional arguments as needed */
3438 char buffer
[10240]; /* Format buffer */
3439 va_list ap
; /* Pointer to arguments */
3443 * Format the error message...
3447 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
3454 if (data
->output
== IPPTOOL_OUTPUT_PLIST
)
3456 print_xml_header(data
);
3457 print_xml_trailer(data
, 0, buffer
);
3460 _cupsLangPrintf(stderr
, "ipptool: %s", buffer
);
3465 * 'print_ippserver_attr()' - Print a attribute suitable for use by ippserver.
3469 print_ippserver_attr(
3470 ipptool_test_t
*data
, /* I - Test data */
3471 ipp_attribute_t
*attr
, /* I - Attribute to print */
3472 int indent
) /* I - Indentation level */
3474 int i
, /* Looping var */
3475 count
= ippGetCount(attr
);
3476 /* Number of values */
3477 ipp_attribute_t
*colattr
; /* Collection attribute */
3481 cupsFilePrintf(data
->outfile
, "ATTR %s %s", ippTagString(ippGetValueTag(attr
)), ippGetName(attr
));
3483 cupsFilePrintf(data
->outfile
, "%*sMEMBER %s %s", indent
, "", ippTagString(ippGetValueTag(attr
)), ippGetName(attr
));
3485 switch (ippGetValueTag(attr
))
3487 case IPP_TAG_INTEGER
:
3489 for (i
= 0; i
< count
; i
++)
3490 cupsFilePrintf(data
->outfile
, "%s%d", i
? "," : " ", ippGetInteger(attr
, i
));
3493 case IPP_TAG_BOOLEAN
:
3494 cupsFilePuts(data
->outfile
, ippGetBoolean(attr
, 0) ? " true" : " false");
3496 for (i
= 1; i
< count
; i
++)
3497 cupsFilePuts(data
->outfile
, ippGetBoolean(attr
, 1) ? ",true" : ",false");
3500 case IPP_TAG_RANGE
:
3501 for (i
= 0; i
< count
; i
++)
3503 int upper
, lower
= ippGetRange(attr
, i
, &upper
);
3505 cupsFilePrintf(data
->outfile
, "%s%d-%d", i
? "," : " ", lower
, upper
);
3509 case IPP_TAG_RESOLUTION
:
3510 for (i
= 0; i
< count
; i
++)
3513 int yres
, xres
= ippGetResolution(attr
, i
, &yres
, &units
);
3515 cupsFilePrintf(data
->outfile
, "%s%dx%d%s", i
? "," : " ", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
3520 for (i
= 0; i
< count
; i
++)
3521 cupsFilePrintf(data
->outfile
, "%s%s", i
? "," : " ", iso_date(ippGetDate(attr
, i
)));
3524 case IPP_TAG_STRING
:
3525 for (i
= 0; i
< count
; i
++)
3528 const char *s
= (const char *)ippGetOctetString(attr
, i
, &len
);
3530 cupsFilePuts(data
->outfile
, i
? "," : " ");
3531 print_ippserver_string(data
, s
, (size_t)len
);
3536 case IPP_TAG_TEXTLANG
:
3538 case IPP_TAG_NAMELANG
:
3539 case IPP_TAG_KEYWORD
:
3541 case IPP_TAG_URISCHEME
:
3542 case IPP_TAG_CHARSET
:
3543 case IPP_TAG_LANGUAGE
:
3544 case IPP_TAG_MIMETYPE
:
3545 for (i
= 0; i
< count
; i
++)
3547 const char *s
= ippGetString(attr
, i
, NULL
);
3549 cupsFilePuts(data
->outfile
, i
? "," : " ");
3550 print_ippserver_string(data
, s
, strlen(s
));
3554 case IPP_TAG_BEGIN_COLLECTION
:
3555 for (i
= 0; i
< count
; i
++)
3557 ipp_t
*col
= ippGetCollection(attr
, i
);
3559 cupsFilePuts(data
->outfile
, i
? ",{\n" : " {\n");
3560 for (colattr
= ippFirstAttribute(col
); colattr
; colattr
= ippNextAttribute(col
))
3561 print_ippserver_attr(data
, colattr
, indent
+ 4);
3562 cupsFilePrintf(data
->outfile
, "%*s}", indent
, "");
3567 /* Out-of-band value */
3571 cupsFilePuts(data
->outfile
, "\n");
3576 * 'print_ippserver_string()' - Print a string suitable for use by ippserver.
3580 print_ippserver_string(
3581 ipptool_test_t
*data
, /* I - Test data */
3582 const char *s
, /* I - String to print */
3583 size_t len
) /* I - Length of string */
3585 cupsFilePutChar(data
->outfile
, '\"');
3589 cupsFilePutChar(data
->outfile
, '\\');
3590 cupsFilePutChar(data
->outfile
, *s
);
3595 cupsFilePutChar(data
->outfile
, '\"');
3600 * 'print_line()' - Print a line of formatted or CSV text.
3603 static ipp_attribute_t
* /* O - Next attribute */
3605 ipptool_test_t
*data
, /* I - Test data */
3606 ipp_t
*ipp
, /* I - Response message */
3607 ipp_attribute_t
*attr
, /* I - First attribute for line */
3608 int num_displayed
, /* I - Number of attributes to display */
3609 char **displayed
, /* I - Attributes to display */
3610 size_t *widths
) /* I - Column widths */
3612 int i
; /* Looping var */
3613 size_t maxlength
; /* Max length of all columns */
3614 ipp_attribute_t
*current
= attr
; /* Current attribute */
3615 char *values
[MAX_DISPLAY
]; /* Strings to display */
3619 * Get the maximum string length we have to show and allocate...
3622 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
3623 if (widths
[i
] > maxlength
)
3624 maxlength
= widths
[i
];
3629 * Loop through the attributes to display...
3634 // Collect the values...
3635 memset(values
, 0, sizeof(values
));
3637 for (; current
; current
= ippNextAttribute(ipp
))
3639 if (!ippGetName(current
))
3642 for (i
= 0; i
< num_displayed
; i
++)
3644 if (!strcmp(ippGetName(current
), displayed
[i
]))
3646 if ((values
[i
] = (char *)calloc(1, maxlength
)) != NULL
)
3647 ippAttributeString(current
, values
[i
], maxlength
);
3653 // Output the line...
3654 for (i
= 0; i
< num_displayed
; i
++)
3657 cupsFilePutChar(data
->outfile
, ' ');
3659 cupsFilePrintf(data
->outfile
, "%*s", (int)-widths
[i
], values
[i
] ? values
[i
] : "");
3662 cupsFilePutChar(data
->outfile
, '\n');
3666 // Show column headings...
3667 char *buffer
= (char *)malloc(maxlength
);
3668 // Buffer for separator lines
3673 for (i
= 0; i
< num_displayed
; i
++)
3676 cupsFilePutChar(data
->outfile
, ' ');
3678 cupsFilePrintf(data
->outfile
, "%*s", (int)-widths
[i
], displayed
[i
]);
3680 cupsFilePutChar(data
->outfile
, '\n');
3682 for (i
= 0; i
< num_displayed
; i
++)
3685 cupsFilePutChar(data
->outfile
, ' ');
3687 memset(buffer
, '-', widths
[i
]);
3688 buffer
[widths
[i
]] = '\0';
3689 cupsFilePuts(data
->outfile
, buffer
);
3691 cupsFilePutChar(data
->outfile
, '\n');
3700 * 'print_xml_header()' - Print a standard XML plist header.
3704 print_xml_header(ipptool_test_t
*data
)/* I - Test data */
3706 if (!data
->xml_header
)
3708 cupsFilePuts(data
->outfile
, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
3709 cupsFilePuts(data
->outfile
, "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n");
3710 cupsFilePuts(data
->outfile
, "<plist version=\"1.0\">\n");
3711 cupsFilePuts(data
->outfile
, "<dict>\n");
3712 cupsFilePuts(data
->outfile
, "<key>ipptoolVersion</key>\n");
3713 cupsFilePuts(data
->outfile
, "<string>" CUPS_SVERSION
"</string>\n");
3714 cupsFilePuts(data
->outfile
, "<key>Transfer</key>\n");
3715 cupsFilePrintf(data
->outfile
, "<string>%s</string>\n", data
->transfer
== IPPTOOL_TRANSFER_AUTO
? "auto" : data
->transfer
== IPPTOOL_TRANSFER_CHUNKED
? "chunked" : "length");
3716 cupsFilePuts(data
->outfile
, "<key>Tests</key>\n");
3717 cupsFilePuts(data
->outfile
, "<array>\n");
3719 data
->xml_header
= 1;
3725 * 'print_xml_string()' - Print an XML string with escaping.
3729 print_xml_string(cups_file_t
*outfile
, /* I - Test data */
3730 const char *element
, /* I - Element name or NULL */
3731 const char *s
) /* I - String to print */
3734 cupsFilePrintf(outfile
, "<%s>", element
);
3739 cupsFilePuts(outfile
, "&");
3741 cupsFilePuts(outfile
, "<");
3743 cupsFilePuts(outfile
, ">");
3744 else if ((*s
& 0xe0) == 0xc0)
3747 * Validate UTF-8 two-byte sequence...
3750 if ((s
[1] & 0xc0) != 0x80)
3752 cupsFilePutChar(outfile
, '?');
3757 cupsFilePutChar(outfile
, *s
++);
3758 cupsFilePutChar(outfile
, *s
);
3761 else if ((*s
& 0xf0) == 0xe0)
3764 * Validate UTF-8 three-byte sequence...
3767 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80)
3769 cupsFilePutChar(outfile
, '?');
3774 cupsFilePutChar(outfile
, *s
++);
3775 cupsFilePutChar(outfile
, *s
++);
3776 cupsFilePutChar(outfile
, *s
);
3779 else if ((*s
& 0xf8) == 0xf0)
3782 * Validate UTF-8 four-byte sequence...
3785 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80 ||
3786 (s
[3] & 0xc0) != 0x80)
3788 cupsFilePutChar(outfile
, '?');
3793 cupsFilePutChar(outfile
, *s
++);
3794 cupsFilePutChar(outfile
, *s
++);
3795 cupsFilePutChar(outfile
, *s
++);
3796 cupsFilePutChar(outfile
, *s
);
3799 else if ((*s
& 0x80) || (*s
< ' ' && !isspace(*s
& 255)))
3802 * Invalid control character...
3805 cupsFilePutChar(outfile
, '?');
3808 cupsFilePutChar(outfile
, *s
);
3814 cupsFilePrintf(outfile
, "</%s>\n", element
);
3819 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
3824 ipptool_test_t
*data
, /* I - Test data */
3825 int success
, /* I - 1 on success, 0 on failure */
3826 const char *message
) /* I - Error message or NULL */
3828 if (data
->xml_header
)
3830 cupsFilePuts(data
->outfile
, "</array>\n");
3831 cupsFilePuts(data
->outfile
, "<key>Successful</key>\n");
3832 cupsFilePuts(data
->outfile
, success
? "<true />\n" : "<false />\n");
3835 cupsFilePuts(data
->outfile
, "<key>ErrorMessage</key>\n");
3836 print_xml_string(data
->outfile
, "string", message
);
3838 cupsFilePuts(data
->outfile
, "</dict>\n");
3839 cupsFilePuts(data
->outfile
, "</plist>\n");
3841 data
->xml_header
= 0;
3848 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
3852 sigterm_handler(int sig
) /* I - Signal number (unused) */
3858 signal(SIGINT
, SIG_DFL
);
3859 signal(SIGTERM
, SIG_DFL
);
3861 #endif /* !_WIN32 */
3865 * 'timeout_cb()' - Handle HTTP timeouts.
3868 static int /* O - 1 to continue, 0 to cancel */
3869 timeout_cb(http_t
*http
, /* I - Connection to server */
3870 void *user_data
) /* I - User data (unused) */
3872 int buffered
= 0; /* Bytes buffered but not yet sent */
3878 * If the socket still have data waiting to be sent to the printer (as can
3879 * happen if the printer runs out of paper), continue to wait until the output
3880 * buffer is empty...
3883 #ifdef SO_NWRITE /* macOS and some versions of Linux */
3884 socklen_t len
= sizeof(buffered
); /* Size of return value */
3886 if (getsockopt(httpGetFd(http
), SOL_SOCKET
, SO_NWRITE
, &buffered
, &len
))
3889 #elif defined(SIOCOUTQ) /* Others except Windows */
3890 if (ioctl(httpGetFd(http
), SIOCOUTQ
, &buffered
))
3893 #else /* Windows (not possible) */
3895 #endif /* SO_NWRITE */
3897 return (buffered
> 0);
3902 * 'token_cb()' - Parse test file-specific tokens and run tests.
3905 static int /* O - 1 to continue, 0 to stop */
3906 token_cb(_ipp_file_t
*f
, /* I - IPP file data */
3907 _ipp_vars_t
*vars
, /* I - IPP variables */
3908 ipptool_test_t
*data
, /* I - Test data */
3909 const char *token
) /* I - Current token */
3911 char name
[1024], /* Name string */
3912 temp
[1024], /* Temporary string */
3913 value
[1024], /* Value string */
3914 *ptr
; /* Pointer into value */
3920 * Initialize state as needed (nothing for now...)
3928 * Parse until we see a close brace...
3931 if (_cups_strcasecmp(token
, "COUNT") &&
3932 _cups_strcasecmp(token
, "DEFINE-MATCH") &&
3933 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
3934 _cups_strcasecmp(token
, "DEFINE-VALUE") &&
3935 _cups_strcasecmp(token
, "IF-DEFINED") &&
3936 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
3937 _cups_strcasecmp(token
, "IN-GROUP") &&
3938 _cups_strcasecmp(token
, "OF-TYPE") &&
3939 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
3940 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
3941 _cups_strcasecmp(token
, "REPEAT-NO-MATCH") &&
3942 _cups_strcasecmp(token
, "SAME-COUNT-AS") &&
3943 _cups_strcasecmp(token
, "WITH-ALL-VALUES") &&
3944 _cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") &&
3945 _cups_strcasecmp(token
, "WITH-ALL-RESOURCES") &&
3946 _cups_strcasecmp(token
, "WITH-ALL-SCHEMES") &&
3947 _cups_strcasecmp(token
, "WITH-DISTINCT-VALUES") &&
3948 _cups_strcasecmp(token
, "WITH-HOSTNAME") &&
3949 _cups_strcasecmp(token
, "WITH-RESOURCE") &&
3950 _cups_strcasecmp(token
, "WITH-SCHEME") &&
3951 _cups_strcasecmp(token
, "WITH-VALUE") &&
3952 _cups_strcasecmp(token
, "WITH-VALUE-FROM"))
3953 data
->last_expect
= NULL
;
3955 if (_cups_strcasecmp(token
, "DEFINE-MATCH") &&
3956 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
3957 _cups_strcasecmp(token
, "IF-DEFINED") &&
3958 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
3959 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
3960 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
3961 _cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
3962 data
->last_status
= NULL
;
3964 if (!strcmp(token
, "}"))
3966 return (do_test(f
, data
));
3968 else if (!strcmp(token
, "MONITOR-PRINTER-STATE"))
3970 if (data
->monitor_uri
)
3972 print_fatal_error(data
, "Extra MONITOR-PRINTER-STATE seen on line %d of \"%s\".", f
->linenum
, f
->filename
);
3976 return (parse_monitor_printer_state(f
, data
));
3978 else if (!strcmp(token
, "COMPRESSION"))
3982 * COMPRESSION deflate
3986 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3988 _ippVarsExpand(vars
, data
->compression
, temp
, sizeof(data
->compression
));
3990 if (strcmp(data
->compression
, "none") && strcmp(data
->compression
, "deflate") &&
3991 strcmp(data
->compression
, "gzip"))
3993 if (strcmp(data
->compression
, "none"))
3994 #endif /* HAVE_LIBZ */
3996 print_fatal_error(data
, "Unsupported COMPRESSION value \"%s\" on line %d of \"%s\".", data
->compression
, f
->linenum
, f
->filename
);
4000 if (!strcmp(data
->compression
, "none"))
4001 data
->compression
[0] = '\0';
4005 print_fatal_error(data
, "Missing COMPRESSION value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4009 else if (!strcmp(token
, "DEFINE"))
4015 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
4017 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
4018 _ippVarsSet(vars
, name
, value
);
4022 print_fatal_error(data
, "Missing DEFINE name and/or value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4026 else if (!strcmp(token
, "IGNORE-ERRORS"))
4033 if (_ippFileReadToken(f
, temp
, sizeof(temp
)) && (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
4035 data
->ignore_errors
= !_cups_strcasecmp(temp
, "yes");
4039 print_fatal_error(data
, "Missing IGNORE-ERRORS value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4043 else if (!_cups_strcasecmp(token
, "NAME"))
4049 _ippFileReadToken(f
, temp
, sizeof(temp
));
4050 _ippVarsExpand(vars
, data
->name
, temp
, sizeof(data
->name
));
4052 else if (!_cups_strcasecmp(token
, "PAUSE"))
4055 * Pause with a message...
4058 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
4060 strlcpy(data
->pause
, temp
, sizeof(data
->pause
));
4064 print_fatal_error(data
, "Missing PAUSE message on line %d of \"%s\".", f
->linenum
, f
->filename
);
4068 else if (!strcmp(token
, "REQUEST-ID"))
4075 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
4077 if (isdigit(temp
[0] & 255))
4079 data
->request_id
= atoi(temp
) - 1;
4081 else if (!_cups_strcasecmp(temp
, "random"))
4083 data
->request_id
= (CUPS_RAND() % 1000) * 137;
4087 print_fatal_error(data
, "Bad REQUEST-ID value \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
4093 print_fatal_error(data
, "Missing REQUEST-ID value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4097 else if (!strcmp(token
, "PASS-IF-DEFINED"))
4100 * PASS-IF-DEFINED variable
4103 if (_ippFileReadToken(f
, name
, sizeof(name
)))
4105 if (_ippVarsGet(vars
, name
))
4106 data
->pass_test
= 1;
4110 print_fatal_error(data
, "Missing PASS-IF-DEFINED value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4114 else if (!strcmp(token
, "PASS-IF-NOT-DEFINED"))
4117 * PASS-IF-NOT-DEFINED variable
4120 if (_ippFileReadToken(f
, name
, sizeof(name
)))
4122 if (!_ippVarsGet(vars
, name
))
4123 data
->pass_test
= 1;
4127 print_fatal_error(data
, "Missing PASS-IF-NOT-DEFINED value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4131 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
4134 * SKIP-IF-DEFINED variable
4137 if (_ippFileReadToken(f
, name
, sizeof(name
)))
4139 if (_ippVarsGet(vars
, name
))
4140 data
->skip_test
= 1;
4144 print_fatal_error(data
, "Missing SKIP-IF-DEFINED value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4148 else if (!strcmp(token
, "SKIP-IF-MISSING"))
4151 * SKIP-IF-MISSING filename
4154 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
4156 char filename
[1024]; /* Filename */
4158 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
4159 get_filename(f
->filename
, filename
, temp
, sizeof(filename
));
4161 if (access(filename
, R_OK
))
4162 data
->skip_test
= 1;
4166 print_fatal_error(data
, "Missing SKIP-IF-MISSING filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
4170 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
4173 * SKIP-IF-NOT-DEFINED variable
4176 if (_ippFileReadToken(f
, name
, sizeof(name
)))
4178 if (!_ippVarsGet(vars
, name
))
4179 data
->skip_test
= 1;
4183 print_fatal_error(data
, "Missing SKIP-IF-NOT-DEFINED value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4187 else if (!strcmp(token
, "SKIP-PREVIOUS-ERROR"))
4190 * SKIP-PREVIOUS-ERROR yes
4191 * SKIP-PREVIOUS-ERROR no
4194 if (_ippFileReadToken(f
, temp
, sizeof(temp
)) && (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
4196 data
->skip_previous
= !_cups_strcasecmp(temp
, "yes");
4200 print_fatal_error(data
, "Missing SKIP-PREVIOUS-ERROR value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4204 else if (!strcmp(token
, "TEST-ID"))
4210 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
4212 _ippVarsExpand(vars
, data
->test_id
, temp
, sizeof(data
->test_id
));
4216 print_fatal_error(data
, "Missing TEST-ID value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4220 else if (!strcmp(token
, "TRANSFER"))
4228 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
4230 if (!strcmp(temp
, "auto"))
4232 data
->transfer
= IPPTOOL_TRANSFER_AUTO
;
4234 else if (!strcmp(temp
, "chunked"))
4236 data
->transfer
= IPPTOOL_TRANSFER_CHUNKED
;
4238 else if (!strcmp(temp
, "length"))
4240 data
->transfer
= IPPTOOL_TRANSFER_LENGTH
;
4244 print_fatal_error(data
, "Bad TRANSFER value \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
4250 print_fatal_error(data
, "Missing TRANSFER value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4254 else if (!_cups_strcasecmp(token
, "VERSION"))
4256 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
4258 if (!strcmp(temp
, "0.0"))
4262 else if (!strcmp(temp
, "1.0"))
4266 else if (!strcmp(temp
, "1.1"))
4270 else if (!strcmp(temp
, "2.0"))
4274 else if (!strcmp(temp
, "2.1"))
4278 else if (!strcmp(temp
, "2.2"))
4284 print_fatal_error(data
, "Bad VERSION \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
4290 print_fatal_error(data
, "Missing VERSION number on line %d of \"%s\".", f
->linenum
, f
->filename
);
4294 else if (!_cups_strcasecmp(token
, "RESOURCE"))
4300 if (!_ippFileReadToken(f
, data
->resource
, sizeof(data
->resource
)))
4302 print_fatal_error(data
, "Missing RESOURCE path on line %d of \"%s\".", f
->linenum
, f
->filename
);
4306 else if (!_cups_strcasecmp(token
, "OPERATION"))
4312 ipp_op_t op
; /* Operation code */
4314 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4316 print_fatal_error(data
, "Missing OPERATION code on line %d of \"%s\".", f
->linenum
, f
->filename
);
4320 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
4322 if ((op
= ippOpValue(value
)) == (ipp_op_t
)-1 && (op
= (ipp_op_t
)strtol(value
, NULL
, 0)) == 0)
4324 print_fatal_error(data
, "Bad OPERATION code \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
4328 ippSetOperation(f
->attrs
, op
);
4330 else if (!_cups_strcasecmp(token
, "GROUP"))
4333 * Attribute group...
4336 ipp_tag_t group_tag
; /* Group tag */
4338 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4340 print_fatal_error(data
, "Missing GROUP tag on line %d of \"%s\".", f
->linenum
, f
->filename
);
4344 if ((group_tag
= ippTagValue(temp
)) == IPP_TAG_ZERO
|| group_tag
>= IPP_TAG_UNSUPPORTED_VALUE
)
4346 print_fatal_error(data
, "Bad GROUP tag \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
4350 if (group_tag
== f
->group_tag
)
4351 ippAddSeparator(f
->attrs
);
4353 f
->group_tag
= group_tag
;
4355 else if (!_cups_strcasecmp(token
, "DELAY"))
4358 * Delay before operation...
4361 double dval
; /* Delay value */
4363 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4365 print_fatal_error(data
, "Missing DELAY value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4369 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
4371 if ((dval
= _cupsStrScand(value
, &ptr
, localeconv())) < 0.0 || (*ptr
&& *ptr
!= ','))
4373 print_fatal_error(data
, "Bad DELAY value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
4377 data
->delay
= (useconds_t
)(1000000.0 * dval
);
4381 if ((dval
= _cupsStrScand(ptr
+ 1, &ptr
, localeconv())) <= 0.0 || *ptr
)
4383 print_fatal_error(data
, "Bad DELAY value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
4387 data
->repeat_interval
= (useconds_t
)(1000000.0 * dval
);
4390 data
->repeat_interval
= data
->delay
;
4392 else if (!_cups_strcasecmp(token
, "FILE"))
4398 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4400 print_fatal_error(data
, "Missing FILE filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
4404 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
4405 get_filename(f
->filename
, data
->file
, value
, sizeof(data
->file
));
4407 if (access(data
->file
, R_OK
))
4409 print_fatal_error(data
, "Filename \"%s\" (mapped to \"%s\") on line %d of \"%s\" cannot be read.", value
, data
->file
, f
->linenum
, f
->filename
);
4413 else if (!_cups_strcasecmp(token
, "STATUS"))
4419 if (data
->num_statuses
>= (int)(sizeof(data
->statuses
) / sizeof(data
->statuses
[0])))
4421 print_fatal_error(data
, "Too many STATUS's on line %d of \"%s\".", f
->linenum
, f
->filename
);
4425 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4427 print_fatal_error(data
, "Missing STATUS code on line %d of \"%s\".", f
->linenum
, f
->filename
);
4431 if ((data
->statuses
[data
->num_statuses
].status
= ippErrorValue(temp
)) == (ipp_status_t
)-1 && (data
->statuses
[data
->num_statuses
].status
= (ipp_status_t
)strtol(temp
, NULL
, 0)) == 0)
4433 print_fatal_error(data
, "Bad STATUS code \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
4437 data
->last_status
= data
->statuses
+ data
->num_statuses
;
4438 data
->num_statuses
++;
4440 data
->last_status
->define_match
= NULL
;
4441 data
->last_status
->define_no_match
= NULL
;
4442 data
->last_status
->if_defined
= NULL
;
4443 data
->last_status
->if_not_defined
= NULL
;
4444 data
->last_status
->repeat_limit
= 1000;
4445 data
->last_status
->repeat_match
= 0;
4446 data
->last_status
->repeat_no_match
= 0;
4448 else if (!_cups_strcasecmp(token
, "EXPECT") || !_cups_strcasecmp(token
, "EXPECT-ALL"))
4451 * Expected attributes...
4454 int expect_all
= !_cups_strcasecmp(token
, "EXPECT-ALL");
4456 if (data
->num_expects
>= (int)(sizeof(data
->expects
) / sizeof(data
->expects
[0])))
4458 print_fatal_error(data
, "Too many EXPECT's on line %d of \"%s\".", f
->linenum
, f
->filename
);
4462 if (!_ippFileReadToken(f
, name
, sizeof(name
)))
4464 print_fatal_error(data
, "Missing EXPECT name on line %d of \"%s\".", f
->linenum
, f
->filename
);
4468 data
->last_expect
= data
->expects
+ data
->num_expects
;
4469 data
->num_expects
++;
4471 memset(data
->last_expect
, 0, sizeof(ipptool_expect_t
));
4472 data
->last_expect
->repeat_limit
= 1000;
4473 data
->last_expect
->expect_all
= expect_all
;
4477 data
->last_expect
->not_expect
= 1;
4478 data
->last_expect
->name
= strdup(name
+ 1);
4480 else if (name
[0] == '?')
4482 data
->last_expect
->optional
= 1;
4483 data
->last_expect
->name
= strdup(name
+ 1);
4486 data
->last_expect
->name
= strdup(name
);
4488 else if (!_cups_strcasecmp(token
, "COUNT"))
4490 int count
; /* Count value */
4492 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4494 print_fatal_error(data
, "Missing COUNT number on line %d of \"%s\".", f
->linenum
, f
->filename
);
4498 if ((count
= atoi(temp
)) <= 0)
4500 print_fatal_error(data
, "Bad COUNT \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
4504 if (data
->last_expect
)
4506 data
->last_expect
->count
= count
;
4510 print_fatal_error(data
, "COUNT without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
4514 else if (!_cups_strcasecmp(token
, "DEFINE-MATCH"))
4516 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4518 print_fatal_error(data
, "Missing DEFINE-MATCH variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
4522 if (data
->last_expect
)
4524 data
->last_expect
->define_match
= strdup(temp
);
4526 else if (data
->last_status
)
4528 data
->last_status
->define_match
= strdup(temp
);
4532 print_fatal_error(data
, "DEFINE-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
4536 else if (!_cups_strcasecmp(token
, "DEFINE-NO-MATCH"))
4538 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4540 print_fatal_error(data
, "Missing DEFINE-NO-MATCH variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
4544 if (data
->last_expect
)
4546 data
->last_expect
->define_no_match
= strdup(temp
);
4548 else if (data
->last_status
)
4550 data
->last_status
->define_no_match
= strdup(temp
);
4554 print_fatal_error(data
, "DEFINE-NO-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
4558 else if (!_cups_strcasecmp(token
, "DEFINE-VALUE"))
4560 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4562 print_fatal_error(data
, "Missing DEFINE-VALUE variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
4566 if (data
->last_expect
)
4568 data
->last_expect
->define_value
= strdup(temp
);
4572 print_fatal_error(data
, "DEFINE-VALUE without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
4576 else if (!_cups_strcasecmp(token
, "OF-TYPE"))
4578 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4580 print_fatal_error(data
, "Missing OF-TYPE value tag(s) on line %d of \"%s\".", f
->linenum
, f
->filename
);
4584 if (data
->last_expect
)
4586 data
->last_expect
->of_type
= strdup(temp
);
4590 print_fatal_error(data
, "OF-TYPE without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
4594 else if (!_cups_strcasecmp(token
, "IN-GROUP"))
4596 ipp_tag_t in_group
; /* IN-GROUP value */
4598 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4600 print_fatal_error(data
, "Missing IN-GROUP group tag on line %d of \"%s\".", f
->linenum
, f
->filename
);
4604 if ((in_group
= ippTagValue(temp
)) == IPP_TAG_ZERO
|| in_group
>= IPP_TAG_UNSUPPORTED_VALUE
)
4606 print_fatal_error(data
, "Bad IN-GROUP group tag \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
4609 else if (data
->last_expect
)
4611 data
->last_expect
->in_group
= in_group
;
4615 print_fatal_error(data
, "IN-GROUP without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
4619 else if (!_cups_strcasecmp(token
, "REPEAT-LIMIT"))
4621 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4623 print_fatal_error(data
, "Missing REPEAT-LIMIT value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4626 else if (atoi(temp
) <= 0)
4628 print_fatal_error(data
, "Bad REPEAT-LIMIT value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4632 if (data
->last_status
)
4634 data
->last_status
->repeat_limit
= atoi(temp
);
4636 else if (data
->last_expect
)
4638 data
->last_expect
->repeat_limit
= atoi(temp
);
4642 print_fatal_error(data
, "REPEAT-LIMIT without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
4646 else if (!_cups_strcasecmp(token
, "REPEAT-MATCH"))
4648 if (data
->last_status
)
4650 data
->last_status
->repeat_match
= 1;
4652 else if (data
->last_expect
)
4654 data
->last_expect
->repeat_match
= 1;
4658 print_fatal_error(data
, "REPEAT-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
4662 else if (!_cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
4664 if (data
->last_status
)
4666 data
->last_status
->repeat_no_match
= 1;
4668 else if (data
->last_expect
)
4670 data
->last_expect
->repeat_no_match
= 1;
4674 print_fatal_error(data
, "REPEAT-NO-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
4678 else if (!_cups_strcasecmp(token
, "SAME-COUNT-AS"))
4680 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4682 print_fatal_error(data
, "Missing SAME-COUNT-AS name on line %d of \"%s\".", f
->linenum
, f
->filename
);
4686 if (data
->last_expect
)
4688 data
->last_expect
->same_count_as
= strdup(temp
);
4692 print_fatal_error(data
, "SAME-COUNT-AS without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
4696 else if (!_cups_strcasecmp(token
, "IF-DEFINED"))
4698 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4700 print_fatal_error(data
, "Missing IF-DEFINED name on line %d of \"%s\".", f
->linenum
, f
->filename
);
4704 if (data
->last_expect
)
4706 data
->last_expect
->if_defined
= strdup(temp
);
4708 else if (data
->last_status
)
4710 data
->last_status
->if_defined
= strdup(temp
);
4714 print_fatal_error(data
, "IF-DEFINED without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
4718 else if (!_cups_strcasecmp(token
, "IF-NOT-DEFINED"))
4720 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4722 print_fatal_error(data
, "Missing IF-NOT-DEFINED name on line %d of \"%s\".", f
->linenum
, f
->filename
);
4726 if (data
->last_expect
)
4728 data
->last_expect
->if_not_defined
= strdup(temp
);
4730 else if (data
->last_status
)
4732 data
->last_status
->if_not_defined
= strdup(temp
);
4736 print_fatal_error(data
, "IF-NOT-DEFINED without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
4740 else if (!_cups_strcasecmp(token
, "WITH-DISTINCT-VALUES"))
4742 if (data
->last_expect
)
4744 data
->last_expect
->with_distinct
= 1;
4748 print_fatal_error(data
, "%s without a preceding EXPECT on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
4752 else if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") ||
4753 !_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
4754 !_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
4755 !_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
4756 !_cups_strcasecmp(token
, "WITH-HOSTNAME") ||
4757 !_cups_strcasecmp(token
, "WITH-RESOURCE") ||
4758 !_cups_strcasecmp(token
, "WITH-SCHEME") ||
4759 !_cups_strcasecmp(token
, "WITH-VALUE"))
4761 off_t lastpos
; /* Last file position */
4762 int lastline
; /* Last line number */
4764 if (data
->last_expect
)
4766 if (!_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") || !_cups_strcasecmp(token
, "WITH-HOSTNAME"))
4767 data
->last_expect
->with_flags
= IPPTOOL_WITH_HOSTNAME
;
4768 else if (!_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") || !_cups_strcasecmp(token
, "WITH-RESOURCE"))
4769 data
->last_expect
->with_flags
= IPPTOOL_WITH_RESOURCE
;
4770 else if (!_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") || !_cups_strcasecmp(token
, "WITH-SCHEME"))
4771 data
->last_expect
->with_flags
= IPPTOOL_WITH_SCHEME
;
4773 if (!_cups_strncasecmp(token
, "WITH-ALL-", 9))
4774 data
->last_expect
->with_flags
|= IPPTOOL_WITH_ALL
;
4777 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4779 print_fatal_error(data
, "Missing %s value on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
4784 * Read additional comma-delimited values - needed since legacy test files
4785 * will have unquoted WITH-VALUE values with commas...
4788 ptr
= temp
+ strlen(temp
);
4792 lastpos
= cupsFileTell(f
->fp
);
4793 lastline
= f
->linenum
;
4796 if (!_ippFileReadToken(f
, ptr
, (sizeof(temp
) - (size_t)(ptr
- temp
))))
4799 if (!strcmp(ptr
, ","))
4807 if (!_ippFileReadToken(f
, ptr
, (sizeof(temp
) - (size_t)(ptr
- temp
))))
4813 * Not another value, stop here...
4816 cupsFileSeek(f
->fp
, lastpos
);
4817 f
->linenum
= lastline
;
4823 if (data
->last_expect
)
4826 * Expand any variables in the value and then save it.
4829 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
4831 ptr
= value
+ strlen(value
) - 1;
4833 if (value
[0] == '/' && ptr
> value
&& *ptr
== '/')
4836 * WITH-VALUE is a POSIX extended regular expression.
4839 data
->last_expect
->with_value
= calloc(1, (size_t)(ptr
- value
));
4840 data
->last_expect
->with_flags
|= IPPTOOL_WITH_REGEX
;
4842 if (data
->last_expect
->with_value
)
4843 memcpy(data
->last_expect
->with_value
, value
+ 1, (size_t)(ptr
- value
- 1));
4848 * WITH-VALUE is a literal value...
4851 for (ptr
= value
; *ptr
; ptr
++)
4853 if (*ptr
== '\\' && ptr
[1])
4856 * Remove \ from \foo...
4859 _cups_strcpy(ptr
, ptr
+ 1);
4863 data
->last_expect
->with_value
= strdup(value
);
4864 data
->last_expect
->with_flags
|= IPPTOOL_WITH_LITERAL
;
4869 print_fatal_error(data
, "%s without a preceding EXPECT on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
4873 else if (!_cups_strcasecmp(token
, "WITH-VALUE-FROM"))
4875 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4877 print_fatal_error(data
, "Missing %s value on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
4881 if (data
->last_expect
)
4884 * Expand any variables in the value and then save it.
4887 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
4889 data
->last_expect
->with_value_from
= strdup(value
);
4890 data
->last_expect
->with_flags
= IPPTOOL_WITH_LITERAL
;
4894 print_fatal_error(data
, "%s without a preceding EXPECT on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
4898 else if (!_cups_strcasecmp(token
, "DISPLAY"))
4901 * Display attributes...
4904 if (data
->num_displayed
>= (int)(sizeof(data
->displayed
) / sizeof(data
->displayed
[0])))
4906 print_fatal_error(data
, "Too many DISPLAY's on line %d of \"%s\".", f
->linenum
, f
->filename
);
4910 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4912 print_fatal_error(data
, "Missing DISPLAY name on line %d of \"%s\".", f
->linenum
, f
->filename
);
4916 data
->displayed
[data
->num_displayed
] = strdup(temp
);
4917 data
->num_displayed
++;
4921 print_fatal_error(data
, "Unexpected token %s seen on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
4928 * Scan for the start of a test (open brace)...
4931 if (!strcmp(token
, "{"))
4937 if (data
->show_header
)
4939 if (data
->output
== IPPTOOL_OUTPUT_PLIST
)
4940 print_xml_header(data
);
4942 if (data
->output
== IPPTOOL_OUTPUT_TEST
|| (data
->output
== IPPTOOL_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
4943 cupsFilePrintf(cupsFileStdout(), "\"%s\":\n", f
->filename
);
4945 data
->show_header
= 0;
4948 data
->compression
[0] = '\0';
4950 data
->num_expects
= 0;
4951 data
->last_expect
= NULL
;
4952 data
->file
[0] = '\0';
4953 data
->ignore_errors
= data
->def_ignore_errors
;
4954 strlcpy(data
->name
, f
->filename
, sizeof(data
->name
));
4955 if ((ptr
= strrchr(data
->name
, '.')) != NULL
)
4957 data
->repeat_interval
= 5000000;
4958 strlcpy(data
->resource
, data
->vars
->resource
, sizeof(data
->resource
));
4959 data
->skip_previous
= 0;
4960 data
->pass_test
= 0;
4961 data
->skip_test
= 0;
4962 data
->num_statuses
= 0;
4963 data
->last_status
= NULL
;
4964 data
->test_id
[0] = '\0';
4965 data
->transfer
= data
->def_transfer
;
4966 data
->version
= data
->def_version
;
4968 free(data
->monitor_uri
);
4969 data
->monitor_uri
= NULL
;
4970 data
->monitor_delay
= 0;
4971 data
->monitor_interval
= 5000000;
4972 data
->num_monitor_expects
= 0;
4974 _ippVarsSet(vars
, "date-current", iso_date(ippTimeToDate(time(NULL
))));
4976 f
->attrs
= ippNew();
4977 f
->group_tag
= IPP_TAG_ZERO
;
4979 else if (!strcmp(token
, "DEFINE"))
4985 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
4987 _ippVarsSet(vars
, "date-current", iso_date(ippTimeToDate(time(NULL
))));
4988 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
4989 _ippVarsSet(vars
, name
, value
);
4993 print_fatal_error(data
, "Missing DEFINE name and/or value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4997 else if (!strcmp(token
, "DEFINE-DEFAULT"))
5000 * DEFINE-DEFAULT name value
5003 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
5005 if (!_ippVarsGet(vars
, name
))
5007 _ippVarsSet(vars
, "date-current", iso_date(ippTimeToDate(time(NULL
))));
5008 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
5009 _ippVarsSet(vars
, name
, value
);
5014 print_fatal_error(data
, "Missing DEFINE-DEFAULT name and/or value on line %d of \"%s\".", f
->linenum
, f
->filename
);
5018 else if (!strcmp(token
, "FILE-ID"))
5024 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
5026 _ippVarsSet(vars
, "date-current", iso_date(ippTimeToDate(time(NULL
))));
5027 _ippVarsExpand(vars
, data
->file_id
, temp
, sizeof(data
->file_id
));
5031 print_fatal_error(data
, "Missing FILE-ID value on line %d of \"%s\".", f
->linenum
, f
->filename
);
5035 else if (!strcmp(token
, "IGNORE-ERRORS"))
5042 if (_ippFileReadToken(f
, temp
, sizeof(temp
)) && (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
5044 data
->def_ignore_errors
= !_cups_strcasecmp(temp
, "yes");
5048 print_fatal_error(data
, "Missing IGNORE-ERRORS value on line %d of \"%s\".", f
->linenum
, f
->filename
);
5052 else if (!strcmp(token
, "INCLUDE"))
5055 * INCLUDE "filename"
5056 * INCLUDE <filename>
5059 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
5062 * Map the filename to and then run the tests...
5065 ipptool_test_t inc_data
; /* Data for included file */
5066 char filename
[1024]; /* Mapped filename */
5068 memcpy(&inc_data
, data
, sizeof(inc_data
));
5069 inc_data
.http
= NULL
;
5071 inc_data
.prev_pass
= 1;
5072 inc_data
.show_header
= 1;
5074 if (!do_tests(get_filename(f
->filename
, filename
, temp
, sizeof(filename
)), &inc_data
) && data
->stop_after_include_error
)
5076 data
->pass
= data
->prev_pass
= 0;
5082 print_fatal_error(data
, "Missing INCLUDE filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
5086 data
->show_header
= 1;
5088 else if (!strcmp(token
, "INCLUDE-IF-DEFINED"))
5091 * INCLUDE-IF-DEFINED name "filename"
5092 * INCLUDE-IF-DEFINED name <filename>
5095 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
5098 * Map the filename to and then run the tests...
5101 ipptool_test_t inc_data
; /* Data for included file */
5102 char filename
[1024]; /* Mapped filename */
5104 memcpy(&inc_data
, data
, sizeof(inc_data
));
5105 inc_data
.http
= NULL
;
5107 inc_data
.prev_pass
= 1;
5108 inc_data
.show_header
= 1;
5110 if (!do_tests(get_filename(f
->filename
, filename
, temp
, sizeof(filename
)), &inc_data
) && data
->stop_after_include_error
)
5112 data
->pass
= data
->prev_pass
= 0;
5118 print_fatal_error(data
, "Missing INCLUDE-IF-DEFINED name or filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
5122 data
->show_header
= 1;
5124 else if (!strcmp(token
, "INCLUDE-IF-NOT-DEFINED"))
5127 * INCLUDE-IF-NOT-DEFINED name "filename"
5128 * INCLUDE-IF-NOT-DEFINED name <filename>
5131 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
5134 * Map the filename to and then run the tests...
5137 ipptool_test_t inc_data
; /* Data for included file */
5138 char filename
[1024]; /* Mapped filename */
5140 memcpy(&inc_data
, data
, sizeof(inc_data
));
5141 inc_data
.http
= NULL
;
5143 inc_data
.prev_pass
= 1;
5144 inc_data
.show_header
= 1;
5146 if (!do_tests(get_filename(f
->filename
, filename
, temp
, sizeof(filename
)), &inc_data
) && data
->stop_after_include_error
)
5148 data
->pass
= data
->prev_pass
= 0;
5154 print_fatal_error(data
, "Missing INCLUDE-IF-NOT-DEFINED name or filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
5158 data
->show_header
= 1;
5160 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
5163 * SKIP-IF-DEFINED variable
5166 if (_ippFileReadToken(f
, name
, sizeof(name
)))
5168 if (_ippVarsGet(vars
, name
))
5169 data
->skip_test
= 1;
5173 print_fatal_error(data
, "Missing SKIP-IF-DEFINED variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
5177 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
5180 * SKIP-IF-NOT-DEFINED variable
5183 if (_ippFileReadToken(f
, name
, sizeof(name
)))
5185 if (!_ippVarsGet(vars
, name
))
5186 data
->skip_test
= 1;
5190 print_fatal_error(data
, "Missing SKIP-IF-NOT-DEFINED variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
5194 else if (!strcmp(token
, "STOP-AFTER-INCLUDE-ERROR"))
5197 * STOP-AFTER-INCLUDE-ERROR yes
5198 * STOP-AFTER-INCLUDE-ERROR no
5201 if (_ippFileReadToken(f
, temp
, sizeof(temp
)) && (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
5203 data
->stop_after_include_error
= !_cups_strcasecmp(temp
, "yes");
5207 print_fatal_error(data
, "Missing STOP-AFTER-INCLUDE-ERROR value on line %d of \"%s\".", f
->linenum
, f
->filename
);
5211 else if (!strcmp(token
, "TRANSFER"))
5219 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
5221 if (!strcmp(temp
, "auto"))
5222 data
->def_transfer
= IPPTOOL_TRANSFER_AUTO
;
5223 else if (!strcmp(temp
, "chunked"))
5224 data
->def_transfer
= IPPTOOL_TRANSFER_CHUNKED
;
5225 else if (!strcmp(temp
, "length"))
5226 data
->def_transfer
= IPPTOOL_TRANSFER_LENGTH
;
5229 print_fatal_error(data
, "Bad TRANSFER value \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
5235 print_fatal_error(data
, "Missing TRANSFER value on line %d of \"%s\".", f
->linenum
, f
->filename
);
5239 else if (!strcmp(token
, "VERSION"))
5241 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
5243 if (!strcmp(temp
, "1.0"))
5244 data
->def_version
= 10;
5245 else if (!strcmp(temp
, "1.1"))
5246 data
->def_version
= 11;
5247 else if (!strcmp(temp
, "2.0"))
5248 data
->def_version
= 20;
5249 else if (!strcmp(temp
, "2.1"))
5250 data
->def_version
= 21;
5251 else if (!strcmp(temp
, "2.2"))
5252 data
->def_version
= 22;
5255 print_fatal_error(data
, "Bad VERSION \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
5261 print_fatal_error(data
, "Missing VERSION number on line %d of \"%s\".", f
->linenum
, f
->filename
);
5267 print_fatal_error(data
, "Unexpected token %s seen on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
5277 * 'usage()' - Show program usage.
5283 _cupsLangPuts(stderr
, _("Usage: ipptool [options] URI filename [ ... filenameN ]"));
5284 _cupsLangPuts(stderr
, _("Options:"));
5285 _cupsLangPuts(stderr
, _("--ippserver filename Produce ippserver attribute file"));
5286 _cupsLangPuts(stderr
, _("--stop-after-include-error\n"
5287 " Stop tests after a failed INCLUDE"));
5288 _cupsLangPuts(stderr
, _("--version Show version"));
5289 _cupsLangPuts(stderr
, _("-4 Connect using IPv4"));
5290 _cupsLangPuts(stderr
, _("-6 Connect using IPv6"));
5291 _cupsLangPuts(stderr
, _("-C Send requests using chunking (default)"));
5292 _cupsLangPuts(stderr
, _("-E Test with encryption using HTTP Upgrade to TLS"));
5293 _cupsLangPuts(stderr
, _("-I Ignore errors"));
5294 _cupsLangPuts(stderr
, _("-L Send requests using content-length"));
5295 _cupsLangPuts(stderr
, _("-P filename.plist Produce XML plist to a file and test report to standard output"));
5296 _cupsLangPuts(stderr
, _("-R Repeat tests on server-error-busy"));
5297 _cupsLangPuts(stderr
, _("-S Test with encryption using HTTPS"));
5298 _cupsLangPuts(stderr
, _("-T seconds Set the receive/send timeout in seconds"));
5299 _cupsLangPuts(stderr
, _("-V version Set default IPP version"));
5300 _cupsLangPuts(stderr
, _("-X Produce XML plist instead of plain text"));
5301 _cupsLangPuts(stderr
, _("-c Produce CSV output"));
5302 _cupsLangPuts(stderr
, _("-d name=value Set named variable to value"));
5303 _cupsLangPuts(stderr
, _("-f filename Set default request filename"));
5304 _cupsLangPuts(stderr
, _("-h Validate HTTP response headers"));
5305 _cupsLangPuts(stderr
, _("-i seconds Repeat the last file with the given time interval"));
5306 _cupsLangPuts(stderr
, _("-l Produce plain text output"));
5307 _cupsLangPuts(stderr
, _("-n count Repeat the last file the given number of times"));
5308 _cupsLangPuts(stderr
, _("-q Run silently"));
5309 _cupsLangPuts(stderr
, _("-t Produce a test report"));
5310 _cupsLangPuts(stderr
, _("-v Be verbose"));
5317 * 'with_distinct_values()' - Verify that an attribute contains unique values.
5320 static int // O - 1 if distinct, 0 if duplicate
5321 with_distinct_values(
5322 cups_array_t
*errors
, // I - Array of errors
5323 ipp_attribute_t
*attr
) // I - Attribute to test
5325 int i
, // Looping var
5326 count
; // Number of values
5327 ipp_tag_t value_tag
; // Value syntax
5328 const char *value
; // Current value
5329 char buffer
[8192]; // Temporary buffer
5330 cups_array_t
*values
; // Array of values as strings
5333 // If there is only 1 value, it must be distinct
5334 if ((count
= ippGetCount(attr
)) == 1)
5337 // Only check integers, enums, rangeOfInteger, resolution, and nul-terminated
5339 switch (value_tag
= ippGetValueTag(attr
))
5341 case IPP_TAG_INTEGER
:
5343 case IPP_TAG_RANGE
:
5344 case IPP_TAG_RESOLUTION
:
5345 case IPP_TAG_KEYWORD
:
5346 case IPP_TAG_URISCHEME
:
5347 case IPP_TAG_CHARSET
:
5348 case IPP_TAG_LANGUAGE
:
5349 case IPP_TAG_MIMETYPE
:
5350 case IPP_TAG_BEGIN_COLLECTION
:
5354 add_stringf(errors
, "WITH-DISTINCT-VALUES %s not supported for 1setOf %s", ippGetName(attr
), ippTagString(value_tag
));
5358 // Collect values and determine they are all unique...
5359 values
= cupsArrayNew3((cups_array_func_t
)strcmp
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
, (cups_afree_func_t
)free
);
5361 for (i
= 0; i
< count
; i
++)
5365 case IPP_TAG_INTEGER
:
5367 snprintf(buffer
, sizeof(buffer
), "%d", ippGetInteger(attr
, i
));
5370 case IPP_TAG_RANGE
:
5372 int upper
, lower
= ippGetRange(attr
, i
, &upper
);
5375 snprintf(buffer
, sizeof(buffer
), "%d-%d", lower
, upper
);
5379 case IPP_TAG_RESOLUTION
:
5381 ipp_res_t units
; // Resolution units
5382 int yres
, xres
= ippGetResolution(attr
, i
, &yres
, &units
);
5383 // Resolution values
5386 snprintf(buffer
, sizeof(buffer
), "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5388 snprintf(buffer
, sizeof(buffer
), "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5392 case IPP_TAG_KEYWORD
:
5393 case IPP_TAG_URISCHEME
:
5394 case IPP_TAG_CHARSET
:
5395 case IPP_TAG_LANGUAGE
:
5396 case IPP_TAG_MIMETYPE
:
5397 value
= ippGetString(attr
, i
, NULL
);
5399 case IPP_TAG_BEGIN_COLLECTION
:
5401 ipp_t
*col
= ippGetCollection(attr
, i
);
5403 ipp_attribute_t
*member
; // Member attribute
5404 char *bufptr
, // Pointer into buffer
5405 *bufend
, // End of buffer
5406 prefix
; // Prefix character
5408 for (prefix
= '{', bufptr
= buffer
, bufend
= buffer
+ sizeof(buffer
) - 2, member
= ippFirstAttribute(col
); member
&& bufptr
< bufend
; member
= ippNextAttribute(col
))
5413 ippAttributeString(member
, bufptr
, (size_t)(bufend
- bufptr
));
5414 bufptr
+= strlen(bufptr
);
5422 default : // Should never happen
5423 value
= "unsupported";
5427 if (cupsArrayFind(values
, (void *)value
))
5428 add_stringf(errors
, "DUPLICATE: %s=%s", ippGetName(attr
), value
);
5430 cupsArrayAdd(values
, (void *)value
);
5434 i
= cupsArrayCount(values
) == count
;
5435 cupsArrayDelete(values
);
5442 * 'with_flags_string()' - Return the "WITH-xxx" predicate that corresponds to
5446 static const char * /* O - WITH-xxx string */
5447 with_flags_string(int flags
) /* I - WITH flags */
5449 if (flags
& IPPTOOL_WITH_ALL
)
5451 if (flags
& IPPTOOL_WITH_HOSTNAME
)
5452 return ("WITH-ALL-HOSTNAMES");
5453 else if (flags
& IPPTOOL_WITH_RESOURCE
)
5454 return ("WITH-ALL-RESOURCES");
5455 else if (flags
& IPPTOOL_WITH_SCHEME
)
5456 return ("WITH-ALL-SCHEMES");
5458 return ("WITH-ALL-VALUES");
5460 else if (flags
& IPPTOOL_WITH_HOSTNAME
)
5461 return ("WITH-HOSTNAME");
5462 else if (flags
& IPPTOOL_WITH_RESOURCE
)
5463 return ("WITH-RESOURCE");
5464 else if (flags
& IPPTOOL_WITH_SCHEME
)
5465 return ("WITH-SCHEME");
5467 return ("WITH-VALUE");
5472 * 'with_value()' - Test a WITH-VALUE predicate.
5475 static int /* O - 1 on match, 0 on non-match */
5476 with_value(ipptool_test_t
*data
, /* I - Test data */
5477 cups_array_t
*errors
, /* I - Errors array */
5478 char *value
, /* I - Value string */
5479 int flags
, /* I - Flags for match */
5480 ipp_attribute_t
*attr
, /* I - Attribute to compare */
5481 char *matchbuf
, /* I - Buffer to hold matching value */
5482 size_t matchlen
) /* I - Length of match buffer */
5484 int i
, /* Looping var */
5485 count
, /* Number of values */
5487 char temp
[1024], /* Temporary value string */
5488 *valptr
; /* Pointer into value */
5489 const char *name
; /* Attribute name */
5493 match
= (flags
& IPPTOOL_WITH_ALL
) ? 1 : 0;
5496 * NULL matches everything.
5499 if (!value
|| !*value
)
5503 * Compare the value string to the attribute value.
5506 name
= ippGetName(attr
);
5507 count
= ippGetCount(attr
);
5509 switch (ippGetValueTag(attr
))
5511 case IPP_TAG_INTEGER
:
5513 for (i
= 0; i
< count
; i
++)
5515 char op
, /* Comparison operator */
5516 *nextptr
; /* Next pointer */
5517 int intvalue
, /* Integer value */
5518 attrvalue
= ippGetInteger(attr
, i
),
5519 /* Attribute value */
5520 valmatch
= 0; /* Does the current value match? */
5524 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5525 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5526 *valptr
== '=' || *valptr
== '>')
5529 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5531 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5539 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
5540 if (nextptr
== valptr
)
5544 if ((op
== '=' && attrvalue
== intvalue
) ||
5545 (op
== '<' && attrvalue
< intvalue
) ||
5546 (op
== '>' && attrvalue
> intvalue
))
5549 snprintf(matchbuf
, matchlen
, "%d", attrvalue
);
5556 if (flags
& IPPTOOL_WITH_ALL
)
5571 if (!match
&& errors
)
5573 for (i
= 0; i
< count
; i
++)
5574 add_stringf(data
->errors
, "GOT: %s=%d", name
, ippGetInteger(attr
, i
));
5578 case IPP_TAG_RANGE
:
5579 for (i
= 0; i
< count
; i
++)
5581 char op
, /* Comparison operator */
5582 *nextptr
; /* Next pointer */
5583 int intvalue
, /* Integer value */
5584 lower
, /* Lower range */
5585 upper
, /* Upper range */
5586 valmatch
= 0; /* Does the current value match? */
5588 lower
= ippGetRange(attr
, i
, &upper
);
5591 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5592 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5593 *valptr
== '=' || *valptr
== '>')
5596 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5598 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5606 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
5607 if (nextptr
== valptr
)
5611 if ((op
== '=' && (lower
== intvalue
|| upper
== intvalue
)) ||
5612 (op
== '<' && upper
< intvalue
) ||
5613 (op
== '>' && upper
> intvalue
))
5616 snprintf(matchbuf
, matchlen
, "%d-%d", lower
, upper
);
5623 if (flags
& IPPTOOL_WITH_ALL
)
5638 if (!match
&& errors
)
5640 for (i
= 0; i
< count
; i
++)
5642 int lower
, upper
; /* Range values */
5644 lower
= ippGetRange(attr
, i
, &upper
);
5645 add_stringf(data
->errors
, "GOT: %s=%d-%d", name
, lower
, upper
);
5650 case IPP_TAG_BOOLEAN
:
5651 for (i
= 0; i
< count
; i
++)
5653 if ((!strcmp(value
, "true") || !strcmp(value
, "1")) == ippGetBoolean(attr
, i
))
5656 strlcpy(matchbuf
, value
, matchlen
);
5658 if (!(flags
& IPPTOOL_WITH_ALL
))
5664 else if (flags
& IPPTOOL_WITH_ALL
)
5671 if (!match
&& errors
)
5673 for (i
= 0; i
< count
; i
++)
5674 add_stringf(data
->errors
, "GOT: %s=%s", name
, ippGetBoolean(attr
, i
) ? "true" : "false");
5678 case IPP_TAG_RESOLUTION
:
5679 for (i
= 0; i
< count
; i
++)
5681 int xres
, yres
; /* Resolution values */
5682 ipp_res_t units
; /* Resolution units */
5684 xres
= ippGetResolution(attr
, i
, &yres
, &units
);
5686 snprintf(temp
, sizeof(temp
), "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5688 snprintf(temp
, sizeof(temp
), "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5690 if (!strcmp(value
, temp
))
5693 strlcpy(matchbuf
, value
, matchlen
);
5695 if (!(flags
& IPPTOOL_WITH_ALL
))
5701 else if (flags
& IPPTOOL_WITH_ALL
)
5708 if (!match
&& errors
)
5710 for (i
= 0; i
< count
; i
++)
5712 int xres
, yres
; /* Resolution values */
5713 ipp_res_t units
; /* Resolution units */
5715 xres
= ippGetResolution(attr
, i
, &yres
, &units
);
5717 snprintf(temp
, sizeof(temp
), "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5719 snprintf(temp
, sizeof(temp
), "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5721 if (strcmp(value
, temp
))
5722 add_stringf(data
->errors
, "GOT: %s=%s", name
, temp
);
5727 case IPP_TAG_NOVALUE
:
5728 case IPP_TAG_UNKNOWN
:
5731 case IPP_TAG_CHARSET
:
5732 case IPP_TAG_KEYWORD
:
5733 case IPP_TAG_LANGUAGE
:
5734 case IPP_TAG_MIMETYPE
:
5736 case IPP_TAG_NAMELANG
:
5738 case IPP_TAG_TEXTLANG
:
5740 case IPP_TAG_URISCHEME
:
5741 if (flags
& IPPTOOL_WITH_REGEX
)
5744 * Value is an extended, case-sensitive POSIX regular expression...
5747 regex_t re
; /* Regular expression */
5749 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
5751 regerror(i
, &re
, temp
, sizeof(temp
));
5753 print_fatal_error(data
, "Unable to compile WITH-VALUE regular expression \"%s\" - %s", value
, temp
);
5758 * See if ALL of the values match the given regular expression.
5761 for (i
= 0; i
< count
; i
++)
5763 if (!regexec(&re
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5767 strlcpy(matchbuf
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)), matchlen
);
5769 if (!(flags
& IPPTOOL_WITH_ALL
))
5775 else if (flags
& IPPTOOL_WITH_ALL
)
5784 else if (ippGetValueTag(attr
) == IPP_TAG_URI
&& !(flags
& (IPPTOOL_WITH_SCHEME
| IPPTOOL_WITH_HOSTNAME
| IPPTOOL_WITH_RESOURCE
)))
5787 * Value is a literal URI string, see if the value(s) match...
5790 for (i
= 0; i
< count
; i
++)
5792 if (!compare_uris(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
))))
5795 strlcpy(matchbuf
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)), matchlen
);
5797 if (!(flags
& IPPTOOL_WITH_ALL
))
5803 else if (flags
& IPPTOOL_WITH_ALL
)
5813 * Value is a literal string, see if the value(s) match...
5816 for (i
= 0; i
< count
; i
++)
5820 switch (ippGetValueTag(attr
))
5824 * Some URI components are case-sensitive, some not...
5827 if (flags
& (IPPTOOL_WITH_SCHEME
| IPPTOOL_WITH_HOSTNAME
))
5828 result
= _cups_strcasecmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
5830 result
= strcmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
5833 case IPP_TAG_MIMETYPE
:
5835 case IPP_TAG_NAMELANG
:
5837 case IPP_TAG_TEXTLANG
:
5839 * mimeMediaType, nameWithoutLanguage, nameWithLanguage,
5840 * textWithoutLanguage, and textWithLanguage are defined to
5841 * be case-insensitive strings...
5844 result
= _cups_strcasecmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
5849 * Other string syntaxes are defined as lowercased so we use
5850 * case-sensitive comparisons to catch problems...
5853 result
= strcmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
5860 strlcpy(matchbuf
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)), matchlen
);
5862 if (!(flags
& IPPTOOL_WITH_ALL
))
5868 else if (flags
& IPPTOOL_WITH_ALL
)
5876 if (!match
&& errors
)
5878 for (i
= 0; i
< count
; i
++)
5879 add_stringf(data
->errors
, "GOT: %s=\"%s\"", name
, ippGetString(attr
, i
, NULL
));
5883 case IPP_TAG_STRING
:
5884 if (flags
& IPPTOOL_WITH_REGEX
)
5887 * Value is an extended, case-sensitive POSIX regular expression...
5890 void *adata
; /* Pointer to octetString data */
5891 int adatalen
; /* Length of octetString */
5892 regex_t re
; /* Regular expression */
5894 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
5896 regerror(i
, &re
, temp
, sizeof(temp
));
5898 print_fatal_error(data
, "Unable to compile WITH-VALUE regular expression \"%s\" - %s", value
, temp
);
5903 * See if ALL of the values match the given regular expression.
5906 for (i
= 0; i
< count
; i
++)
5908 if ((adata
= ippGetOctetString(attr
, i
, &adatalen
)) == NULL
|| adatalen
>= (int)sizeof(temp
))
5913 memcpy(temp
, adata
, (size_t)adatalen
);
5914 temp
[adatalen
] = '\0';
5916 if (!regexec(&re
, temp
, 0, NULL
, 0))
5919 strlcpy(matchbuf
, temp
, matchlen
);
5921 if (!(flags
& IPPTOOL_WITH_ALL
))
5927 else if (flags
& IPPTOOL_WITH_ALL
)
5936 if (!match
&& errors
)
5938 for (i
= 0; i
< count
; i
++)
5940 adata
= ippGetOctetString(attr
, i
, &adatalen
);
5941 copy_hex_string(temp
, adata
, adatalen
, sizeof(temp
));
5942 add_stringf(data
->errors
, "GOT: %s=\"%s\"", name
, temp
);
5949 * Value is a literal or hex-encoded string...
5952 unsigned char withdata
[1023], /* WITH-VALUE data */
5953 *adata
; /* Pointer to octetString data */
5954 int withlen
, /* Length of WITH-VALUE data */
5955 adatalen
; /* Length of octetString */
5960 * Grab hex-encoded value...
5963 if ((withlen
= (int)strlen(value
)) & 1 || withlen
> (int)(2 * (sizeof(withdata
) + 1)))
5965 print_fatal_error(data
, "Bad WITH-VALUE hex value.");
5969 withlen
= withlen
/ 2 - 1;
5971 for (valptr
= value
+ 1, adata
= withdata
; *valptr
; valptr
+= 2)
5973 int ch
; /* Current character/byte */
5975 if (isdigit(valptr
[0]))
5976 ch
= (valptr
[0] - '0') << 4;
5977 else if (isalpha(valptr
[0]))
5978 ch
= (tolower(valptr
[0]) - 'a' + 10) << 4;
5982 if (isdigit(valptr
[1]))
5983 ch
|= valptr
[1] - '0';
5984 else if (isalpha(valptr
[1]))
5985 ch
|= tolower(valptr
[1]) - 'a' + 10;
5989 *adata
++ = (unsigned char)ch
;
5994 print_fatal_error(data
, "Bad WITH-VALUE hex value.");
6001 * Copy literal string value...
6004 withlen
= (int)strlen(value
);
6006 memcpy(withdata
, value
, (size_t)withlen
);
6009 for (i
= 0; i
< count
; i
++)
6011 adata
= ippGetOctetString(attr
, i
, &adatalen
);
6013 if (withlen
== adatalen
&& !memcmp(withdata
, adata
, (size_t)withlen
))
6016 copy_hex_string(matchbuf
, adata
, adatalen
, matchlen
);
6018 if (!(flags
& IPPTOOL_WITH_ALL
))
6024 else if (flags
& IPPTOOL_WITH_ALL
)
6031 if (!match
&& errors
)
6033 for (i
= 0; i
< count
; i
++)
6035 adata
= ippGetOctetString(attr
, i
, &adatalen
);
6036 copy_hex_string(temp
, adata
, adatalen
, sizeof(temp
));
6037 add_stringf(data
->errors
, "GOT: %s=\"%s\"", name
, temp
);
6052 * 'with_value_from()' - Test a WITH-VALUE-FROM predicate.
6055 static int /* O - 1 on match, 0 on non-match */
6057 cups_array_t
*errors
, /* I - Errors array */
6058 ipp_attribute_t
*fromattr
, /* I - "From" attribute */
6059 ipp_attribute_t
*attr
, /* I - Attribute to compare */
6060 char *matchbuf
, /* I - Buffer to hold matching value */
6061 size_t matchlen
) /* I - Length of match buffer */
6063 int i
, j
, /* Looping vars */
6064 count
= ippGetCount(attr
), /* Number of attribute values */
6065 match
= 1; /* Match? */
6071 * Compare the from value(s) to the attribute value(s)...
6074 switch (ippGetValueTag(attr
))
6076 case IPP_TAG_INTEGER
:
6077 if (ippGetValueTag(fromattr
) != IPP_TAG_INTEGER
&& ippGetValueTag(fromattr
) != IPP_TAG_RANGE
)
6078 goto wrong_value_tag
;
6080 for (i
= 0; i
< count
; i
++)
6082 int value
= ippGetInteger(attr
, i
);
6083 /* Current integer value */
6085 if (ippContainsInteger(fromattr
, value
))
6088 snprintf(matchbuf
, matchlen
, "%d", value
);
6092 add_stringf(errors
, "GOT: %s=%d", ippGetName(attr
), value
);
6099 if (ippGetValueTag(fromattr
) != IPP_TAG_ENUM
)
6100 goto wrong_value_tag
;
6102 for (i
= 0; i
< count
; i
++)
6104 int value
= ippGetInteger(attr
, i
);
6105 /* Current integer value */
6107 if (ippContainsInteger(fromattr
, value
))
6110 snprintf(matchbuf
, matchlen
, "%d", value
);
6114 add_stringf(errors
, "GOT: %s=%d", ippGetName(attr
), value
);
6120 case IPP_TAG_RESOLUTION
:
6121 if (ippGetValueTag(fromattr
) != IPP_TAG_RESOLUTION
)
6122 goto wrong_value_tag
;
6124 for (i
= 0; i
< count
; i
++)
6128 int fromcount
= ippGetCount(fromattr
);
6129 int fromxres
, fromyres
;
6130 ipp_res_t fromunits
;
6132 xres
= ippGetResolution(attr
, i
, &yres
, &units
);
6134 for (j
= 0; j
< fromcount
; j
++)
6136 fromxres
= ippGetResolution(fromattr
, j
, &fromyres
, &fromunits
);
6137 if (fromxres
== xres
&& fromyres
== yres
&& fromunits
== units
)
6146 snprintf(matchbuf
, matchlen
, "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6148 snprintf(matchbuf
, matchlen
, "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6154 add_stringf(errors
, "GOT: %s=%d%s", ippGetName(attr
), xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6156 add_stringf(errors
, "GOT: %s=%dx%d%s", ippGetName(attr
), xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6163 case IPP_TAG_NOVALUE
:
6164 case IPP_TAG_UNKNOWN
:
6167 case IPP_TAG_CHARSET
:
6168 case IPP_TAG_KEYWORD
:
6169 case IPP_TAG_LANGUAGE
:
6170 case IPP_TAG_MIMETYPE
:
6172 case IPP_TAG_NAMELANG
:
6174 case IPP_TAG_TEXTLANG
:
6175 case IPP_TAG_URISCHEME
:
6176 for (i
= 0; i
< count
; i
++)
6178 const char *value
= ippGetString(attr
, i
, NULL
);
6179 /* Current string value */
6181 if (ippContainsString(fromattr
, value
))
6184 strlcpy(matchbuf
, value
, matchlen
);
6188 add_stringf(errors
, "GOT: %s='%s'", ippGetName(attr
), value
);
6195 for (i
= 0; i
< count
; i
++)
6197 const char *value
= ippGetString(attr
, i
, NULL
);
6198 /* Current string value */
6199 int fromcount
= ippGetCount(fromattr
);
6201 for (j
= 0; j
< fromcount
; j
++)
6203 if (!compare_uris(value
, ippGetString(fromattr
, j
, NULL
)))
6206 strlcpy(matchbuf
, value
, matchlen
);
6213 add_stringf(errors
, "GOT: %s='%s'", ippGetName(attr
), value
);
6226 /* value tag mismatch between fromattr and attr */
6229 add_stringf(errors
, "GOT: %s OF-TYPE %s", ippGetName(attr
), ippTagString(ippGetValueTag(attr
)));