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
)
1201 data
->monitor_done
= 0;
1202 data
->monitor_thread
= _cupsThreadCreate((_cups_thread_func_t
)do_monitor_printer_state
, data
);
1206 * Take over control of the attributes in the request...
1213 * Submit the IPP request...
1216 data
->test_count
++;
1218 ippSetVersion(request
, data
->version
/ 10, data
->version
% 10);
1219 ippSetRequestId(request
, data
->request_id
);
1221 if (data
->output
== IPPTOOL_OUTPUT_PLIST
)
1223 cupsFilePuts(data
->outfile
, "<dict>\n");
1224 cupsFilePuts(data
->outfile
, "<key>Name</key>\n");
1225 print_xml_string(data
->outfile
, "string", data
->name
);
1226 if (data
->file_id
[0])
1228 cupsFilePuts(data
->outfile
, "<key>FileId</key>\n");
1229 print_xml_string(data
->outfile
, "string", data
->file_id
);
1231 if (data
->test_id
[0])
1233 cupsFilePuts(data
->outfile
, "<key>TestId</key>\n");
1234 print_xml_string(data
->outfile
, "string", data
->test_id
);
1236 cupsFilePuts(data
->outfile
, "<key>Version</key>\n");
1237 cupsFilePrintf(data
->outfile
, "<string>%d.%d</string>\n", data
->version
/ 10, data
->version
% 10);
1238 cupsFilePuts(data
->outfile
, "<key>Operation</key>\n");
1239 print_xml_string(data
->outfile
, "string", ippOpString(ippGetOperation(request
)));
1240 cupsFilePuts(data
->outfile
, "<key>RequestId</key>\n");
1241 cupsFilePrintf(data
->outfile
, "<integer>%d</integer>\n", data
->request_id
);
1242 cupsFilePuts(data
->outfile
, "<key>RequestAttributes</key>\n");
1243 cupsFilePuts(data
->outfile
, "<array>\n");
1244 if (ippFirstAttribute(request
))
1246 cupsFilePuts(data
->outfile
, "<dict>\n");
1247 for (attrptr
= ippFirstAttribute(request
), group
= ippGetGroupTag(attrptr
); attrptr
; attrptr
= ippNextAttribute(request
))
1248 print_attr(data
->outfile
, data
->output
, attrptr
, &group
);
1249 cupsFilePuts(data
->outfile
, "</dict>\n");
1251 cupsFilePuts(data
->outfile
, "</array>\n");
1254 if (data
->output
== IPPTOOL_OUTPUT_TEST
|| (data
->output
== IPPTOOL_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
1256 if (data
->verbosity
)
1258 cupsFilePrintf(cupsFileStdout(), " %s:\n", ippOpString(ippGetOperation(request
)));
1260 for (attrptr
= ippFirstAttribute(request
); attrptr
; attrptr
= ippNextAttribute(request
))
1261 print_attr(cupsFileStdout(), IPPTOOL_OUTPUT_TEST
, attrptr
, NULL
);
1264 cupsFilePrintf(cupsFileStdout(), " %-68.68s [", data
->name
);
1267 if ((data
->skip_previous
&& !data
->prev_pass
) || data
->skip_test
|| data
->pass_test
)
1269 if (!data
->pass_test
)
1270 data
->skip_count
++;
1276 if (data
->output
== IPPTOOL_OUTPUT_PLIST
)
1278 cupsFilePuts(data
->outfile
, "<key>Successful</key>\n");
1279 cupsFilePuts(data
->outfile
, "<true />\n");
1280 cupsFilePuts(data
->outfile
, "<key>Skipped</key>\n");
1281 if (data
->pass_test
)
1282 cupsFilePuts(data
->outfile
, "<false />\n");
1284 cupsFilePuts(data
->outfile
, "<true />\n");
1285 cupsFilePuts(data
->outfile
, "<key>StatusCode</key>\n");
1286 if (data
->pass_test
)
1287 print_xml_string(data
->outfile
, "string", "pass");
1289 print_xml_string(data
->outfile
, "string", "skip");
1290 cupsFilePuts(data
->outfile
, "<key>ResponseAttributes</key>\n");
1291 cupsFilePuts(data
->outfile
, "<dict />\n");
1294 if (data
->output
== IPPTOOL_OUTPUT_TEST
|| (data
->output
== IPPTOOL_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
1296 if (data
->pass_test
)
1297 cupsFilePuts(cupsFileStdout(), "PASS]\n");
1299 cupsFilePuts(cupsFileStdout(), "SKIP]\n");
1305 data
->vars
->password_tries
= 0;
1309 if (data
->delay
> 0)
1310 usleep(data
->delay
);
1312 data
->delay
= data
->repeat_interval
;
1315 status
= HTTP_STATUS_OK
;
1317 if (data
->transfer
== IPPTOOL_TRANSFER_CHUNKED
|| (data
->transfer
== IPPTOOL_TRANSFER_AUTO
&& data
->file
[0]))
1320 * Send request using chunking - a 0 length means "chunk".
1328 * Send request using content length...
1331 length
= ippLength(request
);
1333 if (data
->file
[0] && (reqfile
= cupsFileOpen(data
->file
, "r")) != NULL
)
1336 * Read the file to get the uncompressed file size...
1339 while ((bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
1340 length
+= (size_t)bytes
;
1342 cupsFileClose(reqfile
);
1347 * Send the request...
1350 data
->prev_pass
= 1;
1354 if (status
!= HTTP_STATUS_ERROR
)
1356 while (!response
&& !Cancel
&& data
->prev_pass
)
1358 ippSetRequestId(request
, ++ data
->request_id
);
1360 status
= cupsSendRequest(data
->http
, request
, data
->resource
, length
);
1363 if (data
->compression
[0])
1364 httpSetField(data
->http
, HTTP_FIELD_CONTENT_ENCODING
, data
->compression
);
1365 #endif /* HAVE_LIBZ */
1367 if (!Cancel
&& status
== HTTP_STATUS_CONTINUE
&& ippGetState(request
) == IPP_DATA
&& data
->file
[0])
1369 if ((reqfile
= cupsFileOpen(data
->file
, "r")) != NULL
)
1371 while (!Cancel
&& (bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
1373 if ((status
= cupsWriteRequestData(data
->http
, buffer
, (size_t)bytes
)) != HTTP_STATUS_CONTINUE
)
1377 cupsFileClose(reqfile
);
1381 snprintf(buffer
, sizeof(buffer
), "%s: %s", data
->file
, strerror(errno
));
1382 _cupsSetError(IPP_INTERNAL_ERROR
, buffer
, 0);
1384 status
= HTTP_STATUS_ERROR
;
1389 * Get the server's response...
1392 if (!Cancel
&& status
!= HTTP_STATUS_ERROR
)
1394 response
= cupsGetResponse(data
->http
, data
->resource
);
1395 status
= httpGetStatus(data
->http
);
1398 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& httpError(data
->http
) != EINVAL
&&
1400 httpError(data
->http
) != WSAETIMEDOUT
)
1402 httpError(data
->http
) != ETIMEDOUT
)
1405 if (httpReconnect2(data
->http
, 30000, NULL
))
1406 data
->prev_pass
= 0;
1408 else if (status
== HTTP_STATUS_ERROR
|| status
== HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED
)
1410 data
->prev_pass
= 0;
1413 else if (status
!= HTTP_STATUS_OK
)
1415 httpFlush(data
->http
);
1417 if (status
== HTTP_STATUS_UNAUTHORIZED
)
1425 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& httpError(data
->http
) != EINVAL
&&
1427 httpError(data
->http
) != WSAETIMEDOUT
)
1429 httpError(data
->http
) != ETIMEDOUT
)
1432 if (httpReconnect2(data
->http
, 30000, NULL
))
1433 data
->prev_pass
= 0;
1435 else if (status
== HTTP_STATUS_ERROR
)
1438 httpReconnect2(data
->http
, 30000, NULL
);
1440 data
->prev_pass
= 0;
1442 else if (status
!= HTTP_STATUS_OK
)
1444 httpFlush(data
->http
);
1445 data
->prev_pass
= 0;
1449 * Check results of request...
1452 cupsArrayClear(data
->errors
);
1454 if (httpGetVersion(data
->http
) != HTTP_1_1
)
1456 int version
= (int)httpGetVersion(data
->http
);
1458 add_stringf(data
->errors
, "Bad HTTP version (%d.%d)", version
/ 100, version
% 100);
1461 if (data
->validate_headers
)
1463 const char *header
; /* HTTP header value */
1465 if ((header
= httpGetField(data
->http
, HTTP_FIELD_CONTENT_TYPE
)) == NULL
|| _cups_strcasecmp(header
, "application/ipp"))
1466 add_stringf(data
->errors
, "Bad HTTP Content-Type in response (%s)", header
&& *header
? header
: "<missing>");
1468 if ((header
= httpGetField(data
->http
, HTTP_FIELD_DATE
)) != NULL
&& *header
&& httpGetDateTime(header
) == 0)
1469 add_stringf(data
->errors
, "Bad HTTP Date in response (%s)", header
);
1475 * No response, log error...
1478 add_stringf(data
->errors
, "IPP request failed with status %s (%s)", ippErrorString(cupsLastError()), cupsLastErrorString());
1483 * Collect common attribute values...
1486 if ((attrptr
= ippFindAttribute(response
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
1488 snprintf(temp
, sizeof(temp
), "%d", ippGetInteger(attrptr
, 0));
1489 _ippVarsSet(data
->vars
, "job-id", temp
);
1492 if ((attrptr
= ippFindAttribute(response
, "job-uri", IPP_TAG_URI
)) != NULL
)
1493 _ippVarsSet(data
->vars
, "job-uri", ippGetString(attrptr
, 0, NULL
));
1495 if ((attrptr
= ippFindAttribute(response
, "notify-subscription-id", IPP_TAG_INTEGER
)) != NULL
)
1497 snprintf(temp
, sizeof(temp
), "%d", ippGetInteger(attrptr
, 0));
1498 _ippVarsSet(data
->vars
, "notify-subscription-id", temp
);
1502 * Check response, validating groups and attributes and logging errors
1506 if (ippGetState(response
) != IPP_DATA
)
1507 add_stringf(data
->errors
, "Missing end-of-attributes-tag in response (RFC 2910 section 3.5.1)");
1511 int major
, minor
; /* IPP version */
1513 major
= ippGetVersion(response
, &minor
);
1515 if (major
!= (data
->version
/ 10) || minor
!= (data
->version
% 10))
1516 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);
1519 if (ippGetRequestId(response
) != data
->request_id
)
1520 add_stringf(data
->errors
, "Bad request ID %d in response - expected %d (RFC 8011 section 4.1.1)", ippGetRequestId(response
), data
->request_id
);
1522 attrptr
= ippFirstAttribute(response
);
1525 add_stringf(data
->errors
, "Missing first attribute \"attributes-charset (charset)\" in group operation-attributes-tag (RFC 8011 section 4.1.4).");
1529 if (!ippGetName(attrptr
) || ippGetValueTag(attrptr
) != IPP_TAG_CHARSET
|| ippGetGroupTag(attrptr
) != IPP_TAG_OPERATION
|| ippGetCount(attrptr
) != 1 ||strcmp(ippGetName(attrptr
), "attributes-charset"))
1530 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
)));
1532 attrptr
= ippNextAttribute(response
);
1534 add_stringf(data
->errors
, "Missing second attribute \"attributes-natural-language (naturalLanguage)\" in group operation-attributes-tag (RFC 8011 section 4.1.4).");
1535 else if (!ippGetName(attrptr
) || ippGetValueTag(attrptr
) != IPP_TAG_LANGUAGE
|| ippGetGroupTag(attrptr
) != IPP_TAG_OPERATION
|| ippGetCount(attrptr
) != 1 || strcmp(ippGetName(attrptr
), "attributes-natural-language"))
1536 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
)));
1539 if ((attrptr
= ippFindAttribute(response
, "status-message", IPP_TAG_ZERO
)) != NULL
)
1541 const char *status_message
= ippGetString(attrptr
, 0, NULL
);
1544 if (ippGetValueTag(attrptr
) != IPP_TAG_TEXT
)
1545 add_stringf(data
->errors
, "status-message (text(255)) has wrong value tag %s (RFC 8011 section 4.1.6.2).", ippTagString(ippGetValueTag(attrptr
)));
1546 if (ippGetGroupTag(attrptr
) != IPP_TAG_OPERATION
)
1547 add_stringf(data
->errors
, "status-message (text(255)) has wrong group tag %s (RFC 8011 section 4.1.6.2).", ippTagString(ippGetGroupTag(attrptr
)));
1548 if (ippGetCount(attrptr
) != 1)
1549 add_stringf(data
->errors
, "status-message (text(255)) has %d values (RFC 8011 section 4.1.6.2).", ippGetCount(attrptr
));
1550 if (status_message
&& strlen(status_message
) > 255)
1551 add_stringf(data
->errors
, "status-message (text(255)) has bad length %d (RFC 8011 section 4.1.6.2).", (int)strlen(status_message
));
1554 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
1555 IPP_TAG_ZERO
)) != NULL
)
1557 const char *detailed_status_message
= ippGetString(attrptr
, 0, NULL
);
1560 if (ippGetValueTag(attrptr
) != IPP_TAG_TEXT
)
1561 add_stringf(data
->errors
, "detailed-status-message (text(MAX)) has wrong value tag %s (RFC 8011 section 4.1.6.3).", ippTagString(ippGetValueTag(attrptr
)));
1562 if (ippGetGroupTag(attrptr
) != IPP_TAG_OPERATION
)
1563 add_stringf(data
->errors
, "detailed-status-message (text(MAX)) has wrong group tag %s (RFC 8011 section 4.1.6.3).", ippTagString(ippGetGroupTag(attrptr
)));
1564 if (ippGetCount(attrptr
) != 1)
1565 add_stringf(data
->errors
, "detailed-status-message (text(MAX)) has %d values (RFC 8011 section 4.1.6.3).", ippGetCount(attrptr
));
1566 if (detailed_status_message
&& strlen(detailed_status_message
) > 1023)
1567 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
));
1570 a
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
1572 for (attrptr
= ippFirstAttribute(response
), group
= ippGetGroupTag(attrptr
);
1574 attrptr
= ippNextAttribute(response
))
1576 if (ippGetGroupTag(attrptr
) != group
)
1578 int out_of_order
= 0; /* Are attribute groups out-of-order? */
1581 switch (ippGetGroupTag(attrptr
))
1586 case IPP_TAG_OPERATION
:
1590 case IPP_TAG_UNSUPPORTED_GROUP
:
1591 if (group
!= IPP_TAG_OPERATION
)
1596 case IPP_TAG_PRINTER
:
1597 if (group
!= IPP_TAG_OPERATION
&& group
!= IPP_TAG_UNSUPPORTED_GROUP
)
1601 case IPP_TAG_SUBSCRIPTION
:
1602 if (group
> ippGetGroupTag(attrptr
) && group
!= IPP_TAG_DOCUMENT
)
1607 if (group
> ippGetGroupTag(attrptr
))
1613 add_stringf(data
->errors
, "Attribute groups out of order (%s < %s)", ippTagString(ippGetGroupTag(attrptr
)), ippTagString(group
));
1615 if (ippGetGroupTag(attrptr
) != IPP_TAG_ZERO
)
1616 group
= ippGetGroupTag(attrptr
);
1619 if (!ippValidateAttribute(attrptr
))
1620 cupsArrayAdd(data
->errors
, (void *)cupsLastErrorString());
1622 if (ippGetName(attrptr
))
1624 if (cupsArrayFind(a
, (void *)ippGetName(attrptr
)) && data
->output
< IPPTOOL_OUTPUT_LIST
)
1625 add_stringf(data
->errors
, "Duplicate \"%s\" attribute in %s group", ippGetName(attrptr
), ippTagString(group
));
1627 cupsArrayAdd(a
, (void *)ippGetName(attrptr
));
1634 * Now check the test-defined expected status-code and attribute
1638 if (ippGetStatusCode(response
) == IPP_STATUS_ERROR_BUSY
&& data
->repeat_on_busy
)
1640 // Repeat on a server-error-busy status code...
1645 for (i
= 0, status_ok
= 0; i
< data
->num_statuses
; i
++)
1647 if (data
->statuses
[i
].if_defined
&&
1648 !_ippVarsGet(data
->vars
, data
->statuses
[i
].if_defined
))
1651 if (data
->statuses
[i
].if_not_defined
&&
1652 _ippVarsGet(data
->vars
, data
->statuses
[i
].if_not_defined
))
1655 if (ippGetStatusCode(response
) == data
->statuses
[i
].status
)
1659 if (data
->statuses
[i
].repeat_match
&& repeat_count
< data
->statuses
[i
].repeat_limit
)
1662 if (data
->statuses
[i
].define_match
)
1663 _ippVarsSet(data
->vars
, data
->statuses
[i
].define_match
, "1");
1667 if (data
->statuses
[i
].repeat_no_match
&& repeat_count
< data
->statuses
[i
].repeat_limit
)
1670 if (data
->statuses
[i
].define_no_match
)
1672 _ippVarsSet(data
->vars
, data
->statuses
[i
].define_no_match
, "1");
1678 if (!status_ok
&& data
->num_statuses
> 0)
1680 for (i
= 0; i
< data
->num_statuses
; i
++)
1682 if (data
->statuses
[i
].if_defined
&&
1683 !_ippVarsGet(data
->vars
, data
->statuses
[i
].if_defined
))
1686 if (data
->statuses
[i
].if_not_defined
&&
1687 _ippVarsGet(data
->vars
, data
->statuses
[i
].if_not_defined
))
1690 if (!data
->statuses
[i
].repeat_match
|| repeat_count
>= data
->statuses
[i
].repeat_limit
)
1691 add_stringf(data
->errors
, "EXPECTED: STATUS %s (got %s)", ippErrorString(data
->statuses
[i
].status
), ippErrorString(cupsLastError()));
1694 if ((attrptr
= ippFindAttribute(response
, "status-message", IPP_TAG_TEXT
)) != NULL
)
1695 add_stringf(data
->errors
, "status-message=\"%s\"", ippGetString(attrptr
, 0, NULL
));
1698 for (i
= data
->num_expects
, expect
= data
->expects
; i
> 0; i
--, expect
++)
1700 ipp_attribute_t
*group_found
; /* Found parent attribute for group tests */
1702 if (expect
->if_defined
&& !_ippVarsGet(data
->vars
, expect
->if_defined
))
1705 if (expect
->if_not_defined
&&
1706 _ippVarsGet(data
->vars
, expect
->if_not_defined
))
1709 if ((found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
)) != NULL
&& expect
->in_group
&& expect
->in_group
!= ippGetGroupTag(found
))
1711 while ((found
= ippFindNextAttribute(response
, expect
->name
, IPP_TAG_ZERO
)) != NULL
)
1712 if (expect
->in_group
== ippGetGroupTag(found
))
1718 group_found
= found
;
1720 if (expect
->in_group
&& strchr(expect
->name
, '/'))
1722 char group_name
[256],/* Parent attribute name */
1723 *group_ptr
; /* Pointer into parent attribute name */
1725 strlcpy(group_name
, expect
->name
, sizeof(group_name
));
1726 if ((group_ptr
= strchr(group_name
, '/')) != NULL
)
1729 group_found
= ippFindAttribute(response
, group_name
, IPP_TAG_ZERO
);
1732 if ((found
&& expect
->not_expect
) ||
1733 (!found
&& !(expect
->not_expect
|| expect
->optional
)) ||
1734 (found
&& !expect_matches(expect
, found
)) ||
1735 (group_found
&& expect
->in_group
&& ippGetGroupTag(group_found
) != expect
->in_group
) ||
1736 (expect
->with_distinct
&& !with_distinct_values(NULL
, found
)))
1738 if (expect
->define_no_match
)
1739 _ippVarsSet(data
->vars
, expect
->define_no_match
, "1");
1740 else if (!expect
->define_match
&& !expect
->define_value
)
1742 if (found
&& expect
->not_expect
&& !expect
->with_value
&& !expect
->with_value_from
)
1743 add_stringf(data
->errors
, "NOT EXPECTED: %s", expect
->name
);
1744 else if (!found
&& !(expect
->not_expect
|| expect
->optional
))
1745 add_stringf(data
->errors
, "EXPECTED: %s", expect
->name
);
1748 if (!expect_matches(expect
, found
))
1749 add_stringf(data
->errors
, "EXPECTED: %s OF-TYPE %s (got %s)",
1750 expect
->name
, expect
->of_type
,
1751 ippTagString(ippGetValueTag(found
)));
1753 if (expect
->in_group
&& ippGetGroupTag(group_found
) != expect
->in_group
)
1754 add_stringf(data
->errors
, "EXPECTED: %s IN-GROUP %s (got %s).",
1755 expect
->name
, ippTagString(expect
->in_group
),
1756 ippTagString(ippGetGroupTag(group_found
)));
1758 if (expect
->with_distinct
)
1759 with_distinct_values(data
->errors
, found
);
1763 if (expect
->repeat_no_match
&& repeat_count
< expect
->repeat_limit
)
1769 ippAttributeString(found
, buffer
, sizeof(buffer
));
1771 if (found
&& expect
->with_value_from
&& !with_value_from(NULL
, ippFindAttribute(response
, expect
->with_value_from
, IPP_TAG_ZERO
), found
, buffer
, sizeof(buffer
)))
1773 if (expect
->define_no_match
)
1774 _ippVarsSet(data
->vars
, expect
->define_no_match
, "1");
1775 else if (!expect
->define_match
&& !expect
->define_value
&& ((!expect
->repeat_match
&& !expect
->repeat_no_match
) || repeat_count
>= expect
->repeat_limit
))
1777 add_stringf(data
->errors
, "EXPECTED: %s WITH-VALUES-FROM %s", expect
->name
, expect
->with_value_from
);
1779 with_value_from(data
->errors
, ippFindAttribute(response
, expect
->with_value_from
, IPP_TAG_ZERO
), found
, buffer
, sizeof(buffer
));
1782 if (expect
->repeat_no_match
&& repeat_count
< expect
->repeat_limit
)
1787 else if (found
&& !with_value(data
, NULL
, expect
->with_value
, expect
->with_flags
, found
, buffer
, sizeof(buffer
)))
1789 if (expect
->define_no_match
)
1790 _ippVarsSet(data
->vars
, expect
->define_no_match
, "1");
1791 else if (!expect
->define_match
&& !expect
->define_value
&&
1792 !expect
->repeat_match
&& (!expect
->repeat_no_match
|| repeat_count
>= expect
->repeat_limit
))
1794 if (expect
->with_flags
& IPPTOOL_WITH_REGEX
)
1795 add_stringf(data
->errors
, "EXPECTED: %s %s /%s/", expect
->name
, with_flags_string(expect
->with_flags
), expect
->with_value
);
1797 add_stringf(data
->errors
, "EXPECTED: %s %s \"%s\"", expect
->name
, with_flags_string(expect
->with_flags
), expect
->with_value
);
1799 with_value(data
, data
->errors
, expect
->with_value
, expect
->with_flags
, found
, buffer
, sizeof(buffer
));
1802 if (expect
->repeat_no_match
&&
1803 repeat_count
< expect
->repeat_limit
)
1809 if (found
&& expect
->count
> 0 && ippGetCount(found
) != expect
->count
)
1811 if (expect
->define_no_match
)
1812 _ippVarsSet(data
->vars
, expect
->define_no_match
, "1");
1813 else if (!expect
->define_match
&& !expect
->define_value
)
1815 add_stringf(data
->errors
, "EXPECTED: %s COUNT %d (got %d)", expect
->name
, expect
->count
, ippGetCount(found
));
1818 if (expect
->repeat_no_match
&&
1819 repeat_count
< expect
->repeat_limit
)
1825 if (found
&& expect
->same_count_as
)
1827 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
1830 if (!attrptr
|| ippGetCount(attrptr
) != ippGetCount(found
))
1832 if (expect
->define_no_match
)
1833 _ippVarsSet(data
->vars
, expect
->define_no_match
, "1");
1834 else if (!expect
->define_match
&& !expect
->define_value
)
1837 add_stringf(data
->errors
, "EXPECTED: %s (%d values) SAME-COUNT-AS %s (not returned)", expect
->name
, ippGetCount(found
), expect
->same_count_as
);
1838 else if (ippGetCount(attrptr
) != ippGetCount(found
))
1839 add_stringf(data
->errors
, "EXPECTED: %s (%d values) SAME-COUNT-AS %s (%d values)", expect
->name
, ippGetCount(found
), expect
->same_count_as
, ippGetCount(attrptr
));
1842 if (expect
->repeat_no_match
&&
1843 repeat_count
< expect
->repeat_limit
)
1850 if (found
&& expect
->define_match
)
1851 _ippVarsSet(data
->vars
, expect
->define_match
, "1");
1853 if (found
&& expect
->define_value
)
1855 if (!expect
->with_value
)
1857 int last
= ippGetCount(found
) - 1;
1858 /* Last element in attribute */
1860 switch (ippGetValueTag(found
))
1863 case IPP_TAG_INTEGER
:
1864 snprintf(buffer
, sizeof(buffer
), "%d", ippGetInteger(found
, last
));
1867 case IPP_TAG_BOOLEAN
:
1868 if (ippGetBoolean(found
, last
))
1869 strlcpy(buffer
, "true", sizeof(buffer
));
1871 strlcpy(buffer
, "false", sizeof(buffer
));
1874 case IPP_TAG_RESOLUTION
:
1876 int xres
, /* Horizontal resolution */
1877 yres
; /* Vertical resolution */
1878 ipp_res_t units
; /* Resolution units */
1880 xres
= ippGetResolution(found
, last
, &yres
, &units
);
1883 snprintf(buffer
, sizeof(buffer
), "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
1885 snprintf(buffer
, sizeof(buffer
), "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
1889 case IPP_TAG_CHARSET
:
1890 case IPP_TAG_KEYWORD
:
1891 case IPP_TAG_LANGUAGE
:
1892 case IPP_TAG_MIMETYPE
:
1894 case IPP_TAG_NAMELANG
:
1896 case IPP_TAG_TEXTLANG
:
1898 case IPP_TAG_URISCHEME
:
1899 strlcpy(buffer
, ippGetString(found
, last
, NULL
), sizeof(buffer
));
1903 ippAttributeString(found
, buffer
, sizeof(buffer
));
1908 _ippVarsSet(data
->vars
, expect
->define_value
, buffer
);
1911 if (found
&& expect
->repeat_match
&&
1912 repeat_count
< expect
->repeat_limit
)
1915 while (expect
->expect_all
&& (found
= ippFindNextAttribute(response
, expect
->name
, IPP_TAG_ZERO
)) != NULL
);
1920 * If we are going to repeat this test, display intermediate results...
1925 if (data
->output
== IPPTOOL_OUTPUT_TEST
|| (data
->output
== IPPTOOL_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
1927 cupsFilePrintf(cupsFileStdout(), "%04d]\n", repeat_count
);
1929 if (data
->num_displayed
> 0)
1931 for (attrptr
= ippFirstAttribute(response
); attrptr
; attrptr
= ippNextAttribute(response
))
1933 const char *attrname
= ippGetName(attrptr
);
1936 for (i
= 0; i
< data
->num_displayed
; i
++)
1938 if (!strcmp(data
->displayed
[i
], attrname
))
1940 print_attr(cupsFileStdout(), IPPTOOL_OUTPUT_TEST
, attrptr
, NULL
);
1949 if (data
->output
== IPPTOOL_OUTPUT_TEST
|| (data
->output
== IPPTOOL_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
1951 cupsFilePrintf(cupsFileStdout(), " %-68.68s [", data
->name
);
1954 ippDelete(response
);
1958 while (repeat_test
);
1964 if (cupsArrayCount(data
->errors
) > 0)
1965 data
->prev_pass
= data
->pass
= 0;
1967 if (data
->prev_pass
)
1968 data
->pass_count
++;
1970 data
->fail_count
++;
1972 if (data
->output
== IPPTOOL_OUTPUT_PLIST
)
1974 cupsFilePuts(data
->outfile
, "<key>Successful</key>\n");
1975 cupsFilePuts(data
->outfile
, data
->prev_pass
? "<true />\n" : "<false />\n");
1976 cupsFilePuts(data
->outfile
, "<key>StatusCode</key>\n");
1977 print_xml_string(data
->outfile
, "string", ippErrorString(cupsLastError()));
1978 cupsFilePuts(data
->outfile
, "<key>ResponseAttributes</key>\n");
1979 cupsFilePuts(data
->outfile
, "<array>\n");
1980 cupsFilePuts(data
->outfile
, "<dict>\n");
1981 for (attrptr
= ippFirstAttribute(response
), group
= ippGetGroupTag(attrptr
);
1983 attrptr
= ippNextAttribute(response
))
1984 print_attr(data
->outfile
, data
->output
, attrptr
, &group
);
1985 cupsFilePuts(data
->outfile
, "</dict>\n");
1986 cupsFilePuts(data
->outfile
, "</array>\n");
1988 else if (data
->output
== IPPTOOL_OUTPUT_IPPSERVER
&& response
)
1990 for (attrptr
= ippFirstAttribute(response
); attrptr
; attrptr
= ippNextAttribute(response
))
1992 if (!ippGetName(attrptr
) || ippGetGroupTag(attrptr
) != IPP_TAG_PRINTER
)
1995 print_ippserver_attr(data
, attrptr
, 0);
1999 if (data
->output
== IPPTOOL_OUTPUT_TEST
|| (data
->output
== IPPTOOL_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
2001 cupsFilePuts(cupsFileStdout(), data
->prev_pass
? "PASS]\n" : "FAIL]\n");
2003 if (!data
->prev_pass
|| (data
->verbosity
&& response
))
2005 cupsFilePrintf(cupsFileStdout(), " RECEIVED: %lu bytes in response\n", (unsigned long)ippLength(response
));
2006 cupsFilePrintf(cupsFileStdout(), " status-code = %s (%s)\n", ippErrorString(cupsLastError()), cupsLastErrorString());
2008 if (data
->verbosity
&& response
)
2010 for (attrptr
= ippFirstAttribute(response
); attrptr
; attrptr
= ippNextAttribute(response
))
2011 print_attr(cupsFileStdout(), IPPTOOL_OUTPUT_TEST
, attrptr
, NULL
);
2015 else if (!data
->prev_pass
&& data
->output
!= IPPTOOL_OUTPUT_QUIET
)
2016 fprintf(stderr
, "%s\n", cupsLastErrorString());
2018 if (data
->prev_pass
&& data
->output
>= IPPTOOL_OUTPUT_LIST
&& !data
->verbosity
&& data
->num_displayed
> 0)
2020 size_t width
; /* Length of value */
2022 for (i
= 0; i
< data
->num_displayed
; i
++)
2024 widths
[i
] = strlen(data
->displayed
[i
]);
2026 for (attrptr
= ippFindAttribute(response
, data
->displayed
[i
], IPP_TAG_ZERO
);
2028 attrptr
= ippFindNextAttribute(response
, data
->displayed
[i
], IPP_TAG_ZERO
))
2030 width
= ippAttributeString(attrptr
, NULL
, 0);
2031 if (width
> widths
[i
])
2036 if (data
->output
== IPPTOOL_OUTPUT_CSV
)
2037 print_csv(data
, NULL
, NULL
, data
->num_displayed
, data
->displayed
, widths
);
2039 print_line(data
, NULL
, NULL
, data
->num_displayed
, data
->displayed
, widths
);
2041 attrptr
= ippFirstAttribute(response
);
2045 while (attrptr
&& ippGetGroupTag(attrptr
) <= IPP_TAG_OPERATION
)
2046 attrptr
= ippNextAttribute(response
);
2050 if (data
->output
== IPPTOOL_OUTPUT_CSV
)
2051 attrptr
= print_csv(data
, response
, attrptr
, data
->num_displayed
, data
->displayed
, widths
);
2053 attrptr
= print_line(data
, response
, attrptr
, data
->num_displayed
, data
->displayed
, widths
);
2055 while (attrptr
&& ippGetGroupTag(attrptr
) > IPP_TAG_OPERATION
)
2056 attrptr
= ippNextAttribute(response
);
2060 else if (!data
->prev_pass
)
2062 if (data
->output
== IPPTOOL_OUTPUT_PLIST
)
2064 cupsFilePuts(data
->outfile
, "<key>Errors</key>\n");
2065 cupsFilePuts(data
->outfile
, "<array>\n");
2067 for (error
= (char *)cupsArrayFirst(data
->errors
);
2069 error
= (char *)cupsArrayNext(data
->errors
))
2070 print_xml_string(data
->outfile
, "string", error
);
2072 cupsFilePuts(data
->outfile
, "</array>\n");
2075 if (data
->output
== IPPTOOL_OUTPUT_TEST
|| (data
->output
== IPPTOOL_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
2077 for (error
= (char *)cupsArrayFirst(data
->errors
);
2079 error
= (char *)cupsArrayNext(data
->errors
))
2080 cupsFilePrintf(cupsFileStdout(), " %s\n", error
);
2084 if (data
->num_displayed
> 0 && !data
->verbosity
&& response
&& (data
->output
== IPPTOOL_OUTPUT_TEST
|| (data
->output
== IPPTOOL_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout())))
2086 for (attrptr
= ippFirstAttribute(response
); attrptr
; attrptr
= ippNextAttribute(response
))
2088 if (ippGetName(attrptr
))
2090 for (i
= 0; i
< data
->num_displayed
; i
++)
2092 if (!strcmp(data
->displayed
[i
], ippGetName(attrptr
)))
2094 print_attr(data
->outfile
, data
->output
, attrptr
, NULL
);
2104 if (data
->monitor_thread
)
2106 data
->monitor_done
= 1;
2107 _cupsThreadWait(data
->monitor_thread
);
2110 if (data
->output
== IPPTOOL_OUTPUT_PLIST
)
2111 cupsFilePuts(data
->outfile
, "</dict>\n");
2113 ippDelete(response
);
2116 for (i
= 0; i
< data
->num_statuses
; i
++)
2118 if (data
->statuses
[i
].if_defined
)
2119 free(data
->statuses
[i
].if_defined
);
2120 if (data
->statuses
[i
].if_not_defined
)
2121 free(data
->statuses
[i
].if_not_defined
);
2122 if (data
->statuses
[i
].define_match
)
2123 free(data
->statuses
[i
].define_match
);
2124 if (data
->statuses
[i
].define_no_match
)
2125 free(data
->statuses
[i
].define_no_match
);
2127 data
->num_statuses
= 0;
2129 for (i
= data
->num_expects
, expect
= data
->expects
; i
> 0; i
--, expect
++)
2132 if (expect
->of_type
)
2133 free(expect
->of_type
);
2134 if (expect
->same_count_as
)
2135 free(expect
->same_count_as
);
2136 if (expect
->if_defined
)
2137 free(expect
->if_defined
);
2138 if (expect
->if_not_defined
)
2139 free(expect
->if_not_defined
);
2140 if (expect
->with_value
)
2141 free(expect
->with_value
);
2142 if (expect
->define_match
)
2143 free(expect
->define_match
);
2144 if (expect
->define_no_match
)
2145 free(expect
->define_no_match
);
2146 if (expect
->define_value
)
2147 free(expect
->define_value
);
2149 data
->num_expects
= 0;
2151 for (i
= 0; i
< data
->num_displayed
; i
++)
2152 free(data
->displayed
[i
]);
2153 data
->num_displayed
= 0;
2155 free(data
->monitor_uri
);
2156 data
->monitor_uri
= NULL
;
2158 for (i
= data
->num_monitor_expects
, expect
= data
->monitor_expects
; i
> 0; i
--, expect
++)
2161 if (expect
->of_type
)
2162 free(expect
->of_type
);
2163 if (expect
->same_count_as
)
2164 free(expect
->same_count_as
);
2165 if (expect
->if_defined
)
2166 free(expect
->if_defined
);
2167 if (expect
->if_not_defined
)
2168 free(expect
->if_not_defined
);
2169 if (expect
->with_value
)
2170 free(expect
->with_value
);
2171 if (expect
->define_match
)
2172 free(expect
->define_match
);
2173 if (expect
->define_no_match
)
2174 free(expect
->define_no_match
);
2175 if (expect
->define_value
)
2176 free(expect
->define_value
);
2178 data
->num_monitor_expects
= 0;
2180 return (data
->ignore_errors
|| data
->prev_pass
);
2185 * 'do_tests()' - Do tests as specified in the test file.
2188 static int /* O - 1 on success, 0 on failure */
2189 do_tests(const char *testfile
, /* I - Test file to use */
2190 ipptool_test_t
*data
) /* I - Test data */
2192 http_encryption_t encryption
; /* Encryption mode */
2196 * Connect to the printer/server...
2199 if (!_cups_strcasecmp(data
->vars
->scheme
, "https") || !_cups_strcasecmp(data
->vars
->scheme
, "ipps") || data
->vars
->port
== 443)
2200 encryption
= HTTP_ENCRYPTION_ALWAYS
;
2202 encryption
= data
->encryption
;
2204 if ((data
->http
= httpConnect2(data
->vars
->host
, data
->vars
->port
, NULL
, data
->family
, encryption
, 1, 30000, NULL
)) == NULL
)
2206 print_fatal_error(data
, "Unable to connect to \"%s\" on port %d - %s", data
->vars
->host
, data
->vars
->port
, cupsLastErrorString());
2211 httpSetDefaultField(data
->http
, HTTP_FIELD_ACCEPT_ENCODING
, "deflate, gzip, identity");
2213 httpSetDefaultField(data
->http
, HTTP_FIELD_ACCEPT_ENCODING
, "identity");
2214 #endif /* HAVE_LIBZ */
2216 if (data
->timeout
> 0.0)
2217 httpSetTimeout(data
->http
, data
->timeout
, timeout_cb
, NULL
);
2223 _ippFileParse(data
->vars
, testfile
, (void *)data
);
2226 * Close connection and return...
2229 httpClose(data
->http
);
2232 return (data
->pass
);
2237 * 'error_cb()' - Print/add an error message.
2240 static int /* O - 1 to continue, 0 to stop */
2241 error_cb(_ipp_file_t
*f
, /* I - IPP file data */
2242 ipptool_test_t
*data
, /* I - Test data */
2243 const char *error
) /* I - Error message */
2247 print_fatal_error(data
, "%s", error
);
2254 * 'expect_matches()' - Return true if the tag matches the specification.
2257 static int /* O - 1 if matches, 0 otherwise */
2259 ipptool_expect_t
*expect
, /* I - Expected attribute */
2260 ipp_attribute_t
*attr
) /* I - Attribute */
2262 int i
, /* Looping var */
2263 count
, /* Number of values */
2265 char *of_type
, /* Type name to match */
2266 *paren
, /* Pointer to opening parenthesis */
2267 *next
, /* Next name to match */
2268 sep
; /* Separator character */
2269 ipp_tag_t value_tag
; /* Syntax/value tag */
2270 int lower
, upper
; /* Lower and upper bounds for syntax */
2274 * If we don't expect a particular type, return immediately...
2277 if (!expect
->of_type
)
2281 * Parse the "of_type" value since the string can contain multiple attribute
2282 * types separated by "," or "|"...
2285 value_tag
= ippGetValueTag(attr
);
2286 count
= ippGetCount(attr
);
2288 for (of_type
= expect
->of_type
, match
= 0; !match
&& *of_type
; of_type
= next
)
2291 * Find the next separator, and set it (temporarily) to nul if present.
2294 for (next
= of_type
; *next
&& *next
!= '|' && *next
!= ','; next
++);
2296 if ((sep
= *next
) != '\0')
2300 * Support some meta-types to make it easier to write the test file.
2303 if ((paren
= strchr(of_type
, '(')) != NULL
)
2305 char *ptr
; // Pointer into syntax string
2309 if (!strncmp(paren
+ 1, "MIN:", 4))
2314 else if ((ptr
= strchr(paren
+ 1, ':')) != NULL
)
2316 lower
= atoi(paren
+ 1);
2324 if (!strcmp(ptr
, "MAX)"))
2335 if (!strcmp(of_type
, "text"))
2337 if (upper
== INT_MAX
)
2340 if (value_tag
== IPP_TAG_TEXTLANG
|| value_tag
== IPP_TAG_TEXT
)
2342 for (i
= 0; i
< count
; i
++)
2344 if (strlen(ippGetString(attr
, i
, NULL
)) > (size_t)upper
)
2348 match
= (i
== count
);
2351 else if (!strcmp(of_type
, "name"))
2353 if (upper
== INT_MAX
)
2356 if (value_tag
== IPP_TAG_NAMELANG
|| value_tag
== IPP_TAG_NAME
)
2358 for (i
= 0; i
< count
; i
++)
2360 if (strlen(ippGetString(attr
, i
, NULL
)) > (size_t)upper
)
2364 match
= (i
== count
);
2367 else if (!strcmp(of_type
, "collection"))
2369 match
= value_tag
== IPP_TAG_BEGIN_COLLECTION
;
2371 else if (value_tag
== ippTagValue(of_type
))
2375 case IPP_TAG_KEYWORD
:
2377 if (upper
== INT_MAX
)
2379 if (value_tag
== IPP_TAG_KEYWORD
)
2385 for (i
= 0; i
< count
; i
++)
2387 if (strlen(ippGetString(attr
, i
, NULL
)) > (size_t)upper
)
2391 match
= (i
== count
);
2394 case IPP_TAG_STRING
:
2395 if (upper
== INT_MAX
)
2398 for (i
= 0; i
< count
; i
++)
2400 int datalen
; // Length of octetString value
2402 ippGetOctetString(attr
, i
, &datalen
);
2404 if (datalen
> upper
)
2408 match
= (i
== count
);
2411 case IPP_TAG_INTEGER
:
2412 for (i
= 0; i
< count
; i
++)
2414 int value
= ippGetInteger(attr
, i
);
2417 if (value
< lower
|| value
> upper
)
2421 match
= (i
== count
);
2424 case IPP_TAG_RANGE
:
2425 for (i
= 0; i
< count
; i
++)
2427 int vupper
, vlower
= ippGetRange(attr
, i
, &vupper
);
2430 if (vlower
< lower
|| vlower
> upper
|| vupper
< lower
|| vupper
> upper
)
2434 match
= (i
== count
);
2438 // No other constraints, so this is a match
2445 * Restore the separators if we have them...
2460 * 'get_filename()' - Get a filename based on the current test file.
2463 static char * /* O - Filename */
2464 get_filename(const char *testfile
, /* I - Current test file */
2465 char *dst
, /* I - Destination filename */
2466 const char *src
, /* I - Source filename */
2467 size_t dstsize
) /* I - Size of destination buffer */
2469 char *dstptr
; /* Pointer into destination */
2470 _cups_globals_t
*cg
= _cupsGlobals();
2474 if (*src
== '<' && src
[strlen(src
) - 1] == '>')
2477 * Map <filename> to CUPS_DATADIR/ipptool/filename...
2480 snprintf(dst
, dstsize
, "%s/ipptool/%s", cg
->cups_datadir
, src
+ 1);
2481 dstptr
= dst
+ strlen(dst
) - 1;
2485 else if (!access(src
, R_OK
) || *src
== '/'
2487 || (isalpha(*src
& 255) && src
[1] == ':')
2492 * Use the path as-is...
2495 strlcpy(dst
, src
, dstsize
);
2500 * Make path relative to testfile...
2503 strlcpy(dst
, testfile
, dstsize
);
2504 if ((dstptr
= strrchr(dst
, '/')) != NULL
)
2507 dstptr
= dst
; /* Should never happen */
2509 strlcpy(dstptr
, src
, dstsize
- (size_t)(dstptr
- dst
));
2512 if (_access(dst
, 0))
2515 * Not available relative to the testfile, see if it can be found on the
2518 const char *userprofile
= getenv("USERPROFILE");
2519 /* User home directory */
2522 snprintf(dst
, dstsize
, "%s/Desktop/%s", userprofile
, src
);
2532 * 'get_string()' - Get a pointer to a string value or the portion of interest.
2535 static const char * /* O - Pointer to string */
2536 get_string(ipp_attribute_t
*attr
, /* I - IPP attribute */
2537 int element
, /* I - Element to fetch */
2538 int flags
, /* I - Value ("with") flags */
2539 char *buffer
, /* I - Temporary buffer */
2540 size_t bufsize
) /* I - Size of temporary buffer */
2542 const char *value
; /* Value */
2543 char *ptr
, /* Pointer into value */
2544 scheme
[256], /* URI scheme */
2545 userpass
[256], /* Username/password */
2546 hostname
[256], /* Hostname */
2547 resource
[1024]; /* Resource */
2548 int port
; /* Port number */
2551 value
= ippGetString(attr
, element
, NULL
);
2553 if (flags
& IPPTOOL_WITH_HOSTNAME
)
2555 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), buffer
, (int)bufsize
, &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
2558 ptr
= buffer
+ strlen(buffer
) - 1;
2559 if (ptr
>= buffer
&& *ptr
== '.')
2560 *ptr
= '\0'; /* Drop trailing "." */
2564 else if (flags
& IPPTOOL_WITH_RESOURCE
)
2566 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, buffer
, (int)bufsize
) < HTTP_URI_STATUS_OK
)
2571 else if (flags
& IPPTOOL_WITH_SCHEME
)
2573 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, buffer
, (int)bufsize
, userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
2578 else if (ippGetValueTag(attr
) == IPP_TAG_URI
&& (!strncmp(value
, "ipp://", 6) || !strncmp(value
, "http://", 7) || !strncmp(value
, "ipps://", 7) || !strncmp(value
, "https://", 8)))
2580 http_uri_status_t status
= httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
));
2582 if (status
< HTTP_URI_STATUS_OK
)
2593 * Normalize URI with no trailing dot...
2596 if ((ptr
= hostname
+ strlen(hostname
) - 1) >= hostname
&& *ptr
== '.')
2599 httpAssembleURI(HTTP_URI_CODING_ALL
, buffer
, (int)bufsize
, scheme
, userpass
, hostname
, port
, resource
);
2610 * 'init_data()' - Initialize test data.
2614 init_data(ipptool_test_t
*data
) /* I - Data */
2616 memset(data
, 0, sizeof(ipptool_test_t
));
2618 data
->output
= IPPTOOL_OUTPUT_LIST
;
2619 data
->outfile
= cupsFileStdout();
2620 data
->family
= AF_UNSPEC
;
2621 data
->def_transfer
= IPPTOOL_TRANSFER_AUTO
;
2622 data
->def_version
= 11;
2623 data
->errors
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
, (cups_afree_func_t
)free
);
2625 data
->prev_pass
= 1;
2626 data
->request_id
= (CUPS_RAND() % 1000) * 137;
2627 data
->show_header
= 1;
2632 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
2636 static char * /* O - ISO 8601 date/time string */
2637 iso_date(const ipp_uchar_t
*date
) /* I - IPP (RFC 1903) date/time value */
2639 time_t utctime
; /* UTC time since 1970 */
2640 struct tm utcdate
; /* UTC date/time */
2641 static char buffer
[255]; /* String buffer */
2644 utctime
= ippDateToTime(date
);
2645 gmtime_r(&utctime
, &utcdate
);
2647 snprintf(buffer
, sizeof(buffer
), "%04d-%02d-%02dT%02d:%02d:%02dZ",
2648 utcdate
.tm_year
+ 1900, utcdate
.tm_mon
+ 1, utcdate
.tm_mday
,
2649 utcdate
.tm_hour
, utcdate
.tm_min
, utcdate
.tm_sec
);
2656 * 'parse_monitor_printer_state()' - Parse the MONITOR-PRINTER-STATE directive.
2658 * MONITOR-PRINTER-STATE [printer-uri] {
2660 * EXPECT attribute-name ...
2664 static int /* O - 1 to continue, 0 to stop */
2665 parse_monitor_printer_state(
2666 _ipp_file_t
*f
, /* I - IPP file data */
2667 ipptool_test_t
*data
) /* I - Test data */
2669 char token
[256], /* Token string */
2670 name
[1024], /* Name string */
2671 temp
[1024], /* Temporary string */
2672 value
[1024], /* Value string */
2673 *ptr
; /* Pointer into value */
2676 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
2678 print_fatal_error(data
, "Missing printer URI on line %d of \"%s\".", f
->linenum
, f
->filename
);
2682 if (strcmp(temp
, "{"))
2684 // Got a printer URI so copy it...
2685 _ippVarsExpand(data
->vars
, value
, temp
, sizeof(value
));
2686 data
->monitor_uri
= strdup(value
);
2688 // Then see if we have an opening brace...
2689 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)) || strcmp(temp
, "{"))
2691 print_fatal_error(data
, "Missing opening brace on line %d of \"%s\".", f
->linenum
, f
->filename
);
2697 // Use the default printer URI...
2698 data
->monitor_uri
= strdup(data
->vars
->uri
);
2701 // Loop until we get a closing brace...
2702 while (_ippFileReadToken(f
, token
, sizeof(token
)))
2704 if (_cups_strcasecmp(token
, "COUNT") &&
2705 _cups_strcasecmp(token
, "DEFINE-MATCH") &&
2706 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
2707 _cups_strcasecmp(token
, "DEFINE-VALUE") &&
2708 _cups_strcasecmp(token
, "IF-DEFINED") &&
2709 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
2710 _cups_strcasecmp(token
, "IN-GROUP") &&
2711 _cups_strcasecmp(token
, "OF-TYPE") &&
2712 _cups_strcasecmp(token
, "WITH-DISTINCT-VALUES") &&
2713 _cups_strcasecmp(token
, "WITH-VALUE"))
2714 data
->last_expect
= NULL
;
2716 if (!strcmp(token
, "}"))
2718 else if (!_cups_strcasecmp(token
, "EXPECT"))
2721 * Expected attributes...
2724 if (data
->num_monitor_expects
>= (int)(sizeof(data
->monitor_expects
) / sizeof(data
->monitor_expects
[0])))
2726 print_fatal_error(data
, "Too many EXPECT's on line %d of \"%s\".", f
->linenum
, f
->filename
);
2730 if (!_ippFileReadToken(f
, name
, sizeof(name
)))
2732 print_fatal_error(data
, "Missing EXPECT name on line %d of \"%s\".", f
->linenum
, f
->filename
);
2736 data
->last_expect
= data
->monitor_expects
+ data
->num_monitor_expects
;
2737 data
->num_monitor_expects
++;
2739 memset(data
->last_expect
, 0, sizeof(ipptool_expect_t
));
2740 data
->last_expect
->repeat_limit
= 1000;
2744 data
->last_expect
->not_expect
= 1;
2745 data
->last_expect
->name
= strdup(name
+ 1);
2747 else if (name
[0] == '?')
2749 data
->last_expect
->optional
= 1;
2750 data
->last_expect
->name
= strdup(name
+ 1);
2753 data
->last_expect
->name
= strdup(name
);
2755 else if (!_cups_strcasecmp(token
, "COUNT"))
2757 int count
; /* Count value */
2759 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
2761 print_fatal_error(data
, "Missing COUNT number on line %d of \"%s\".", f
->linenum
, f
->filename
);
2765 if ((count
= atoi(temp
)) <= 0)
2767 print_fatal_error(data
, "Bad COUNT \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
2771 if (data
->last_expect
)
2773 data
->last_expect
->count
= count
;
2777 print_fatal_error(data
, "COUNT without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
2781 else if (!_cups_strcasecmp(token
, "DEFINE-MATCH"))
2783 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
2785 print_fatal_error(data
, "Missing DEFINE-MATCH variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
2789 if (data
->last_expect
)
2791 data
->last_expect
->define_match
= strdup(temp
);
2795 print_fatal_error(data
, "DEFINE-MATCH without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
2799 else if (!_cups_strcasecmp(token
, "DEFINE-NO-MATCH"))
2801 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
2803 print_fatal_error(data
, "Missing DEFINE-NO-MATCH variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
2807 if (data
->last_expect
)
2809 data
->last_expect
->define_no_match
= strdup(temp
);
2813 print_fatal_error(data
, "DEFINE-NO-MATCH without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
2817 else if (!_cups_strcasecmp(token
, "DEFINE-VALUE"))
2819 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
2821 print_fatal_error(data
, "Missing DEFINE-VALUE variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
2825 if (data
->last_expect
)
2827 data
->last_expect
->define_value
= strdup(temp
);
2831 print_fatal_error(data
, "DEFINE-VALUE without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
2835 else if (!_cups_strcasecmp(token
, "DELAY"))
2838 * Delay before operation...
2841 double dval
; /* Delay value */
2843 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
2845 print_fatal_error(data
, "Missing DELAY value on line %d of \"%s\".", f
->linenum
, f
->filename
);
2849 _ippVarsExpand(data
->vars
, value
, temp
, sizeof(value
));
2851 if ((dval
= _cupsStrScand(value
, &ptr
, localeconv())) < 0.0 || (*ptr
&& *ptr
!= ','))
2853 print_fatal_error(data
, "Bad DELAY value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
2857 data
->monitor_delay
= (useconds_t
)(1000000.0 * dval
);
2861 if ((dval
= _cupsStrScand(ptr
+ 1, &ptr
, localeconv())) <= 0.0 || *ptr
)
2863 print_fatal_error(data
, "Bad DELAY value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
2867 data
->monitor_interval
= (useconds_t
)(1000000.0 * dval
);
2870 data
->monitor_interval
= data
->monitor_delay
;
2872 else if (!_cups_strcasecmp(token
, "OF-TYPE"))
2874 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
2876 print_fatal_error(data
, "Missing OF-TYPE value tag(s) on line %d of \"%s\".", f
->linenum
, f
->filename
);
2880 if (data
->last_expect
)
2882 data
->last_expect
->of_type
= strdup(temp
);
2886 print_fatal_error(data
, "OF-TYPE without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
2890 else if (!_cups_strcasecmp(token
, "IN-GROUP"))
2892 ipp_tag_t in_group
; /* IN-GROUP value */
2894 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
2896 print_fatal_error(data
, "Missing IN-GROUP group tag on line %d of \"%s\".", f
->linenum
, f
->filename
);
2900 if ((in_group
= ippTagValue(temp
)) == IPP_TAG_ZERO
|| in_group
>= IPP_TAG_UNSUPPORTED_VALUE
)
2902 print_fatal_error(data
, "Bad IN-GROUP group tag \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
2905 else if (data
->last_expect
)
2907 data
->last_expect
->in_group
= in_group
;
2911 print_fatal_error(data
, "IN-GROUP without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
2915 else if (!_cups_strcasecmp(token
, "IF-DEFINED"))
2917 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
2919 print_fatal_error(data
, "Missing IF-DEFINED name on line %d of \"%s\".", f
->linenum
, f
->filename
);
2923 if (data
->last_expect
)
2925 data
->last_expect
->if_defined
= strdup(temp
);
2929 print_fatal_error(data
, "IF-DEFINED without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
2933 else if (!_cups_strcasecmp(token
, "IF-NOT-DEFINED"))
2935 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
2937 print_fatal_error(data
, "Missing IF-NOT-DEFINED name on line %d of \"%s\".", f
->linenum
, f
->filename
);
2941 if (data
->last_expect
)
2943 data
->last_expect
->if_not_defined
= strdup(temp
);
2947 print_fatal_error(data
, "IF-NOT-DEFINED without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
2951 else if (!_cups_strcasecmp(token
, "WITH-DISTINCT-VALUES"))
2953 if (data
->last_expect
)
2955 data
->last_expect
->with_distinct
= 1;
2959 print_fatal_error(data
, "%s without a preceding EXPECT on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
2963 else if (!_cups_strcasecmp(token
, "WITH-VALUE"))
2965 off_t lastpos
; /* Last file position */
2966 int lastline
; /* Last line number */
2968 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
2970 print_fatal_error(data
, "Missing %s value on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
2975 * Read additional comma-delimited values - needed since legacy test files
2976 * will have unquoted WITH-VALUE values with commas...
2979 ptr
= temp
+ strlen(temp
);
2983 lastpos
= cupsFileTell(f
->fp
);
2984 lastline
= f
->linenum
;
2987 if (!_ippFileReadToken(f
, ptr
, (sizeof(temp
) - (size_t)(ptr
- temp
))))
2990 if (!strcmp(ptr
, ","))
2998 if (!_ippFileReadToken(f
, ptr
, (sizeof(temp
) - (size_t)(ptr
- temp
))))
3004 * Not another value, stop here...
3007 cupsFileSeek(f
->fp
, lastpos
);
3008 f
->linenum
= lastline
;
3014 if (data
->last_expect
)
3017 * Expand any variables in the value and then save it.
3020 _ippVarsExpand(data
->vars
, value
, temp
, sizeof(value
));
3022 ptr
= value
+ strlen(value
) - 1;
3024 if (value
[0] == '/' && ptr
> value
&& *ptr
== '/')
3027 * WITH-VALUE is a POSIX extended regular expression.
3030 data
->last_expect
->with_value
= calloc(1, (size_t)(ptr
- value
));
3031 data
->last_expect
->with_flags
|= IPPTOOL_WITH_REGEX
;
3033 if (data
->last_expect
->with_value
)
3034 memcpy(data
->last_expect
->with_value
, value
+ 1, (size_t)(ptr
- value
- 1));
3039 * WITH-VALUE is a literal value...
3042 for (ptr
= value
; *ptr
; ptr
++)
3044 if (*ptr
== '\\' && ptr
[1])
3047 * Remove \ from \foo...
3050 _cups_strcpy(ptr
, ptr
+ 1);
3054 data
->last_expect
->with_value
= strdup(value
);
3055 data
->last_expect
->with_flags
|= IPPTOOL_WITH_LITERAL
;
3060 print_fatal_error(data
, "%s without a preceding EXPECT on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
3066 print_fatal_error(data
, "Missing closing brace on line %d of \"%s\".", f
->linenum
, f
->filename
);
3073 * 'pause_message()' - Display the message and pause until the user presses a key.
3077 pause_message(const char *message
) /* I - Message */
3080 HANDLE tty
; /* Console handle */
3081 DWORD mode
; /* Console mode */
3082 char key
; /* Key press */
3083 DWORD bytes
; /* Bytes read for key press */
3087 * Disable input echo and set raw input...
3090 if ((tty
= GetStdHandle(STD_INPUT_HANDLE
)) == INVALID_HANDLE_VALUE
)
3093 if (!GetConsoleMode(tty
, &mode
))
3096 if (!SetConsoleMode(tty
, 0))
3100 int tty
; /* /dev/tty - never read from stdin */
3101 struct termios original
, /* Original input mode */
3102 noecho
; /* No echo input mode */
3103 char key
; /* Current key press */
3107 * Disable input echo and set raw input...
3110 if ((tty
= open("/dev/tty", O_RDONLY
)) < 0)
3113 if (tcgetattr(tty
, &original
))
3120 noecho
.c_lflag
&= (tcflag_t
)~(ICANON
| ECHO
| ECHOE
| ISIG
);
3122 if (tcsetattr(tty
, TCSAFLUSH
, &noecho
))
3130 * Display the prompt...
3133 cupsFilePrintf(cupsFileStdout(), "%s\n---- PRESS ANY KEY ----", message
);
3140 ReadFile(tty
, &key
, 1, &bytes
, NULL
);
3146 SetConsoleMode(tty
, mode
);
3159 tcsetattr(tty
, TCSAFLUSH
, &original
);
3164 * Erase the "press any key" prompt...
3167 cupsFilePuts(cupsFileStdout(), "\r \r");
3172 * 'print_attr()' - Print an attribute on the screen.
3176 print_attr(cups_file_t
*outfile
, /* I - Output file */
3177 ipptool_output_t output
, /* I - Output format */
3178 ipp_attribute_t
*attr
, /* I - Attribute to print */
3179 ipp_tag_t
*group
) /* IO - Current group */
3181 int i
, /* Looping var */
3182 count
; /* Number of values */
3183 ipp_attribute_t
*colattr
; /* Collection attribute */
3186 if (output
== IPPTOOL_OUTPUT_PLIST
)
3188 if (!ippGetName(attr
) || (group
&& *group
!= ippGetGroupTag(attr
)))
3190 if (ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
3192 cupsFilePuts(outfile
, "</dict>\n");
3193 cupsFilePuts(outfile
, "<dict>\n");
3197 *group
= ippGetGroupTag(attr
);
3200 if (!ippGetName(attr
))
3203 print_xml_string(outfile
, "key", ippGetName(attr
));
3204 if ((count
= ippGetCount(attr
)) > 1)
3205 cupsFilePuts(outfile
, "<array>\n");
3207 switch (ippGetValueTag(attr
))
3209 case IPP_TAG_INTEGER
:
3211 for (i
= 0; i
< count
; i
++)
3212 cupsFilePrintf(outfile
, "<integer>%d</integer>\n", ippGetInteger(attr
, i
));
3215 case IPP_TAG_BOOLEAN
:
3216 for (i
= 0; i
< count
; i
++)
3217 cupsFilePuts(outfile
, ippGetBoolean(attr
, i
) ? "<true />\n" : "<false />\n");
3220 case IPP_TAG_RANGE
:
3221 for (i
= 0; i
< count
; i
++)
3223 int lower
, upper
; /* Lower and upper ranges */
3225 lower
= ippGetRange(attr
, i
, &upper
);
3226 cupsFilePrintf(outfile
, "<dict><key>lower</key><integer>%d</integer><key>upper</key><integer>%d</integer></dict>\n", lower
, upper
);
3230 case IPP_TAG_RESOLUTION
:
3231 for (i
= 0; i
< count
; i
++)
3233 int xres
, yres
; /* Resolution values */
3234 ipp_res_t units
; /* Resolution units */
3236 xres
= ippGetResolution(attr
, i
, &yres
, &units
);
3237 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");
3242 for (i
= 0; i
< count
; i
++)
3243 cupsFilePrintf(outfile
, "<date>%s</date>\n", iso_date(ippGetDate(attr
, i
)));
3246 case IPP_TAG_STRING
:
3247 for (i
= 0; i
< count
; i
++)
3249 int datalen
; /* Length of data */
3250 void *data
= ippGetOctetString(attr
, i
, &datalen
);
3252 char buffer
[IPP_MAX_LENGTH
* 5 / 4 + 1];
3253 /* Base64 output buffer */
3255 cupsFilePrintf(outfile
, "<data>%s</data>\n", httpEncode64_2(buffer
, sizeof(buffer
), data
, datalen
));
3261 case IPP_TAG_KEYWORD
:
3263 case IPP_TAG_URISCHEME
:
3264 case IPP_TAG_CHARSET
:
3265 case IPP_TAG_LANGUAGE
:
3266 case IPP_TAG_MIMETYPE
:
3267 for (i
= 0; i
< count
; i
++)
3268 print_xml_string(outfile
, "string", ippGetString(attr
, i
, NULL
));
3271 case IPP_TAG_TEXTLANG
:
3272 case IPP_TAG_NAMELANG
:
3273 for (i
= 0; i
< count
; i
++)
3275 const char *s
, /* String */
3276 *lang
; /* Language */
3278 s
= ippGetString(attr
, i
, &lang
);
3279 cupsFilePuts(outfile
, "<dict><key>language</key><string>");
3280 print_xml_string(outfile
, NULL
, lang
);
3281 cupsFilePuts(outfile
, "</string><key>string</key><string>");
3282 print_xml_string(outfile
, NULL
, s
);
3283 cupsFilePuts(outfile
, "</string></dict>\n");
3287 case IPP_TAG_BEGIN_COLLECTION
:
3288 for (i
= 0; i
< count
; i
++)
3290 ipp_t
*col
= ippGetCollection(attr
, i
);
3291 /* Collection value */
3293 cupsFilePuts(outfile
, "<dict>\n");
3294 for (colattr
= ippFirstAttribute(col
); colattr
; colattr
= ippNextAttribute(col
))
3295 print_attr(outfile
, output
, colattr
, NULL
);
3296 cupsFilePuts(outfile
, "</dict>\n");
3301 cupsFilePrintf(outfile
, "<string><<%s>></string>\n", ippTagString(ippGetValueTag(attr
)));
3306 cupsFilePuts(outfile
, "</array>\n");
3310 char buffer
[131072]; /* Value buffer */
3312 if (output
== IPPTOOL_OUTPUT_TEST
)
3314 if (!ippGetName(attr
))
3316 cupsFilePuts(outfile
, " -- separator --\n");
3320 cupsFilePrintf(outfile
, " %s (%s%s) = ", ippGetName(attr
), ippGetCount(attr
) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr
)));
3323 ippAttributeString(attr
, buffer
, sizeof(buffer
));
3324 cupsFilePrintf(outfile
, "%s\n", buffer
);
3330 * 'print_csv()' - Print a line of CSV text.
3333 static ipp_attribute_t
* /* O - Next attribute */
3335 ipptool_test_t
*data
, /* I - Test data */
3336 ipp_t
*ipp
, /* I - Response message */
3337 ipp_attribute_t
*attr
, /* I - First attribute for line */
3338 int num_displayed
, /* I - Number of attributes to display */
3339 char **displayed
, /* I - Attributes to display */
3340 size_t *widths
) /* I - Column widths */
3342 int i
; /* Looping var */
3343 size_t maxlength
; /* Max length of all columns */
3344 ipp_attribute_t
*current
= attr
; /* Current attribute */
3345 char *values
[MAX_DISPLAY
], /* Strings to display */
3346 *valptr
; /* Pointer into value */
3349 * Get the maximum string length we have to show and allocate...
3352 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
3353 if (widths
[i
] > maxlength
)
3354 maxlength
= widths
[i
];
3359 * Loop through the attributes to display...
3364 // Collect the values...
3365 memset(values
, 0, sizeof(values
));
3367 for (; current
; current
= ippNextAttribute(ipp
))
3369 if (!ippGetName(current
))
3372 for (i
= 0; i
< num_displayed
; i
++)
3374 if (!strcmp(ippGetName(current
), displayed
[i
]))
3376 if ((values
[i
] = (char *)calloc(1, maxlength
)) != NULL
)
3377 ippAttributeString(current
, values
[i
], maxlength
);
3383 // Output the line...
3384 for (i
= 0; i
< num_displayed
; i
++)
3387 cupsFilePutChar(data
->outfile
, ',');
3392 if (strchr(values
[i
], ',') != NULL
|| strchr(values
[i
], '\"') != NULL
|| strchr(values
[i
], '\\') != NULL
)
3395 cupsFilePutChar(data
->outfile
, '\"');
3396 for (valptr
= values
[i
]; *valptr
; valptr
++)
3398 if (*valptr
== '\\' || *valptr
== '\"')
3399 cupsFilePutChar(data
->outfile
, '\\');
3400 cupsFilePutChar(data
->outfile
, *valptr
);
3402 cupsFilePutChar(data
->outfile
, '\"');
3406 // Unquoted value...
3407 cupsFilePuts(data
->outfile
, values
[i
]);
3412 cupsFilePutChar(data
->outfile
, '\n');
3416 // Show column headings...
3417 for (i
= 0; i
< num_displayed
; i
++)
3420 cupsFilePutChar(data
->outfile
, ',');
3422 cupsFilePuts(data
->outfile
, displayed
[i
]);
3424 cupsFilePutChar(data
->outfile
, '\n');
3432 * 'print_fatal_error()' - Print a fatal error message.
3437 ipptool_test_t
*data
, /* I - Test data */
3438 const char *s
, /* I - Printf-style format string */
3439 ...) /* I - Additional arguments as needed */
3441 char buffer
[10240]; /* Format buffer */
3442 va_list ap
; /* Pointer to arguments */
3446 * Format the error message...
3450 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
3457 if (data
->output
== IPPTOOL_OUTPUT_PLIST
)
3459 print_xml_header(data
);
3460 print_xml_trailer(data
, 0, buffer
);
3463 _cupsLangPrintf(stderr
, "ipptool: %s", buffer
);
3468 * 'print_ippserver_attr()' - Print a attribute suitable for use by ippserver.
3472 print_ippserver_attr(
3473 ipptool_test_t
*data
, /* I - Test data */
3474 ipp_attribute_t
*attr
, /* I - Attribute to print */
3475 int indent
) /* I - Indentation level */
3477 int i
, /* Looping var */
3478 count
= ippGetCount(attr
);
3479 /* Number of values */
3480 ipp_attribute_t
*colattr
; /* Collection attribute */
3484 cupsFilePrintf(data
->outfile
, "ATTR %s %s", ippTagString(ippGetValueTag(attr
)), ippGetName(attr
));
3486 cupsFilePrintf(data
->outfile
, "%*sMEMBER %s %s", indent
, "", ippTagString(ippGetValueTag(attr
)), ippGetName(attr
));
3488 switch (ippGetValueTag(attr
))
3490 case IPP_TAG_INTEGER
:
3492 for (i
= 0; i
< count
; i
++)
3493 cupsFilePrintf(data
->outfile
, "%s%d", i
? "," : " ", ippGetInteger(attr
, i
));
3496 case IPP_TAG_BOOLEAN
:
3497 cupsFilePuts(data
->outfile
, ippGetBoolean(attr
, 0) ? " true" : " false");
3499 for (i
= 1; i
< count
; i
++)
3500 cupsFilePuts(data
->outfile
, ippGetBoolean(attr
, 1) ? ",true" : ",false");
3503 case IPP_TAG_RANGE
:
3504 for (i
= 0; i
< count
; i
++)
3506 int upper
, lower
= ippGetRange(attr
, i
, &upper
);
3508 cupsFilePrintf(data
->outfile
, "%s%d-%d", i
? "," : " ", lower
, upper
);
3512 case IPP_TAG_RESOLUTION
:
3513 for (i
= 0; i
< count
; i
++)
3516 int yres
, xres
= ippGetResolution(attr
, i
, &yres
, &units
);
3518 cupsFilePrintf(data
->outfile
, "%s%dx%d%s", i
? "," : " ", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
3523 for (i
= 0; i
< count
; i
++)
3524 cupsFilePrintf(data
->outfile
, "%s%s", i
? "," : " ", iso_date(ippGetDate(attr
, i
)));
3527 case IPP_TAG_STRING
:
3528 for (i
= 0; i
< count
; i
++)
3531 const char *s
= (const char *)ippGetOctetString(attr
, i
, &len
);
3533 cupsFilePuts(data
->outfile
, i
? "," : " ");
3534 print_ippserver_string(data
, s
, (size_t)len
);
3539 case IPP_TAG_TEXTLANG
:
3541 case IPP_TAG_NAMELANG
:
3542 case IPP_TAG_KEYWORD
:
3544 case IPP_TAG_URISCHEME
:
3545 case IPP_TAG_CHARSET
:
3546 case IPP_TAG_LANGUAGE
:
3547 case IPP_TAG_MIMETYPE
:
3548 for (i
= 0; i
< count
; i
++)
3550 const char *s
= ippGetString(attr
, i
, NULL
);
3552 cupsFilePuts(data
->outfile
, i
? "," : " ");
3553 print_ippserver_string(data
, s
, strlen(s
));
3557 case IPP_TAG_BEGIN_COLLECTION
:
3558 for (i
= 0; i
< count
; i
++)
3560 ipp_t
*col
= ippGetCollection(attr
, i
);
3562 cupsFilePuts(data
->outfile
, i
? ",{\n" : " {\n");
3563 for (colattr
= ippFirstAttribute(col
); colattr
; colattr
= ippNextAttribute(col
))
3564 print_ippserver_attr(data
, colattr
, indent
+ 4);
3565 cupsFilePrintf(data
->outfile
, "%*s}", indent
, "");
3570 /* Out-of-band value */
3574 cupsFilePuts(data
->outfile
, "\n");
3579 * 'print_ippserver_string()' - Print a string suitable for use by ippserver.
3583 print_ippserver_string(
3584 ipptool_test_t
*data
, /* I - Test data */
3585 const char *s
, /* I - String to print */
3586 size_t len
) /* I - Length of string */
3588 cupsFilePutChar(data
->outfile
, '\"');
3592 cupsFilePutChar(data
->outfile
, '\\');
3593 cupsFilePutChar(data
->outfile
, *s
);
3598 cupsFilePutChar(data
->outfile
, '\"');
3603 * 'print_line()' - Print a line of formatted or CSV text.
3606 static ipp_attribute_t
* /* O - Next attribute */
3608 ipptool_test_t
*data
, /* I - Test data */
3609 ipp_t
*ipp
, /* I - Response message */
3610 ipp_attribute_t
*attr
, /* I - First attribute for line */
3611 int num_displayed
, /* I - Number of attributes to display */
3612 char **displayed
, /* I - Attributes to display */
3613 size_t *widths
) /* I - Column widths */
3615 int i
; /* Looping var */
3616 size_t maxlength
; /* Max length of all columns */
3617 ipp_attribute_t
*current
= attr
; /* Current attribute */
3618 char *values
[MAX_DISPLAY
]; /* Strings to display */
3622 * Get the maximum string length we have to show and allocate...
3625 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
3626 if (widths
[i
] > maxlength
)
3627 maxlength
= widths
[i
];
3632 * Loop through the attributes to display...
3637 // Collect the values...
3638 memset(values
, 0, sizeof(values
));
3640 for (; current
; current
= ippNextAttribute(ipp
))
3642 if (!ippGetName(current
))
3645 for (i
= 0; i
< num_displayed
; i
++)
3647 if (!strcmp(ippGetName(current
), displayed
[i
]))
3649 if ((values
[i
] = (char *)calloc(1, maxlength
)) != NULL
)
3650 ippAttributeString(current
, values
[i
], maxlength
);
3656 // Output the line...
3657 for (i
= 0; i
< num_displayed
; i
++)
3660 cupsFilePutChar(data
->outfile
, ' ');
3662 cupsFilePrintf(data
->outfile
, "%*s", (int)-widths
[i
], values
[i
] ? values
[i
] : "");
3665 cupsFilePutChar(data
->outfile
, '\n');
3669 // Show column headings...
3670 char *buffer
= (char *)malloc(maxlength
);
3671 // Buffer for separator lines
3676 for (i
= 0; i
< num_displayed
; i
++)
3679 cupsFilePutChar(data
->outfile
, ' ');
3681 cupsFilePrintf(data
->outfile
, "%*s", (int)-widths
[i
], displayed
[i
]);
3683 cupsFilePutChar(data
->outfile
, '\n');
3685 for (i
= 0; i
< num_displayed
; i
++)
3688 cupsFilePutChar(data
->outfile
, ' ');
3690 memset(buffer
, '-', widths
[i
]);
3691 buffer
[widths
[i
]] = '\0';
3692 cupsFilePuts(data
->outfile
, buffer
);
3694 cupsFilePutChar(data
->outfile
, '\n');
3703 * 'print_xml_header()' - Print a standard XML plist header.
3707 print_xml_header(ipptool_test_t
*data
)/* I - Test data */
3709 if (!data
->xml_header
)
3711 cupsFilePuts(data
->outfile
, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
3712 cupsFilePuts(data
->outfile
, "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n");
3713 cupsFilePuts(data
->outfile
, "<plist version=\"1.0\">\n");
3714 cupsFilePuts(data
->outfile
, "<dict>\n");
3715 cupsFilePuts(data
->outfile
, "<key>ipptoolVersion</key>\n");
3716 cupsFilePuts(data
->outfile
, "<string>" CUPS_SVERSION
"</string>\n");
3717 cupsFilePuts(data
->outfile
, "<key>Transfer</key>\n");
3718 cupsFilePrintf(data
->outfile
, "<string>%s</string>\n", data
->transfer
== IPPTOOL_TRANSFER_AUTO
? "auto" : data
->transfer
== IPPTOOL_TRANSFER_CHUNKED
? "chunked" : "length");
3719 cupsFilePuts(data
->outfile
, "<key>Tests</key>\n");
3720 cupsFilePuts(data
->outfile
, "<array>\n");
3722 data
->xml_header
= 1;
3728 * 'print_xml_string()' - Print an XML string with escaping.
3732 print_xml_string(cups_file_t
*outfile
, /* I - Test data */
3733 const char *element
, /* I - Element name or NULL */
3734 const char *s
) /* I - String to print */
3737 cupsFilePrintf(outfile
, "<%s>", element
);
3742 cupsFilePuts(outfile
, "&");
3744 cupsFilePuts(outfile
, "<");
3746 cupsFilePuts(outfile
, ">");
3747 else if ((*s
& 0xe0) == 0xc0)
3750 * Validate UTF-8 two-byte sequence...
3753 if ((s
[1] & 0xc0) != 0x80)
3755 cupsFilePutChar(outfile
, '?');
3760 cupsFilePutChar(outfile
, *s
++);
3761 cupsFilePutChar(outfile
, *s
);
3764 else if ((*s
& 0xf0) == 0xe0)
3767 * Validate UTF-8 three-byte sequence...
3770 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80)
3772 cupsFilePutChar(outfile
, '?');
3777 cupsFilePutChar(outfile
, *s
++);
3778 cupsFilePutChar(outfile
, *s
++);
3779 cupsFilePutChar(outfile
, *s
);
3782 else if ((*s
& 0xf8) == 0xf0)
3785 * Validate UTF-8 four-byte sequence...
3788 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80 ||
3789 (s
[3] & 0xc0) != 0x80)
3791 cupsFilePutChar(outfile
, '?');
3796 cupsFilePutChar(outfile
, *s
++);
3797 cupsFilePutChar(outfile
, *s
++);
3798 cupsFilePutChar(outfile
, *s
++);
3799 cupsFilePutChar(outfile
, *s
);
3802 else if ((*s
& 0x80) || (*s
< ' ' && !isspace(*s
& 255)))
3805 * Invalid control character...
3808 cupsFilePutChar(outfile
, '?');
3811 cupsFilePutChar(outfile
, *s
);
3817 cupsFilePrintf(outfile
, "</%s>\n", element
);
3822 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
3827 ipptool_test_t
*data
, /* I - Test data */
3828 int success
, /* I - 1 on success, 0 on failure */
3829 const char *message
) /* I - Error message or NULL */
3831 if (data
->xml_header
)
3833 cupsFilePuts(data
->outfile
, "</array>\n");
3834 cupsFilePuts(data
->outfile
, "<key>Successful</key>\n");
3835 cupsFilePuts(data
->outfile
, success
? "<true />\n" : "<false />\n");
3838 cupsFilePuts(data
->outfile
, "<key>ErrorMessage</key>\n");
3839 print_xml_string(data
->outfile
, "string", message
);
3841 cupsFilePuts(data
->outfile
, "</dict>\n");
3842 cupsFilePuts(data
->outfile
, "</plist>\n");
3844 data
->xml_header
= 0;
3851 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
3855 sigterm_handler(int sig
) /* I - Signal number (unused) */
3861 signal(SIGINT
, SIG_DFL
);
3862 signal(SIGTERM
, SIG_DFL
);
3864 #endif /* !_WIN32 */
3868 * 'timeout_cb()' - Handle HTTP timeouts.
3871 static int /* O - 1 to continue, 0 to cancel */
3872 timeout_cb(http_t
*http
, /* I - Connection to server */
3873 void *user_data
) /* I - User data (unused) */
3875 int buffered
= 0; /* Bytes buffered but not yet sent */
3881 * If the socket still have data waiting to be sent to the printer (as can
3882 * happen if the printer runs out of paper), continue to wait until the output
3883 * buffer is empty...
3886 #ifdef SO_NWRITE /* macOS and some versions of Linux */
3887 socklen_t len
= sizeof(buffered
); /* Size of return value */
3889 if (getsockopt(httpGetFd(http
), SOL_SOCKET
, SO_NWRITE
, &buffered
, &len
))
3892 #elif defined(SIOCOUTQ) /* Others except Windows */
3893 if (ioctl(httpGetFd(http
), SIOCOUTQ
, &buffered
))
3896 #else /* Windows (not possible) */
3898 #endif /* SO_NWRITE */
3900 return (buffered
> 0);
3905 * 'token_cb()' - Parse test file-specific tokens and run tests.
3908 static int /* O - 1 to continue, 0 to stop */
3909 token_cb(_ipp_file_t
*f
, /* I - IPP file data */
3910 _ipp_vars_t
*vars
, /* I - IPP variables */
3911 ipptool_test_t
*data
, /* I - Test data */
3912 const char *token
) /* I - Current token */
3914 char name
[1024], /* Name string */
3915 temp
[1024], /* Temporary string */
3916 value
[1024], /* Value string */
3917 *ptr
; /* Pointer into value */
3923 * Initialize state as needed (nothing for now...)
3931 * Parse until we see a close brace...
3934 if (_cups_strcasecmp(token
, "COUNT") &&
3935 _cups_strcasecmp(token
, "DEFINE-MATCH") &&
3936 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
3937 _cups_strcasecmp(token
, "DEFINE-VALUE") &&
3938 _cups_strcasecmp(token
, "IF-DEFINED") &&
3939 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
3940 _cups_strcasecmp(token
, "IN-GROUP") &&
3941 _cups_strcasecmp(token
, "OF-TYPE") &&
3942 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
3943 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
3944 _cups_strcasecmp(token
, "REPEAT-NO-MATCH") &&
3945 _cups_strcasecmp(token
, "SAME-COUNT-AS") &&
3946 _cups_strcasecmp(token
, "WITH-ALL-VALUES") &&
3947 _cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") &&
3948 _cups_strcasecmp(token
, "WITH-ALL-RESOURCES") &&
3949 _cups_strcasecmp(token
, "WITH-ALL-SCHEMES") &&
3950 _cups_strcasecmp(token
, "WITH-DISTINCT-VALUES") &&
3951 _cups_strcasecmp(token
, "WITH-HOSTNAME") &&
3952 _cups_strcasecmp(token
, "WITH-RESOURCE") &&
3953 _cups_strcasecmp(token
, "WITH-SCHEME") &&
3954 _cups_strcasecmp(token
, "WITH-VALUE") &&
3955 _cups_strcasecmp(token
, "WITH-VALUE-FROM"))
3956 data
->last_expect
= NULL
;
3958 if (_cups_strcasecmp(token
, "DEFINE-MATCH") &&
3959 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
3960 _cups_strcasecmp(token
, "IF-DEFINED") &&
3961 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
3962 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
3963 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
3964 _cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
3965 data
->last_status
= NULL
;
3967 if (!strcmp(token
, "}"))
3969 return (do_test(f
, data
));
3971 else if (!strcmp(token
, "MONITOR-PRINTER-STATE"))
3973 if (data
->monitor_uri
)
3975 print_fatal_error(data
, "Extra MONITOR-PRINTER-STATE seen on line %d of \"%s\".", f
->linenum
, f
->filename
);
3979 return (parse_monitor_printer_state(f
, data
));
3981 else if (!strcmp(token
, "COMPRESSION"))
3985 * COMPRESSION deflate
3989 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3991 _ippVarsExpand(vars
, data
->compression
, temp
, sizeof(data
->compression
));
3993 if (strcmp(data
->compression
, "none") && strcmp(data
->compression
, "deflate") &&
3994 strcmp(data
->compression
, "gzip"))
3996 if (strcmp(data
->compression
, "none"))
3997 #endif /* HAVE_LIBZ */
3999 print_fatal_error(data
, "Unsupported COMPRESSION value \"%s\" on line %d of \"%s\".", data
->compression
, f
->linenum
, f
->filename
);
4003 if (!strcmp(data
->compression
, "none"))
4004 data
->compression
[0] = '\0';
4008 print_fatal_error(data
, "Missing COMPRESSION value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4012 else if (!strcmp(token
, "DEFINE"))
4018 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
4020 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
4021 _ippVarsSet(vars
, name
, value
);
4025 print_fatal_error(data
, "Missing DEFINE name and/or value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4029 else if (!strcmp(token
, "IGNORE-ERRORS"))
4036 if (_ippFileReadToken(f
, temp
, sizeof(temp
)) && (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
4038 data
->ignore_errors
= !_cups_strcasecmp(temp
, "yes");
4042 print_fatal_error(data
, "Missing IGNORE-ERRORS value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4046 else if (!_cups_strcasecmp(token
, "NAME"))
4052 _ippFileReadToken(f
, temp
, sizeof(temp
));
4053 _ippVarsExpand(vars
, data
->name
, temp
, sizeof(data
->name
));
4055 else if (!_cups_strcasecmp(token
, "PAUSE"))
4058 * Pause with a message...
4061 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
4063 strlcpy(data
->pause
, temp
, sizeof(data
->pause
));
4067 print_fatal_error(data
, "Missing PAUSE message on line %d of \"%s\".", f
->linenum
, f
->filename
);
4071 else if (!strcmp(token
, "REQUEST-ID"))
4078 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
4080 if (isdigit(temp
[0] & 255))
4082 data
->request_id
= atoi(temp
) - 1;
4084 else if (!_cups_strcasecmp(temp
, "random"))
4086 data
->request_id
= (CUPS_RAND() % 1000) * 137;
4090 print_fatal_error(data
, "Bad REQUEST-ID value \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
4096 print_fatal_error(data
, "Missing REQUEST-ID value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4100 else if (!strcmp(token
, "PASS-IF-DEFINED"))
4103 * PASS-IF-DEFINED variable
4106 if (_ippFileReadToken(f
, name
, sizeof(name
)))
4108 if (_ippVarsGet(vars
, name
))
4109 data
->pass_test
= 1;
4113 print_fatal_error(data
, "Missing PASS-IF-DEFINED value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4117 else if (!strcmp(token
, "PASS-IF-NOT-DEFINED"))
4120 * PASS-IF-NOT-DEFINED variable
4123 if (_ippFileReadToken(f
, name
, sizeof(name
)))
4125 if (!_ippVarsGet(vars
, name
))
4126 data
->pass_test
= 1;
4130 print_fatal_error(data
, "Missing PASS-IF-NOT-DEFINED value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4134 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
4137 * SKIP-IF-DEFINED variable
4140 if (_ippFileReadToken(f
, name
, sizeof(name
)))
4142 if (_ippVarsGet(vars
, name
))
4143 data
->skip_test
= 1;
4147 print_fatal_error(data
, "Missing SKIP-IF-DEFINED value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4151 else if (!strcmp(token
, "SKIP-IF-MISSING"))
4154 * SKIP-IF-MISSING filename
4157 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
4159 char filename
[1024]; /* Filename */
4161 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
4162 get_filename(f
->filename
, filename
, temp
, sizeof(filename
));
4164 if (access(filename
, R_OK
))
4165 data
->skip_test
= 1;
4169 print_fatal_error(data
, "Missing SKIP-IF-MISSING filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
4173 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
4176 * SKIP-IF-NOT-DEFINED variable
4179 if (_ippFileReadToken(f
, name
, sizeof(name
)))
4181 if (!_ippVarsGet(vars
, name
))
4182 data
->skip_test
= 1;
4186 print_fatal_error(data
, "Missing SKIP-IF-NOT-DEFINED value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4190 else if (!strcmp(token
, "SKIP-PREVIOUS-ERROR"))
4193 * SKIP-PREVIOUS-ERROR yes
4194 * SKIP-PREVIOUS-ERROR no
4197 if (_ippFileReadToken(f
, temp
, sizeof(temp
)) && (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
4199 data
->skip_previous
= !_cups_strcasecmp(temp
, "yes");
4203 print_fatal_error(data
, "Missing SKIP-PREVIOUS-ERROR value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4207 else if (!strcmp(token
, "TEST-ID"))
4213 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
4215 _ippVarsExpand(vars
, data
->test_id
, temp
, sizeof(data
->test_id
));
4219 print_fatal_error(data
, "Missing TEST-ID value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4223 else if (!strcmp(token
, "TRANSFER"))
4231 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
4233 if (!strcmp(temp
, "auto"))
4235 data
->transfer
= IPPTOOL_TRANSFER_AUTO
;
4237 else if (!strcmp(temp
, "chunked"))
4239 data
->transfer
= IPPTOOL_TRANSFER_CHUNKED
;
4241 else if (!strcmp(temp
, "length"))
4243 data
->transfer
= IPPTOOL_TRANSFER_LENGTH
;
4247 print_fatal_error(data
, "Bad TRANSFER value \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
4253 print_fatal_error(data
, "Missing TRANSFER value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4257 else if (!_cups_strcasecmp(token
, "VERSION"))
4259 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
4261 if (!strcmp(temp
, "0.0"))
4265 else if (!strcmp(temp
, "1.0"))
4269 else if (!strcmp(temp
, "1.1"))
4273 else if (!strcmp(temp
, "2.0"))
4277 else if (!strcmp(temp
, "2.1"))
4281 else if (!strcmp(temp
, "2.2"))
4287 print_fatal_error(data
, "Bad VERSION \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
4293 print_fatal_error(data
, "Missing VERSION number on line %d of \"%s\".", f
->linenum
, f
->filename
);
4297 else if (!_cups_strcasecmp(token
, "RESOURCE"))
4303 if (!_ippFileReadToken(f
, data
->resource
, sizeof(data
->resource
)))
4305 print_fatal_error(data
, "Missing RESOURCE path on line %d of \"%s\".", f
->linenum
, f
->filename
);
4309 else if (!_cups_strcasecmp(token
, "OPERATION"))
4315 ipp_op_t op
; /* Operation code */
4317 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4319 print_fatal_error(data
, "Missing OPERATION code on line %d of \"%s\".", f
->linenum
, f
->filename
);
4323 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
4325 if ((op
= ippOpValue(value
)) == (ipp_op_t
)-1 && (op
= (ipp_op_t
)strtol(value
, NULL
, 0)) == 0)
4327 print_fatal_error(data
, "Bad OPERATION code \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
4331 ippSetOperation(f
->attrs
, op
);
4333 else if (!_cups_strcasecmp(token
, "GROUP"))
4336 * Attribute group...
4339 ipp_tag_t group_tag
; /* Group tag */
4341 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4343 print_fatal_error(data
, "Missing GROUP tag on line %d of \"%s\".", f
->linenum
, f
->filename
);
4347 if ((group_tag
= ippTagValue(temp
)) == IPP_TAG_ZERO
|| group_tag
>= IPP_TAG_UNSUPPORTED_VALUE
)
4349 print_fatal_error(data
, "Bad GROUP tag \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
4353 if (group_tag
== f
->group_tag
)
4354 ippAddSeparator(f
->attrs
);
4356 f
->group_tag
= group_tag
;
4358 else if (!_cups_strcasecmp(token
, "DELAY"))
4361 * Delay before operation...
4364 double dval
; /* Delay value */
4366 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4368 print_fatal_error(data
, "Missing DELAY value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4372 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
4374 if ((dval
= _cupsStrScand(value
, &ptr
, localeconv())) < 0.0 || (*ptr
&& *ptr
!= ','))
4376 print_fatal_error(data
, "Bad DELAY value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
4380 data
->delay
= (useconds_t
)(1000000.0 * dval
);
4384 if ((dval
= _cupsStrScand(ptr
+ 1, &ptr
, localeconv())) <= 0.0 || *ptr
)
4386 print_fatal_error(data
, "Bad DELAY value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
4390 data
->repeat_interval
= (useconds_t
)(1000000.0 * dval
);
4393 data
->repeat_interval
= data
->delay
;
4395 else if (!_cups_strcasecmp(token
, "FILE"))
4401 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4403 print_fatal_error(data
, "Missing FILE filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
4407 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
4408 get_filename(f
->filename
, data
->file
, value
, sizeof(data
->file
));
4410 if (access(data
->file
, R_OK
))
4412 print_fatal_error(data
, "Filename \"%s\" (mapped to \"%s\") on line %d of \"%s\" cannot be read.", value
, data
->file
, f
->linenum
, f
->filename
);
4416 else if (!_cups_strcasecmp(token
, "STATUS"))
4422 if (data
->num_statuses
>= (int)(sizeof(data
->statuses
) / sizeof(data
->statuses
[0])))
4424 print_fatal_error(data
, "Too many STATUS's on line %d of \"%s\".", f
->linenum
, f
->filename
);
4428 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4430 print_fatal_error(data
, "Missing STATUS code on line %d of \"%s\".", f
->linenum
, f
->filename
);
4434 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)
4436 print_fatal_error(data
, "Bad STATUS code \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
4440 data
->last_status
= data
->statuses
+ data
->num_statuses
;
4441 data
->num_statuses
++;
4443 data
->last_status
->define_match
= NULL
;
4444 data
->last_status
->define_no_match
= NULL
;
4445 data
->last_status
->if_defined
= NULL
;
4446 data
->last_status
->if_not_defined
= NULL
;
4447 data
->last_status
->repeat_limit
= 1000;
4448 data
->last_status
->repeat_match
= 0;
4449 data
->last_status
->repeat_no_match
= 0;
4451 else if (!_cups_strcasecmp(token
, "EXPECT") || !_cups_strcasecmp(token
, "EXPECT-ALL"))
4454 * Expected attributes...
4457 int expect_all
= !_cups_strcasecmp(token
, "EXPECT-ALL");
4459 if (data
->num_expects
>= (int)(sizeof(data
->expects
) / sizeof(data
->expects
[0])))
4461 print_fatal_error(data
, "Too many EXPECT's on line %d of \"%s\".", f
->linenum
, f
->filename
);
4465 if (!_ippFileReadToken(f
, name
, sizeof(name
)))
4467 print_fatal_error(data
, "Missing EXPECT name on line %d of \"%s\".", f
->linenum
, f
->filename
);
4471 data
->last_expect
= data
->expects
+ data
->num_expects
;
4472 data
->num_expects
++;
4474 memset(data
->last_expect
, 0, sizeof(ipptool_expect_t
));
4475 data
->last_expect
->repeat_limit
= 1000;
4476 data
->last_expect
->expect_all
= expect_all
;
4480 data
->last_expect
->not_expect
= 1;
4481 data
->last_expect
->name
= strdup(name
+ 1);
4483 else if (name
[0] == '?')
4485 data
->last_expect
->optional
= 1;
4486 data
->last_expect
->name
= strdup(name
+ 1);
4489 data
->last_expect
->name
= strdup(name
);
4491 else if (!_cups_strcasecmp(token
, "COUNT"))
4493 int count
; /* Count value */
4495 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4497 print_fatal_error(data
, "Missing COUNT number on line %d of \"%s\".", f
->linenum
, f
->filename
);
4501 if ((count
= atoi(temp
)) <= 0)
4503 print_fatal_error(data
, "Bad COUNT \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
4507 if (data
->last_expect
)
4509 data
->last_expect
->count
= count
;
4513 print_fatal_error(data
, "COUNT without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
4517 else if (!_cups_strcasecmp(token
, "DEFINE-MATCH"))
4519 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4521 print_fatal_error(data
, "Missing DEFINE-MATCH variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
4525 if (data
->last_expect
)
4527 data
->last_expect
->define_match
= strdup(temp
);
4529 else if (data
->last_status
)
4531 data
->last_status
->define_match
= strdup(temp
);
4535 print_fatal_error(data
, "DEFINE-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
4539 else if (!_cups_strcasecmp(token
, "DEFINE-NO-MATCH"))
4541 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4543 print_fatal_error(data
, "Missing DEFINE-NO-MATCH variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
4547 if (data
->last_expect
)
4549 data
->last_expect
->define_no_match
= strdup(temp
);
4551 else if (data
->last_status
)
4553 data
->last_status
->define_no_match
= strdup(temp
);
4557 print_fatal_error(data
, "DEFINE-NO-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
4561 else if (!_cups_strcasecmp(token
, "DEFINE-VALUE"))
4563 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4565 print_fatal_error(data
, "Missing DEFINE-VALUE variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
4569 if (data
->last_expect
)
4571 data
->last_expect
->define_value
= strdup(temp
);
4575 print_fatal_error(data
, "DEFINE-VALUE without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
4579 else if (!_cups_strcasecmp(token
, "OF-TYPE"))
4581 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4583 print_fatal_error(data
, "Missing OF-TYPE value tag(s) on line %d of \"%s\".", f
->linenum
, f
->filename
);
4587 if (data
->last_expect
)
4589 data
->last_expect
->of_type
= strdup(temp
);
4593 print_fatal_error(data
, "OF-TYPE without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
4597 else if (!_cups_strcasecmp(token
, "IN-GROUP"))
4599 ipp_tag_t in_group
; /* IN-GROUP value */
4601 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4603 print_fatal_error(data
, "Missing IN-GROUP group tag on line %d of \"%s\".", f
->linenum
, f
->filename
);
4607 if ((in_group
= ippTagValue(temp
)) == IPP_TAG_ZERO
|| in_group
>= IPP_TAG_UNSUPPORTED_VALUE
)
4609 print_fatal_error(data
, "Bad IN-GROUP group tag \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
4612 else if (data
->last_expect
)
4614 data
->last_expect
->in_group
= in_group
;
4618 print_fatal_error(data
, "IN-GROUP without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
4622 else if (!_cups_strcasecmp(token
, "REPEAT-LIMIT"))
4624 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4626 print_fatal_error(data
, "Missing REPEAT-LIMIT value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4629 else if (atoi(temp
) <= 0)
4631 print_fatal_error(data
, "Bad REPEAT-LIMIT value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4635 if (data
->last_status
)
4637 data
->last_status
->repeat_limit
= atoi(temp
);
4639 else if (data
->last_expect
)
4641 data
->last_expect
->repeat_limit
= atoi(temp
);
4645 print_fatal_error(data
, "REPEAT-LIMIT without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
4649 else if (!_cups_strcasecmp(token
, "REPEAT-MATCH"))
4651 if (data
->last_status
)
4653 data
->last_status
->repeat_match
= 1;
4655 else if (data
->last_expect
)
4657 data
->last_expect
->repeat_match
= 1;
4661 print_fatal_error(data
, "REPEAT-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
4665 else if (!_cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
4667 if (data
->last_status
)
4669 data
->last_status
->repeat_no_match
= 1;
4671 else if (data
->last_expect
)
4673 data
->last_expect
->repeat_no_match
= 1;
4677 print_fatal_error(data
, "REPEAT-NO-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
4681 else if (!_cups_strcasecmp(token
, "SAME-COUNT-AS"))
4683 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4685 print_fatal_error(data
, "Missing SAME-COUNT-AS name on line %d of \"%s\".", f
->linenum
, f
->filename
);
4689 if (data
->last_expect
)
4691 data
->last_expect
->same_count_as
= strdup(temp
);
4695 print_fatal_error(data
, "SAME-COUNT-AS without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
4699 else if (!_cups_strcasecmp(token
, "IF-DEFINED"))
4701 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4703 print_fatal_error(data
, "Missing IF-DEFINED name on line %d of \"%s\".", f
->linenum
, f
->filename
);
4707 if (data
->last_expect
)
4709 data
->last_expect
->if_defined
= strdup(temp
);
4711 else if (data
->last_status
)
4713 data
->last_status
->if_defined
= strdup(temp
);
4717 print_fatal_error(data
, "IF-DEFINED without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
4721 else if (!_cups_strcasecmp(token
, "IF-NOT-DEFINED"))
4723 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4725 print_fatal_error(data
, "Missing IF-NOT-DEFINED name on line %d of \"%s\".", f
->linenum
, f
->filename
);
4729 if (data
->last_expect
)
4731 data
->last_expect
->if_not_defined
= strdup(temp
);
4733 else if (data
->last_status
)
4735 data
->last_status
->if_not_defined
= strdup(temp
);
4739 print_fatal_error(data
, "IF-NOT-DEFINED without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
4743 else if (!_cups_strcasecmp(token
, "WITH-DISTINCT-VALUES"))
4745 if (data
->last_expect
)
4747 data
->last_expect
->with_distinct
= 1;
4751 print_fatal_error(data
, "%s without a preceding EXPECT on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
4755 else if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") ||
4756 !_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
4757 !_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
4758 !_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
4759 !_cups_strcasecmp(token
, "WITH-HOSTNAME") ||
4760 !_cups_strcasecmp(token
, "WITH-RESOURCE") ||
4761 !_cups_strcasecmp(token
, "WITH-SCHEME") ||
4762 !_cups_strcasecmp(token
, "WITH-VALUE"))
4764 off_t lastpos
; /* Last file position */
4765 int lastline
; /* Last line number */
4767 if (data
->last_expect
)
4769 if (!_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") || !_cups_strcasecmp(token
, "WITH-HOSTNAME"))
4770 data
->last_expect
->with_flags
= IPPTOOL_WITH_HOSTNAME
;
4771 else if (!_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") || !_cups_strcasecmp(token
, "WITH-RESOURCE"))
4772 data
->last_expect
->with_flags
= IPPTOOL_WITH_RESOURCE
;
4773 else if (!_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") || !_cups_strcasecmp(token
, "WITH-SCHEME"))
4774 data
->last_expect
->with_flags
= IPPTOOL_WITH_SCHEME
;
4776 if (!_cups_strncasecmp(token
, "WITH-ALL-", 9))
4777 data
->last_expect
->with_flags
|= IPPTOOL_WITH_ALL
;
4780 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4782 print_fatal_error(data
, "Missing %s value on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
4787 * Read additional comma-delimited values - needed since legacy test files
4788 * will have unquoted WITH-VALUE values with commas...
4791 ptr
= temp
+ strlen(temp
);
4795 lastpos
= cupsFileTell(f
->fp
);
4796 lastline
= f
->linenum
;
4799 if (!_ippFileReadToken(f
, ptr
, (sizeof(temp
) - (size_t)(ptr
- temp
))))
4802 if (!strcmp(ptr
, ","))
4810 if (!_ippFileReadToken(f
, ptr
, (sizeof(temp
) - (size_t)(ptr
- temp
))))
4816 * Not another value, stop here...
4819 cupsFileSeek(f
->fp
, lastpos
);
4820 f
->linenum
= lastline
;
4826 if (data
->last_expect
)
4829 * Expand any variables in the value and then save it.
4832 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
4834 ptr
= value
+ strlen(value
) - 1;
4836 if (value
[0] == '/' && ptr
> value
&& *ptr
== '/')
4839 * WITH-VALUE is a POSIX extended regular expression.
4842 data
->last_expect
->with_value
= calloc(1, (size_t)(ptr
- value
));
4843 data
->last_expect
->with_flags
|= IPPTOOL_WITH_REGEX
;
4845 if (data
->last_expect
->with_value
)
4846 memcpy(data
->last_expect
->with_value
, value
+ 1, (size_t)(ptr
- value
- 1));
4851 * WITH-VALUE is a literal value...
4854 for (ptr
= value
; *ptr
; ptr
++)
4856 if (*ptr
== '\\' && ptr
[1])
4859 * Remove \ from \foo...
4862 _cups_strcpy(ptr
, ptr
+ 1);
4866 data
->last_expect
->with_value
= strdup(value
);
4867 data
->last_expect
->with_flags
|= IPPTOOL_WITH_LITERAL
;
4872 print_fatal_error(data
, "%s without a preceding EXPECT on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
4876 else if (!_cups_strcasecmp(token
, "WITH-VALUE-FROM"))
4878 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4880 print_fatal_error(data
, "Missing %s value on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
4884 if (data
->last_expect
)
4887 * Expand any variables in the value and then save it.
4890 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
4892 data
->last_expect
->with_value_from
= strdup(value
);
4893 data
->last_expect
->with_flags
= IPPTOOL_WITH_LITERAL
;
4897 print_fatal_error(data
, "%s without a preceding EXPECT on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
4901 else if (!_cups_strcasecmp(token
, "DISPLAY"))
4904 * Display attributes...
4907 if (data
->num_displayed
>= (int)(sizeof(data
->displayed
) / sizeof(data
->displayed
[0])))
4909 print_fatal_error(data
, "Too many DISPLAY's on line %d of \"%s\".", f
->linenum
, f
->filename
);
4913 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
4915 print_fatal_error(data
, "Missing DISPLAY name on line %d of \"%s\".", f
->linenum
, f
->filename
);
4919 data
->displayed
[data
->num_displayed
] = strdup(temp
);
4920 data
->num_displayed
++;
4924 print_fatal_error(data
, "Unexpected token %s seen on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
4931 * Scan for the start of a test (open brace)...
4934 if (!strcmp(token
, "{"))
4940 if (data
->show_header
)
4942 if (data
->output
== IPPTOOL_OUTPUT_PLIST
)
4943 print_xml_header(data
);
4945 if (data
->output
== IPPTOOL_OUTPUT_TEST
|| (data
->output
== IPPTOOL_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
4946 cupsFilePrintf(cupsFileStdout(), "\"%s\":\n", f
->filename
);
4948 data
->show_header
= 0;
4951 data
->compression
[0] = '\0';
4953 data
->num_expects
= 0;
4954 data
->last_expect
= NULL
;
4955 data
->file
[0] = '\0';
4956 data
->ignore_errors
= data
->def_ignore_errors
;
4957 strlcpy(data
->name
, f
->filename
, sizeof(data
->name
));
4958 if ((ptr
= strrchr(data
->name
, '.')) != NULL
)
4960 data
->repeat_interval
= 5000000;
4961 strlcpy(data
->resource
, data
->vars
->resource
, sizeof(data
->resource
));
4962 data
->skip_previous
= 0;
4963 data
->pass_test
= 0;
4964 data
->skip_test
= 0;
4965 data
->num_statuses
= 0;
4966 data
->last_status
= NULL
;
4967 data
->test_id
[0] = '\0';
4968 data
->transfer
= data
->def_transfer
;
4969 data
->version
= data
->def_version
;
4971 free(data
->monitor_uri
);
4972 data
->monitor_uri
= NULL
;
4973 data
->monitor_delay
= 0;
4974 data
->monitor_interval
= 5000000;
4975 data
->num_monitor_expects
= 0;
4977 _ippVarsSet(vars
, "date-current", iso_date(ippTimeToDate(time(NULL
))));
4979 f
->attrs
= ippNew();
4980 f
->group_tag
= IPP_TAG_ZERO
;
4982 else if (!strcmp(token
, "DEFINE"))
4988 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
4990 _ippVarsSet(vars
, "date-current", iso_date(ippTimeToDate(time(NULL
))));
4991 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
4992 _ippVarsSet(vars
, name
, value
);
4996 print_fatal_error(data
, "Missing DEFINE name and/or value on line %d of \"%s\".", f
->linenum
, f
->filename
);
5000 else if (!strcmp(token
, "DEFINE-DEFAULT"))
5003 * DEFINE-DEFAULT name value
5006 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
5008 if (!_ippVarsGet(vars
, name
))
5010 _ippVarsSet(vars
, "date-current", iso_date(ippTimeToDate(time(NULL
))));
5011 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
5012 _ippVarsSet(vars
, name
, value
);
5017 print_fatal_error(data
, "Missing DEFINE-DEFAULT name and/or value on line %d of \"%s\".", f
->linenum
, f
->filename
);
5021 else if (!strcmp(token
, "FILE-ID"))
5027 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
5029 _ippVarsSet(vars
, "date-current", iso_date(ippTimeToDate(time(NULL
))));
5030 _ippVarsExpand(vars
, data
->file_id
, temp
, sizeof(data
->file_id
));
5034 print_fatal_error(data
, "Missing FILE-ID value on line %d of \"%s\".", f
->linenum
, f
->filename
);
5038 else if (!strcmp(token
, "IGNORE-ERRORS"))
5045 if (_ippFileReadToken(f
, temp
, sizeof(temp
)) && (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
5047 data
->def_ignore_errors
= !_cups_strcasecmp(temp
, "yes");
5051 print_fatal_error(data
, "Missing IGNORE-ERRORS value on line %d of \"%s\".", f
->linenum
, f
->filename
);
5055 else if (!strcmp(token
, "INCLUDE"))
5058 * INCLUDE "filename"
5059 * INCLUDE <filename>
5062 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
5065 * Map the filename to and then run the tests...
5068 ipptool_test_t inc_data
; /* Data for included file */
5069 char filename
[1024]; /* Mapped filename */
5071 memcpy(&inc_data
, data
, sizeof(inc_data
));
5072 inc_data
.http
= NULL
;
5074 inc_data
.prev_pass
= 1;
5075 inc_data
.show_header
= 1;
5077 if (!do_tests(get_filename(f
->filename
, filename
, temp
, sizeof(filename
)), &inc_data
) && data
->stop_after_include_error
)
5079 data
->pass
= data
->prev_pass
= 0;
5085 print_fatal_error(data
, "Missing INCLUDE filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
5089 data
->show_header
= 1;
5091 else if (!strcmp(token
, "INCLUDE-IF-DEFINED"))
5094 * INCLUDE-IF-DEFINED name "filename"
5095 * INCLUDE-IF-DEFINED name <filename>
5098 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
5101 * Map the filename to and then run the tests...
5104 ipptool_test_t inc_data
; /* Data for included file */
5105 char filename
[1024]; /* Mapped filename */
5107 memcpy(&inc_data
, data
, sizeof(inc_data
));
5108 inc_data
.http
= NULL
;
5110 inc_data
.prev_pass
= 1;
5111 inc_data
.show_header
= 1;
5113 if (!do_tests(get_filename(f
->filename
, filename
, temp
, sizeof(filename
)), &inc_data
) && data
->stop_after_include_error
)
5115 data
->pass
= data
->prev_pass
= 0;
5121 print_fatal_error(data
, "Missing INCLUDE-IF-DEFINED name or filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
5125 data
->show_header
= 1;
5127 else if (!strcmp(token
, "INCLUDE-IF-NOT-DEFINED"))
5130 * INCLUDE-IF-NOT-DEFINED name "filename"
5131 * INCLUDE-IF-NOT-DEFINED name <filename>
5134 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
5137 * Map the filename to and then run the tests...
5140 ipptool_test_t inc_data
; /* Data for included file */
5141 char filename
[1024]; /* Mapped filename */
5143 memcpy(&inc_data
, data
, sizeof(inc_data
));
5144 inc_data
.http
= NULL
;
5146 inc_data
.prev_pass
= 1;
5147 inc_data
.show_header
= 1;
5149 if (!do_tests(get_filename(f
->filename
, filename
, temp
, sizeof(filename
)), &inc_data
) && data
->stop_after_include_error
)
5151 data
->pass
= data
->prev_pass
= 0;
5157 print_fatal_error(data
, "Missing INCLUDE-IF-NOT-DEFINED name or filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
5161 data
->show_header
= 1;
5163 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
5166 * SKIP-IF-DEFINED variable
5169 if (_ippFileReadToken(f
, name
, sizeof(name
)))
5171 if (_ippVarsGet(vars
, name
))
5172 data
->skip_test
= 1;
5176 print_fatal_error(data
, "Missing SKIP-IF-DEFINED variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
5180 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
5183 * SKIP-IF-NOT-DEFINED variable
5186 if (_ippFileReadToken(f
, name
, sizeof(name
)))
5188 if (!_ippVarsGet(vars
, name
))
5189 data
->skip_test
= 1;
5193 print_fatal_error(data
, "Missing SKIP-IF-NOT-DEFINED variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
5197 else if (!strcmp(token
, "STOP-AFTER-INCLUDE-ERROR"))
5200 * STOP-AFTER-INCLUDE-ERROR yes
5201 * STOP-AFTER-INCLUDE-ERROR no
5204 if (_ippFileReadToken(f
, temp
, sizeof(temp
)) && (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
5206 data
->stop_after_include_error
= !_cups_strcasecmp(temp
, "yes");
5210 print_fatal_error(data
, "Missing STOP-AFTER-INCLUDE-ERROR value on line %d of \"%s\".", f
->linenum
, f
->filename
);
5214 else if (!strcmp(token
, "TRANSFER"))
5222 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
5224 if (!strcmp(temp
, "auto"))
5225 data
->def_transfer
= IPPTOOL_TRANSFER_AUTO
;
5226 else if (!strcmp(temp
, "chunked"))
5227 data
->def_transfer
= IPPTOOL_TRANSFER_CHUNKED
;
5228 else if (!strcmp(temp
, "length"))
5229 data
->def_transfer
= IPPTOOL_TRANSFER_LENGTH
;
5232 print_fatal_error(data
, "Bad TRANSFER value \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
5238 print_fatal_error(data
, "Missing TRANSFER value on line %d of \"%s\".", f
->linenum
, f
->filename
);
5242 else if (!strcmp(token
, "VERSION"))
5244 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
5246 if (!strcmp(temp
, "1.0"))
5247 data
->def_version
= 10;
5248 else if (!strcmp(temp
, "1.1"))
5249 data
->def_version
= 11;
5250 else if (!strcmp(temp
, "2.0"))
5251 data
->def_version
= 20;
5252 else if (!strcmp(temp
, "2.1"))
5253 data
->def_version
= 21;
5254 else if (!strcmp(temp
, "2.2"))
5255 data
->def_version
= 22;
5258 print_fatal_error(data
, "Bad VERSION \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
5264 print_fatal_error(data
, "Missing VERSION number on line %d of \"%s\".", f
->linenum
, f
->filename
);
5270 print_fatal_error(data
, "Unexpected token %s seen on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
5280 * 'usage()' - Show program usage.
5286 _cupsLangPuts(stderr
, _("Usage: ipptool [options] URI filename [ ... filenameN ]"));
5287 _cupsLangPuts(stderr
, _("Options:"));
5288 _cupsLangPuts(stderr
, _("--ippserver filename Produce ippserver attribute file"));
5289 _cupsLangPuts(stderr
, _("--stop-after-include-error\n"
5290 " Stop tests after a failed INCLUDE"));
5291 _cupsLangPuts(stderr
, _("--version Show version"));
5292 _cupsLangPuts(stderr
, _("-4 Connect using IPv4"));
5293 _cupsLangPuts(stderr
, _("-6 Connect using IPv6"));
5294 _cupsLangPuts(stderr
, _("-C Send requests using chunking (default)"));
5295 _cupsLangPuts(stderr
, _("-E Test with encryption using HTTP Upgrade to TLS"));
5296 _cupsLangPuts(stderr
, _("-I Ignore errors"));
5297 _cupsLangPuts(stderr
, _("-L Send requests using content-length"));
5298 _cupsLangPuts(stderr
, _("-P filename.plist Produce XML plist to a file and test report to standard output"));
5299 _cupsLangPuts(stderr
, _("-R Repeat tests on server-error-busy"));
5300 _cupsLangPuts(stderr
, _("-S Test with encryption using HTTPS"));
5301 _cupsLangPuts(stderr
, _("-T seconds Set the receive/send timeout in seconds"));
5302 _cupsLangPuts(stderr
, _("-V version Set default IPP version"));
5303 _cupsLangPuts(stderr
, _("-X Produce XML plist instead of plain text"));
5304 _cupsLangPuts(stderr
, _("-c Produce CSV output"));
5305 _cupsLangPuts(stderr
, _("-d name=value Set named variable to value"));
5306 _cupsLangPuts(stderr
, _("-f filename Set default request filename"));
5307 _cupsLangPuts(stderr
, _("-h Validate HTTP response headers"));
5308 _cupsLangPuts(stderr
, _("-i seconds Repeat the last file with the given time interval"));
5309 _cupsLangPuts(stderr
, _("-l Produce plain text output"));
5310 _cupsLangPuts(stderr
, _("-n count Repeat the last file the given number of times"));
5311 _cupsLangPuts(stderr
, _("-q Run silently"));
5312 _cupsLangPuts(stderr
, _("-t Produce a test report"));
5313 _cupsLangPuts(stderr
, _("-v Be verbose"));
5320 * 'with_distinct_values()' - Verify that an attribute contains unique values.
5323 static int // O - 1 if distinct, 0 if duplicate
5324 with_distinct_values(
5325 cups_array_t
*errors
, // I - Array of errors
5326 ipp_attribute_t
*attr
) // I - Attribute to test
5328 int i
, // Looping var
5329 count
; // Number of values
5330 ipp_tag_t value_tag
; // Value syntax
5331 const char *value
; // Current value
5332 char buffer
[8192]; // Temporary buffer
5333 cups_array_t
*values
; // Array of values as strings
5336 // If there is only 1 value, it must be distinct
5337 if ((count
= ippGetCount(attr
)) == 1)
5340 // Only check integers, enums, rangeOfInteger, resolution, and nul-terminated
5342 switch (value_tag
= ippGetValueTag(attr
))
5344 case IPP_TAG_INTEGER
:
5346 case IPP_TAG_RANGE
:
5347 case IPP_TAG_RESOLUTION
:
5348 case IPP_TAG_KEYWORD
:
5349 case IPP_TAG_URISCHEME
:
5350 case IPP_TAG_CHARSET
:
5351 case IPP_TAG_LANGUAGE
:
5352 case IPP_TAG_MIMETYPE
:
5353 case IPP_TAG_BEGIN_COLLECTION
:
5357 add_stringf(errors
, "WITH-DISTINCT-VALUES %s not supported for 1setOf %s", ippGetName(attr
), ippTagString(value_tag
));
5361 // Collect values and determine they are all unique...
5362 values
= cupsArrayNew3((cups_array_func_t
)strcmp
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
, (cups_afree_func_t
)free
);
5364 for (i
= 0; i
< count
; i
++)
5368 case IPP_TAG_INTEGER
:
5370 snprintf(buffer
, sizeof(buffer
), "%d", ippGetInteger(attr
, i
));
5373 case IPP_TAG_RANGE
:
5375 int upper
, lower
= ippGetRange(attr
, i
, &upper
);
5378 snprintf(buffer
, sizeof(buffer
), "%d-%d", lower
, upper
);
5382 case IPP_TAG_RESOLUTION
:
5384 ipp_res_t units
; // Resolution units
5385 int yres
, xres
= ippGetResolution(attr
, i
, &yres
, &units
);
5386 // Resolution values
5389 snprintf(buffer
, sizeof(buffer
), "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5391 snprintf(buffer
, sizeof(buffer
), "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5395 case IPP_TAG_KEYWORD
:
5396 case IPP_TAG_URISCHEME
:
5397 case IPP_TAG_CHARSET
:
5398 case IPP_TAG_LANGUAGE
:
5399 case IPP_TAG_MIMETYPE
:
5400 value
= ippGetString(attr
, i
, NULL
);
5402 case IPP_TAG_BEGIN_COLLECTION
:
5404 ipp_t
*col
= ippGetCollection(attr
, i
);
5406 ipp_attribute_t
*member
; // Member attribute
5407 char *bufptr
, // Pointer into buffer
5408 *bufend
, // End of buffer
5409 prefix
; // Prefix character
5411 for (prefix
= '{', bufptr
= buffer
, bufend
= buffer
+ sizeof(buffer
) - 2, member
= ippFirstAttribute(col
); member
&& bufptr
< bufend
; member
= ippNextAttribute(col
))
5416 ippAttributeString(member
, bufptr
, (size_t)(bufend
- bufptr
));
5417 bufptr
+= strlen(bufptr
);
5425 default : // Should never happen
5426 value
= "unsupported";
5430 if (cupsArrayFind(values
, (void *)value
))
5431 add_stringf(errors
, "DUPLICATE: %s=%s", ippGetName(attr
), value
);
5433 cupsArrayAdd(values
, (void *)value
);
5437 i
= cupsArrayCount(values
) == count
;
5438 cupsArrayDelete(values
);
5445 * 'with_flags_string()' - Return the "WITH-xxx" predicate that corresponds to
5449 static const char * /* O - WITH-xxx string */
5450 with_flags_string(int flags
) /* I - WITH flags */
5452 if (flags
& IPPTOOL_WITH_ALL
)
5454 if (flags
& IPPTOOL_WITH_HOSTNAME
)
5455 return ("WITH-ALL-HOSTNAMES");
5456 else if (flags
& IPPTOOL_WITH_RESOURCE
)
5457 return ("WITH-ALL-RESOURCES");
5458 else if (flags
& IPPTOOL_WITH_SCHEME
)
5459 return ("WITH-ALL-SCHEMES");
5461 return ("WITH-ALL-VALUES");
5463 else if (flags
& IPPTOOL_WITH_HOSTNAME
)
5464 return ("WITH-HOSTNAME");
5465 else if (flags
& IPPTOOL_WITH_RESOURCE
)
5466 return ("WITH-RESOURCE");
5467 else if (flags
& IPPTOOL_WITH_SCHEME
)
5468 return ("WITH-SCHEME");
5470 return ("WITH-VALUE");
5475 * 'with_value()' - Test a WITH-VALUE predicate.
5478 static int /* O - 1 on match, 0 on non-match */
5479 with_value(ipptool_test_t
*data
, /* I - Test data */
5480 cups_array_t
*errors
, /* I - Errors array */
5481 char *value
, /* I - Value string */
5482 int flags
, /* I - Flags for match */
5483 ipp_attribute_t
*attr
, /* I - Attribute to compare */
5484 char *matchbuf
, /* I - Buffer to hold matching value */
5485 size_t matchlen
) /* I - Length of match buffer */
5487 int i
, /* Looping var */
5488 count
, /* Number of values */
5490 char temp
[1024], /* Temporary value string */
5491 *valptr
; /* Pointer into value */
5492 const char *name
; /* Attribute name */
5496 match
= (flags
& IPPTOOL_WITH_ALL
) ? 1 : 0;
5499 * NULL matches everything.
5502 if (!value
|| !*value
)
5506 * Compare the value string to the attribute value.
5509 name
= ippGetName(attr
);
5510 count
= ippGetCount(attr
);
5512 switch (ippGetValueTag(attr
))
5514 case IPP_TAG_INTEGER
:
5516 for (i
= 0; i
< count
; i
++)
5518 char op
, /* Comparison operator */
5519 *nextptr
; /* Next pointer */
5520 int intvalue
, /* Integer value */
5521 attrvalue
= ippGetInteger(attr
, i
),
5522 /* Attribute value */
5523 valmatch
= 0; /* Does the current value match? */
5527 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5528 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5529 *valptr
== '=' || *valptr
== '>')
5532 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5534 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5542 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
5543 if (nextptr
== valptr
)
5547 if ((op
== '=' && attrvalue
== intvalue
) ||
5548 (op
== '<' && attrvalue
< intvalue
) ||
5549 (op
== '>' && attrvalue
> intvalue
))
5552 snprintf(matchbuf
, matchlen
, "%d", attrvalue
);
5559 if (flags
& IPPTOOL_WITH_ALL
)
5574 if (!match
&& errors
)
5576 for (i
= 0; i
< count
; i
++)
5577 add_stringf(data
->errors
, "GOT: %s=%d", name
, ippGetInteger(attr
, i
));
5581 case IPP_TAG_RANGE
:
5582 for (i
= 0; i
< count
; i
++)
5584 char op
, /* Comparison operator */
5585 *nextptr
; /* Next pointer */
5586 int intvalue
, /* Integer value */
5587 lower
, /* Lower range */
5588 upper
, /* Upper range */
5589 valmatch
= 0; /* Does the current value match? */
5591 lower
= ippGetRange(attr
, i
, &upper
);
5594 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5595 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5596 *valptr
== '=' || *valptr
== '>')
5599 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5601 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5609 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
5610 if (nextptr
== valptr
)
5614 if ((op
== '=' && (lower
== intvalue
|| upper
== intvalue
)) ||
5615 (op
== '<' && upper
< intvalue
) ||
5616 (op
== '>' && upper
> intvalue
))
5619 snprintf(matchbuf
, matchlen
, "%d-%d", lower
, upper
);
5626 if (flags
& IPPTOOL_WITH_ALL
)
5641 if (!match
&& errors
)
5643 for (i
= 0; i
< count
; i
++)
5645 int lower
, upper
; /* Range values */
5647 lower
= ippGetRange(attr
, i
, &upper
);
5648 add_stringf(data
->errors
, "GOT: %s=%d-%d", name
, lower
, upper
);
5653 case IPP_TAG_BOOLEAN
:
5654 for (i
= 0; i
< count
; i
++)
5656 if ((!strcmp(value
, "true") || !strcmp(value
, "1")) == ippGetBoolean(attr
, i
))
5659 strlcpy(matchbuf
, value
, matchlen
);
5661 if (!(flags
& IPPTOOL_WITH_ALL
))
5667 else if (flags
& IPPTOOL_WITH_ALL
)
5674 if (!match
&& errors
)
5676 for (i
= 0; i
< count
; i
++)
5677 add_stringf(data
->errors
, "GOT: %s=%s", name
, ippGetBoolean(attr
, i
) ? "true" : "false");
5681 case IPP_TAG_RESOLUTION
:
5682 for (i
= 0; i
< count
; i
++)
5684 int xres
, yres
; /* Resolution values */
5685 ipp_res_t units
; /* Resolution units */
5687 xres
= ippGetResolution(attr
, i
, &yres
, &units
);
5689 snprintf(temp
, sizeof(temp
), "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5691 snprintf(temp
, sizeof(temp
), "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5693 if (!strcmp(value
, temp
))
5696 strlcpy(matchbuf
, value
, matchlen
);
5698 if (!(flags
& IPPTOOL_WITH_ALL
))
5704 else if (flags
& IPPTOOL_WITH_ALL
)
5711 if (!match
&& errors
)
5713 for (i
= 0; i
< count
; i
++)
5715 int xres
, yres
; /* Resolution values */
5716 ipp_res_t units
; /* Resolution units */
5718 xres
= ippGetResolution(attr
, i
, &yres
, &units
);
5720 snprintf(temp
, sizeof(temp
), "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5722 snprintf(temp
, sizeof(temp
), "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5724 if (strcmp(value
, temp
))
5725 add_stringf(data
->errors
, "GOT: %s=%s", name
, temp
);
5730 case IPP_TAG_NOVALUE
:
5731 case IPP_TAG_UNKNOWN
:
5734 case IPP_TAG_CHARSET
:
5735 case IPP_TAG_KEYWORD
:
5736 case IPP_TAG_LANGUAGE
:
5737 case IPP_TAG_MIMETYPE
:
5739 case IPP_TAG_NAMELANG
:
5741 case IPP_TAG_TEXTLANG
:
5743 case IPP_TAG_URISCHEME
:
5744 if (flags
& IPPTOOL_WITH_REGEX
)
5747 * Value is an extended, case-sensitive POSIX regular expression...
5750 regex_t re
; /* Regular expression */
5752 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
5754 regerror(i
, &re
, temp
, sizeof(temp
));
5756 print_fatal_error(data
, "Unable to compile WITH-VALUE regular expression \"%s\" - %s", value
, temp
);
5761 * See if ALL of the values match the given regular expression.
5764 for (i
= 0; i
< count
; i
++)
5766 if (!regexec(&re
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5770 strlcpy(matchbuf
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)), matchlen
);
5772 if (!(flags
& IPPTOOL_WITH_ALL
))
5778 else if (flags
& IPPTOOL_WITH_ALL
)
5787 else if (ippGetValueTag(attr
) == IPP_TAG_URI
&& !(flags
& (IPPTOOL_WITH_SCHEME
| IPPTOOL_WITH_HOSTNAME
| IPPTOOL_WITH_RESOURCE
)))
5790 * Value is a literal URI string, see if the value(s) match...
5793 for (i
= 0; i
< count
; i
++)
5795 if (!compare_uris(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
))))
5798 strlcpy(matchbuf
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)), matchlen
);
5800 if (!(flags
& IPPTOOL_WITH_ALL
))
5806 else if (flags
& IPPTOOL_WITH_ALL
)
5816 * Value is a literal string, see if the value(s) match...
5819 for (i
= 0; i
< count
; i
++)
5823 switch (ippGetValueTag(attr
))
5827 * Some URI components are case-sensitive, some not...
5830 if (flags
& (IPPTOOL_WITH_SCHEME
| IPPTOOL_WITH_HOSTNAME
))
5831 result
= _cups_strcasecmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
5833 result
= strcmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
5836 case IPP_TAG_MIMETYPE
:
5838 case IPP_TAG_NAMELANG
:
5840 case IPP_TAG_TEXTLANG
:
5842 * mimeMediaType, nameWithoutLanguage, nameWithLanguage,
5843 * textWithoutLanguage, and textWithLanguage are defined to
5844 * be case-insensitive strings...
5847 result
= _cups_strcasecmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
5852 * Other string syntaxes are defined as lowercased so we use
5853 * case-sensitive comparisons to catch problems...
5856 result
= strcmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
5863 strlcpy(matchbuf
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)), matchlen
);
5865 if (!(flags
& IPPTOOL_WITH_ALL
))
5871 else if (flags
& IPPTOOL_WITH_ALL
)
5879 if (!match
&& errors
)
5881 for (i
= 0; i
< count
; i
++)
5882 add_stringf(data
->errors
, "GOT: %s=\"%s\"", name
, ippGetString(attr
, i
, NULL
));
5886 case IPP_TAG_STRING
:
5887 if (flags
& IPPTOOL_WITH_REGEX
)
5890 * Value is an extended, case-sensitive POSIX regular expression...
5893 void *adata
; /* Pointer to octetString data */
5894 int adatalen
; /* Length of octetString */
5895 regex_t re
; /* Regular expression */
5897 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
5899 regerror(i
, &re
, temp
, sizeof(temp
));
5901 print_fatal_error(data
, "Unable to compile WITH-VALUE regular expression \"%s\" - %s", value
, temp
);
5906 * See if ALL of the values match the given regular expression.
5909 for (i
= 0; i
< count
; i
++)
5911 if ((adata
= ippGetOctetString(attr
, i
, &adatalen
)) == NULL
|| adatalen
>= (int)sizeof(temp
))
5916 memcpy(temp
, adata
, (size_t)adatalen
);
5917 temp
[adatalen
] = '\0';
5919 if (!regexec(&re
, temp
, 0, NULL
, 0))
5922 strlcpy(matchbuf
, temp
, matchlen
);
5924 if (!(flags
& IPPTOOL_WITH_ALL
))
5930 else if (flags
& IPPTOOL_WITH_ALL
)
5939 if (!match
&& errors
)
5941 for (i
= 0; i
< count
; i
++)
5943 adata
= ippGetOctetString(attr
, i
, &adatalen
);
5944 copy_hex_string(temp
, adata
, adatalen
, sizeof(temp
));
5945 add_stringf(data
->errors
, "GOT: %s=\"%s\"", name
, temp
);
5952 * Value is a literal or hex-encoded string...
5955 unsigned char withdata
[1023], /* WITH-VALUE data */
5956 *adata
; /* Pointer to octetString data */
5957 int withlen
, /* Length of WITH-VALUE data */
5958 adatalen
; /* Length of octetString */
5963 * Grab hex-encoded value...
5966 if ((withlen
= (int)strlen(value
)) & 1 || withlen
> (int)(2 * (sizeof(withdata
) + 1)))
5968 print_fatal_error(data
, "Bad WITH-VALUE hex value.");
5972 withlen
= withlen
/ 2 - 1;
5974 for (valptr
= value
+ 1, adata
= withdata
; *valptr
; valptr
+= 2)
5976 int ch
; /* Current character/byte */
5978 if (isdigit(valptr
[0]))
5979 ch
= (valptr
[0] - '0') << 4;
5980 else if (isalpha(valptr
[0]))
5981 ch
= (tolower(valptr
[0]) - 'a' + 10) << 4;
5985 if (isdigit(valptr
[1]))
5986 ch
|= valptr
[1] - '0';
5987 else if (isalpha(valptr
[1]))
5988 ch
|= tolower(valptr
[1]) - 'a' + 10;
5992 *adata
++ = (unsigned char)ch
;
5997 print_fatal_error(data
, "Bad WITH-VALUE hex value.");
6004 * Copy literal string value...
6007 withlen
= (int)strlen(value
);
6009 memcpy(withdata
, value
, (size_t)withlen
);
6012 for (i
= 0; i
< count
; i
++)
6014 adata
= ippGetOctetString(attr
, i
, &adatalen
);
6016 if (withlen
== adatalen
&& !memcmp(withdata
, adata
, (size_t)withlen
))
6019 copy_hex_string(matchbuf
, adata
, adatalen
, matchlen
);
6021 if (!(flags
& IPPTOOL_WITH_ALL
))
6027 else if (flags
& IPPTOOL_WITH_ALL
)
6034 if (!match
&& errors
)
6036 for (i
= 0; i
< count
; i
++)
6038 adata
= ippGetOctetString(attr
, i
, &adatalen
);
6039 copy_hex_string(temp
, adata
, adatalen
, sizeof(temp
));
6040 add_stringf(data
->errors
, "GOT: %s=\"%s\"", name
, temp
);
6055 * 'with_value_from()' - Test a WITH-VALUE-FROM predicate.
6058 static int /* O - 1 on match, 0 on non-match */
6060 cups_array_t
*errors
, /* I - Errors array */
6061 ipp_attribute_t
*fromattr
, /* I - "From" attribute */
6062 ipp_attribute_t
*attr
, /* I - Attribute to compare */
6063 char *matchbuf
, /* I - Buffer to hold matching value */
6064 size_t matchlen
) /* I - Length of match buffer */
6066 int i
, j
, /* Looping vars */
6067 count
= ippGetCount(attr
), /* Number of attribute values */
6068 match
= 1; /* Match? */
6074 * Compare the from value(s) to the attribute value(s)...
6077 switch (ippGetValueTag(attr
))
6079 case IPP_TAG_INTEGER
:
6080 if (ippGetValueTag(fromattr
) != IPP_TAG_INTEGER
&& ippGetValueTag(fromattr
) != IPP_TAG_RANGE
)
6081 goto wrong_value_tag
;
6083 for (i
= 0; i
< count
; i
++)
6085 int value
= ippGetInteger(attr
, i
);
6086 /* Current integer value */
6088 if (ippContainsInteger(fromattr
, value
))
6091 snprintf(matchbuf
, matchlen
, "%d", value
);
6095 add_stringf(errors
, "GOT: %s=%d", ippGetName(attr
), value
);
6102 if (ippGetValueTag(fromattr
) != IPP_TAG_ENUM
)
6103 goto wrong_value_tag
;
6105 for (i
= 0; i
< count
; i
++)
6107 int value
= ippGetInteger(attr
, i
);
6108 /* Current integer value */
6110 if (ippContainsInteger(fromattr
, value
))
6113 snprintf(matchbuf
, matchlen
, "%d", value
);
6117 add_stringf(errors
, "GOT: %s=%d", ippGetName(attr
), value
);
6123 case IPP_TAG_RESOLUTION
:
6124 if (ippGetValueTag(fromattr
) != IPP_TAG_RESOLUTION
)
6125 goto wrong_value_tag
;
6127 for (i
= 0; i
< count
; i
++)
6131 int fromcount
= ippGetCount(fromattr
);
6132 int fromxres
, fromyres
;
6133 ipp_res_t fromunits
;
6135 xres
= ippGetResolution(attr
, i
, &yres
, &units
);
6137 for (j
= 0; j
< fromcount
; j
++)
6139 fromxres
= ippGetResolution(fromattr
, j
, &fromyres
, &fromunits
);
6140 if (fromxres
== xres
&& fromyres
== yres
&& fromunits
== units
)
6149 snprintf(matchbuf
, matchlen
, "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6151 snprintf(matchbuf
, matchlen
, "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6157 add_stringf(errors
, "GOT: %s=%d%s", ippGetName(attr
), xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6159 add_stringf(errors
, "GOT: %s=%dx%d%s", ippGetName(attr
), xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6166 case IPP_TAG_NOVALUE
:
6167 case IPP_TAG_UNKNOWN
:
6170 case IPP_TAG_CHARSET
:
6171 case IPP_TAG_KEYWORD
:
6172 case IPP_TAG_LANGUAGE
:
6173 case IPP_TAG_MIMETYPE
:
6175 case IPP_TAG_NAMELANG
:
6177 case IPP_TAG_TEXTLANG
:
6178 case IPP_TAG_URISCHEME
:
6179 for (i
= 0; i
< count
; i
++)
6181 const char *value
= ippGetString(attr
, i
, NULL
);
6182 /* Current string value */
6184 if (ippContainsString(fromattr
, value
))
6187 strlcpy(matchbuf
, value
, matchlen
);
6191 add_stringf(errors
, "GOT: %s='%s'", ippGetName(attr
), value
);
6198 for (i
= 0; i
< count
; i
++)
6200 const char *value
= ippGetString(attr
, i
, NULL
);
6201 /* Current string value */
6202 int fromcount
= ippGetCount(fromattr
);
6204 for (j
= 0; j
< fromcount
; j
++)
6206 if (!compare_uris(value
, ippGetString(fromattr
, j
, NULL
)))
6209 strlcpy(matchbuf
, value
, matchlen
);
6216 add_stringf(errors
, "GOT: %s='%s'", ippGetName(attr
), value
);
6229 /* value tag mismatch between fromattr and attr */
6232 add_stringf(errors
, "GOT: %s OF-TYPE %s", ippGetName(attr
), ippTagString(ippGetValueTag(attr
)));