1 /*
2 * ipptool command for CUPS.
3 *
4 * Copyright © 2021-2024 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.
8 *
9 * Licensed under Apache License v2.0. See the file "LICENSE" for more
10 * information.
11 */
12
13 /*
14 * Include necessary headers...
15 */
16
17 #include <cups/cups-private.h>
18 #include <regex.h>
19 #include <sys/stat.h>
20 #ifdef _WIN32
21 # include <windows.h>
22 # ifndef R_OK
23 # define R_OK 0
24 # endif /* !R_OK */
25 #else
26 # include <signal.h>
27 # include <termios.h>
28 #endif /* _WIN32 */
29 #ifndef O_BINARY
30 # define O_BINARY 0
31 #endif /* !O_BINARY */
32
33
34 /*
35 * Limits...
36 */
37
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
41
42
43 /*
44 * Types...
45 */
46
47 typedef enum ipptool_transfer_e /**** How to send request data ****/
48 {
49 IPPTOOL_TRANSFER_AUTO, /* Chunk for files, length for static */
50 IPPTOOL_TRANSFER_CHUNKED, /* Chunk always */
51 IPPTOOL_TRANSFER_LENGTH /* Length always */
52 } ipptool_transfer_t;
53
54 typedef enum ipptool_output_e /**** Output mode ****/
55 {
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 */
62 IPPTOOL_OUTPUT_JSON /* JSON output */
63 } ipptool_output_t;
64
65 typedef enum ipptool_with_e /**** WITH flags ****/
66 {
67 IPPTOOL_WITH_LITERAL = 0, /* Match string is a literal value */
68 IPPTOOL_WITH_ALL = 1, /* Must match all values */
69 IPPTOOL_WITH_REGEX = 2, /* Match string is a regular expression */
70 IPPTOOL_WITH_HOSTNAME = 4, /* Match string is a URI hostname */
71 IPPTOOL_WITH_RESOURCE = 8, /* Match string is a URI resource */
72 IPPTOOL_WITH_SCHEME = 16 /* Match string is a URI scheme */
73 } ipptool_with_t;
74
75 typedef struct ipptool_expect_s /**** Expected attribute info ****/
76 {
77 int optional, /* Optional attribute? */
78 not_expect, /* Don't expect attribute? */
79 expect_all; /* Expect all attributes to match/not match */
80 char *name, /* Attribute name */
81 *of_type, /* Type name */
82 *same_count_as, /* Parallel attribute name */
83 *if_defined, /* Only required if variable defined */
84 *if_not_defined, /* Only required if variable is not defined */
85 *with_value, /* Attribute must include this value */
86 *with_value_from, /* Attribute must have one of the values in this attribute */
87 *define_match, /* Variable to define on match */
88 *define_no_match, /* Variable to define on no-match */
89 *define_value, /* Variable to define with value */
90 *display_match; /* Message to display on a match */
91 int repeat_limit, /* Maximum number of times to repeat */
92 repeat_match, /* Repeat test on match */
93 repeat_no_match, /* Repeat test on no match */
94 with_distinct, /* WITH-DISTINCT-VALUES? */
95 with_flags, /* WITH flags */
96 count; /* Expected count if > 0 */
97 ipp_tag_t in_group; /* IN-GROUP value */
98 } ipptool_expect_t;
99
100 typedef struct ipptool_status_s /**** Status info ****/
101 {
102 ipp_status_t status; /* Expected status code */
103 char *if_defined, /* Only if variable is defined */
104 *if_not_defined, /* Only if variable is not defined */
105 *define_match, /* Variable to define on match */
106 *define_no_match, /* Variable to define on no-match */
107 *define_value; /* Variable to define with value */
108 int repeat_limit, /* Maximum number of times to repeat */
109 repeat_match, /* Repeat the test when it does not match */
110 repeat_no_match; /* Repeat the test when it matches */
111 } ipptool_status_t;
112
113 typedef struct ipptool_test_s /**** Test Data ****/
114 {
115 /* Global Options */
116 _ipp_vars_t *vars; /* Variables */
117 http_encryption_t encryption; /* Encryption for connection */
118 int family; /* Address family */
119 ipptool_output_t output; /* Output mode */
120 int repeat_on_busy; /* Repeat tests on server-error-busy */
121 int stop_after_include_error;
122 /* Stop after include errors? */
123 double timeout; /* Timeout for connection */
124 int validate_headers, /* Validate HTTP headers in response? */
125 verbosity; /* Show all attributes? */
126
127 /* Test Defaults */
128 int def_ignore_errors; /* Default IGNORE-ERRORS value */
129 ipptool_transfer_t def_transfer; /* Default TRANSFER value */
130 int def_version; /* Default IPP version */
131
132 /* Global State */
133 http_t *http; /* HTTP connection to printer/server */
134 cups_file_t *outfile; /* Output file */
135 int show_header, /* Show the test header? */
136 xml_header; /* 1 if XML plist header was written */
137 int pass, /* Have we passed all tests? */
138 test_count, /* Number of tests (total) */
139 pass_count, /* Number of tests that passed */
140 fail_count, /* Number of tests that failed */
141 skip_count; /* Number of tests that were skipped */
142
143 /* Per-Test State */
144 cups_array_t *errors; /* Errors array */
145 int prev_pass, /* Result of previous test */
146 skip_previous; /* Skip on previous test failure? */
147 char compression[16]; /* COMPRESSION value */
148 useconds_t delay; /* Initial delay */
149 int num_displayed; /* Number of displayed attributes */
150 char *displayed[MAX_DISPLAY];/* Displayed attributes */
151 int num_expects; /* Number of expected attributes */
152 ipptool_expect_t expects[MAX_EXPECT], /* Expected attributes */
153 *expect, /* Current expected attribute */
154 *last_expect; /* Last EXPECT (for predicates) */
155 char file[1024], /* Data filename */
156 file_id[1024]; /* File identifier */
157 int ignore_errors; /* Ignore test failures? */
158 char name[1024]; /* Test name */
159 char pause[1024]; /* PAUSE value */
160 useconds_t repeat_interval; /* Repeat interval (delay) */
161 int request_id; /* Current request ID */
162 char resource[512]; /* Resource for request */
163 int pass_test, /* Pass this test? */
164 skip_test, /* Skip this test? */
165 num_statuses; /* Number of valid status codes */
166 ipptool_status_t statuses[100], /* Valid status codes */
167 *last_status; /* Last STATUS (for predicates) */
168 char test_id[1024]; /* Test identifier */
169 ipptool_transfer_t transfer; /* To chunk or not to chunk */
170 int version; /* IPP version number to use */
171 _cups_thread_t monitor_thread; /* Monitoring thread ID */
172 int monitor_done; /* Set to 1 to stop monitor thread */
173 char *monitor_uri; /* MONITOR-PRINTER-STATE URI */
174 useconds_t monitor_delay, /* MONITOR-PRINTER-STATE DELAY value, if any */
175 monitor_interval; /* MONITOR-PRINTER-STATE DELAY interval */
176 int num_monitor_expects; /* Number MONITOR-PRINTER-STATE EXPECTs */
177 ipptool_expect_t monitor_expects[MAX_MONITOR];
178 /* MONITOR-PRINTER-STATE EXPECTs */
179 } ipptool_test_t;
180
181
182 /*
183 * Globals...
184 */
185
186 static int Cancel = 0; /* Cancel test? */
187
188
189 /*
190 * Local functions...
191 */
192
193 static void add_stringf(cups_array_t *a, const char *s, ...) _CUPS_FORMAT(2, 3);
194 static int compare_uris(const char *a, const char *b);
195 static void copy_hex_string(char *buffer, unsigned char *data, int datalen, size_t bufsize);
196 static void *do_monitor_printer_state(ipptool_test_t *data);
197 static int do_test(_ipp_file_t *f, ipptool_test_t *data);
198 static int do_tests(const char *testfile, ipptool_test_t *data);
199 static int error_cb(_ipp_file_t *f, ipptool_test_t *data, const char *error);
200 static int expect_matches(ipptool_expect_t *expect, ipp_attribute_t *attr);
201 static char *get_filename(const char *testfile, char *dst, const char *src, size_t dstsize);
202 static const char *get_string(ipp_attribute_t *attr, int element, int flags, char *buffer, size_t bufsize);
203 static void init_data(ipptool_test_t *data);
204 static char *iso_date(const ipp_uchar_t *date);
205 static int parse_monitor_printer_state(_ipp_file_t *f, ipptool_test_t *data);
206 static void pause_message(const char *message);
207 static void print_attr(cups_file_t *outfile, ipptool_output_t output, ipp_attribute_t *attr, ipp_tag_t *group);
208 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);
209 static void print_fatal_error(ipptool_test_t *data, const char *s, ...) _CUPS_FORMAT(2, 3);
210 static void print_ippserver_attr(ipptool_test_t *data, ipp_attribute_t *attr, int indent);
211 static void print_ippserver_string(ipptool_test_t *data, const char *s, size_t len);
212 static void print_json_attr(ipptool_test_t *data, ipp_attribute_t *attr, int indent);
213 static void print_json_string(ipptool_test_t *data, const char *s, size_t len);
214 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);
215 static void print_xml_header(ipptool_test_t *data);
216 static void print_xml_string(cups_file_t *outfile, const char *element, const char *s);
217 static void print_xml_trailer(ipptool_test_t *data, int success, const char *message);
218 #ifndef _WIN32
219 static void sigterm_handler(int sig);
220 #endif /* _WIN32 */
221 static int timeout_cb(http_t *http, void *user_data);
222 static int token_cb(_ipp_file_t *f, _ipp_vars_t *vars, ipptool_test_t *data, const char *token);
223 static void usage(void) _CUPS_NORETURN;
224 static int with_distinct_values(cups_array_t *errors, ipp_attribute_t *attr);
225 static const char *with_flags_string(int flags);
226 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);
227 static int with_value_from(cups_array_t *errors, ipp_attribute_t *fromattr, ipp_attribute_t *attr, char *matchbuf, size_t matchlen);
228
229
230 /*
231 * 'main()' - Parse options and do tests.
232 */
233
234 int /* O - Exit status */
main(int argc,char * argv[])235 main(int argc, /* I - Number of command-line args */
236 char *argv[]) /* I - Command-line arguments */
237 {
238 int i; /* Looping var */
239 int status; /* Status of tests... */
240 char *opt, /* Current option */
241 name[1024], /* Name/value buffer */
242 *value, /* Pointer to value */
243 filename[1024], /* Real filename */
244 testname[1024]; /* Real test filename */
245 const char *ext, /* Extension on filename */
246 *testfile; /* Test file to use */
247 int interval, /* Test interval in microseconds */
248 repeat; /* Repeat count */
249 _ipp_vars_t vars; /* Variables */
250 ipptool_test_t data; /* Test data */
251 _cups_globals_t *cg = _cupsGlobals();
252 /* Global data */
253
254
255 #ifndef _WIN32
256 /*
257 * Catch SIGINT and SIGTERM...
258 */
259
260 signal(SIGINT, sigterm_handler);
261 signal(SIGTERM, sigterm_handler);
262 #endif /* !_WIN32 */
263
264 /*
265 * Initialize the locale and variables...
266 */
267
268 _cupsSetLocale(argv);
269
270 init_data(&data);
271
272 _ippVarsInit(&vars, NULL, (_ipp_ferror_cb_t)error_cb, (_ipp_ftoken_cb_t)token_cb);
273 data.vars = &vars;
274
275 _ippVarsSet(data.vars, "date-start", iso_date(ippTimeToDate(time(NULL))));
276
277 /*
278 * We need at least:
279 *
280 * ipptool URI testfile
281 */
282
283 interval = 0;
284 repeat = 0;
285 status = 0;
286 testfile = NULL;
287
288 for (i = 1; i < argc; i ++)
289 {
290 if (!strcmp(argv[i], "--help"))
291 {
292 usage();
293 }
294 else if (!strcmp(argv[i], "--ippserver"))
295 {
296 i ++;
297
298 if (i >= argc)
299 {
300 _cupsLangPuts(stderr, _("ipptool: Missing filename for \"--ippserver\"."));
301 usage();
302 }
303
304 if (data.outfile != cupsFileStdout())
305 usage();
306
307 if ((data.outfile = cupsFileOpen(argv[i], "w")) == NULL)
308 {
309 _cupsLangPrintf(stderr, _("%s: Unable to open \"%s\": %s"), "ipptool", argv[i], strerror(errno));
310 exit(1);
311 }
312
313 data.output = IPPTOOL_OUTPUT_IPPSERVER;
314 }
315 else if (!strcmp(argv[i], "--stop-after-include-error"))
316 {
317 data.stop_after_include_error = 1;
318 }
319 else if (!strcmp(argv[i], "--version"))
320 {
321 puts(CUPS_SVERSION);
322 return (0);
323 }
324 else if (argv[i][0] == '-')
325 {
326 for (opt = argv[i] + 1; *opt; opt ++)
327 {
328 switch (*opt)
329 {
330 case '4' : /* Connect using IPv4 only */
331 data.family = AF_INET;
332 break;
333
334 #ifdef AF_INET6
335 case '6' : /* Connect using IPv6 only */
336 data.family = AF_INET6;
337 break;
338 #endif /* AF_INET6 */
339
340 case 'C' : /* Enable HTTP chunking */
341 data.def_transfer = IPPTOOL_TRANSFER_CHUNKED;
342 break;
343
344 case 'E' : /* Encrypt with TLS */
345 #ifdef HAVE_TLS
346 data.encryption = HTTP_ENCRYPT_REQUIRED;
347 #else
348 _cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."),
349 argv[0]);
350 #endif /* HAVE_TLS */
351 break;
352
353 case 'I' : /* Ignore errors */
354 data.def_ignore_errors = 1;
355 break;
356
357 case 'L' : /* Disable HTTP chunking */
358 data.def_transfer = IPPTOOL_TRANSFER_LENGTH;
359 break;
360
361 case 'P' : /* Output to plist file */
362 i ++;
363
364 if (i >= argc)
365 {
366 _cupsLangPrintf(stderr, _("%s: Missing filename for \"-P\"."), "ipptool");
367 usage();
368 }
369
370 if (data.outfile != cupsFileStdout())
371 usage();
372
373 if ((data.outfile = cupsFileOpen(argv[i], "w")) == NULL)
374 {
375 _cupsLangPrintf(stderr, _("%s: Unable to open \"%s\": %s"), "ipptool", argv[i], strerror(errno));
376 exit(1);
377 }
378
379 data.output = IPPTOOL_OUTPUT_PLIST;
380
381 if (interval || repeat)
382 {
383 _cupsLangPuts(stderr, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
384 usage();
385 }
386 break;
387
388 case 'R' : /* Repeat on server-error-busy */
389 data.repeat_on_busy = 1;
390 break;
391
392 case 'S' : /* Encrypt with SSL */
393 #ifdef HAVE_TLS
394 data.encryption = HTTP_ENCRYPT_ALWAYS;
395 #else
396 _cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."), "ipptool");
397 #endif /* HAVE_TLS */
398 break;
399
400 case 'T' : /* Set timeout */
401 i ++;
402
403 if (i >= argc)
404 {
405 _cupsLangPrintf(stderr, _("%s: Missing timeout for \"-T\"."), "ipptool");
406 usage();
407 }
408
409 data.timeout = _cupsStrScand(argv[i], NULL, localeconv());
410 break;
411
412 case 'V' : /* Set IPP version */
413 i ++;
414
415 if (i >= argc)
416 {
417 _cupsLangPrintf(stderr, _("%s: Missing version for \"-V\"."), "ipptool");
418 usage();
419 }
420
421 if (!strcmp(argv[i], "1.0"))
422 {
423 data.def_version = 10;
424 }
425 else if (!strcmp(argv[i], "1.1"))
426 {
427 data.def_version = 11;
428 }
429 else if (!strcmp(argv[i], "2.0"))
430 {
431 data.def_version = 20;
432 }
433 else if (!strcmp(argv[i], "2.1"))
434 {
435 data.def_version = 21;
436 }
437 else if (!strcmp(argv[i], "2.2"))
438 {
439 data.def_version = 22;
440 }
441 else
442 {
443 _cupsLangPrintf(stderr, _("%s: Bad version %s for \"-V\"."), "ipptool", argv[i]);
444 usage();
445 }
446 break;
447
448 case 'X' : /* Produce XML output */
449 data.output = IPPTOOL_OUTPUT_PLIST;
450
451 if (interval || repeat)
452 {
453 _cupsLangPuts(stderr, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
454 usage();
455 }
456 break;
457
458 case 'c' : /* CSV output */
459 data.output = IPPTOOL_OUTPUT_CSV;
460 break;
461
462 case 'd' : /* Define a variable */
463 i ++;
464
465 if (i >= argc)
466 {
467 _cupsLangPuts(stderr, _("ipptool: Missing name=value for \"-d\"."));
468 usage();
469 }
470
471 strlcpy(name, argv[i], sizeof(name));
472 if ((value = strchr(name, '=')) != NULL)
473 *value++ = '\0';
474 else
475 value = name + strlen(name);
476
477 _ippVarsSet(data.vars, name, value);
478 break;
479
480 case 'f' : /* Set the default test filename */
481 i ++;
482
483 if (i >= argc)
484 {
485 _cupsLangPuts(stderr, _("ipptool: Missing filename for \"-f\"."));
486 usage();
487 }
488
489 if (access(argv[i], 0))
490 {
491 /*
492 * Try filename.gz...
493 */
494
495 snprintf(filename, sizeof(filename), "%s.gz", argv[i]);
496 if (access(filename, 0) && filename[0] != '/'
497 #ifdef _WIN32
498 && (!isalpha(filename[0] & 255) || filename[1] != ':')
499 #endif /* _WIN32 */
500 )
501 {
502 snprintf(filename, sizeof(filename), "%s/ipptool/%s", cg->cups_datadir, argv[i]);
503 if (access(filename, 0))
504 {
505 snprintf(filename, sizeof(filename), "%s/ipptool/%s.gz", cg->cups_datadir, argv[i]);
506 if (access(filename, 0))
507 strlcpy(filename, argv[i], sizeof(filename));
508 }
509 }
510 }
511 else
512 strlcpy(filename, argv[i], sizeof(filename));
513
514 _ippVarsSet(data.vars, "filename", filename);
515
516 if ((ext = strrchr(filename, '.')) != NULL)
517 {
518 /*
519 * Guess the MIME media type based on the extension...
520 */
521
522 if (!_cups_strcasecmp(ext, ".gif"))
523 _ippVarsSet(data.vars, "filetype", "image/gif");
524 else if (!_cups_strcasecmp(ext, ".htm") ||
525 !_cups_strcasecmp(ext, ".htm.gz") ||
526 !_cups_strcasecmp(ext, ".html") ||
527 !_cups_strcasecmp(ext, ".html.gz"))
528 _ippVarsSet(data.vars, "filetype", "text/html");
529 else if (!_cups_strcasecmp(ext, ".jpg") ||
530 !_cups_strcasecmp(ext, ".jpeg"))
531 _ippVarsSet(data.vars, "filetype", "image/jpeg");
532 else if (!_cups_strcasecmp(ext, ".pcl") ||
533 !_cups_strcasecmp(ext, ".pcl.gz"))
534 _ippVarsSet(data.vars, "filetype", "application/vnd.hp-PCL");
535 else if (!_cups_strcasecmp(ext, ".pdf"))
536 _ippVarsSet(data.vars, "filetype", "application/pdf");
537 else if (!_cups_strcasecmp(ext, ".png"))
538 _ippVarsSet(data.vars, "filetype", "image/png");
539 else if (!_cups_strcasecmp(ext, ".ps") ||
540 !_cups_strcasecmp(ext, ".ps.gz"))
541 _ippVarsSet(data.vars, "filetype", "application/postscript");
542 else if (!_cups_strcasecmp(ext, ".pwg") ||
543 !_cups_strcasecmp(ext, ".pwg.gz") ||
544 !_cups_strcasecmp(ext, ".ras") ||
545 !_cups_strcasecmp(ext, ".ras.gz"))
546 _ippVarsSet(data.vars, "filetype", "image/pwg-raster");
547 else if (!_cups_strcasecmp(ext, ".tif") ||
548 !_cups_strcasecmp(ext, ".tiff"))
549 _ippVarsSet(data.vars, "filetype", "image/tiff");
550 else if (!_cups_strcasecmp(ext, ".txt") ||
551 !_cups_strcasecmp(ext, ".txt.gz"))
552 _ippVarsSet(data.vars, "filetype", "text/plain");
553 else if (!_cups_strcasecmp(ext, ".urf") ||
554 !_cups_strcasecmp(ext, ".urf.gz"))
555 _ippVarsSet(data.vars, "filetype", "image/urf");
556 else if (!_cups_strcasecmp(ext, ".xps"))
557 _ippVarsSet(data.vars, "filetype", "application/openxps");
558 else
559 _ippVarsSet(data.vars, "filetype", "application/octet-stream");
560 }
561 else
562 {
563 /*
564 * Use the "auto-type" MIME media type...
565 */
566
567 _ippVarsSet(data.vars, "filetype", "application/octet-stream");
568 }
569 break;
570
571 case 'h' : /* Validate response headers */
572 data.validate_headers = 1;
573 break;
574
575 case 'i' : /* Test every N seconds */
576 i ++;
577
578 if (i >= argc)
579 {
580 _cupsLangPuts(stderr, _("ipptool: Missing seconds for \"-i\"."));
581 usage();
582 }
583 else
584 {
585 interval = (int)(_cupsStrScand(argv[i], NULL, localeconv()) * 1000000.0);
586 if (interval <= 0)
587 {
588 _cupsLangPuts(stderr, _("ipptool: Invalid seconds for \"-i\"."));
589 usage();
590 }
591 }
592
593 if ((data.output == IPPTOOL_OUTPUT_PLIST || data.output == IPPTOOL_OUTPUT_IPPSERVER) && interval)
594 {
595 _cupsLangPuts(stderr, _("ipptool: \"-i\" and \"-n\" are incompatible with \"--ippserver\", \"-P\", and \"-X\"."));
596 usage();
597 }
598 break;
599
600 case 'j' : /* JSON output */
601 data.output = IPPTOOL_OUTPUT_JSON;
602 break;
603
604 case 'l' : /* List as a table */
605 data.output = IPPTOOL_OUTPUT_LIST;
606 break;
607
608 case 'n' : /* Repeat count */
609 i ++;
610
611 if (i >= argc)
612 {
613 _cupsLangPuts(stderr, _("ipptool: Missing count for \"-n\"."));
614 usage();
615 }
616 else
617 repeat = atoi(argv[i]);
618
619 if ((data.output == IPPTOOL_OUTPUT_PLIST || data.output == IPPTOOL_OUTPUT_IPPSERVER) && repeat)
620 {
621 _cupsLangPuts(stderr, _("ipptool: \"-i\" and \"-n\" are incompatible with \"--ippserver\", \"-P\", and \"-X\"."));
622 usage();
623 }
624 break;
625
626 case 'q' : /* Be quiet */
627 data.output = IPPTOOL_OUTPUT_QUIET;
628 break;
629
630 case 't' : /* CUPS test output */
631 data.output = IPPTOOL_OUTPUT_TEST;
632 break;
633
634 case 'v' : /* Be verbose */
635 data.verbosity ++;
636 break;
637
638 default :
639 _cupsLangPrintf(stderr, _("%s: Unknown option \"-%c\"."), "ipptool", *opt);
640 usage();
641 }
642 }
643 }
644 else if (!strncmp(argv[i], "ipp://", 6) || !strncmp(argv[i], "http://", 7)
645 #ifdef HAVE_TLS
646 || !strncmp(argv[i], "ipps://", 7) || !strncmp(argv[i], "https://", 8)
647 #endif /* HAVE_TLS */
648 )
649 {
650 /*
651 * Set URI...
652 */
653
654 if (data.vars->uri)
655 {
656 _cupsLangPuts(stderr, _("ipptool: May only specify a single URI."));
657 usage();
658 }
659
660 #ifdef HAVE_TLS
661 if (!strncmp(argv[i], "ipps://", 7) || !strncmp(argv[i], "https://", 8))
662 data.encryption = HTTP_ENCRYPT_ALWAYS;
663 #endif /* HAVE_TLS */
664
665 if (!_ippVarsSet(data.vars, "uri", argv[i]))
666 {
667 _cupsLangPrintf(stderr, _("ipptool: Bad URI \"%s\"."), argv[i]);
668 return (1);
669 }
670
671 if (data.vars->username[0] && data.vars->password)
672 cupsSetPasswordCB2(_ippVarsPasswordCB, data.vars);
673 }
674 else
675 {
676 /*
677 * Run test...
678 */
679
680 if (!data.vars->uri)
681 {
682 _cupsLangPuts(stderr, _("ipptool: URI required before test file."));
683 _cupsLangPuts(stderr, argv[i]);
684 usage();
685 }
686
687 if (access(argv[i], 0) && argv[i][0] != '/'
688 #ifdef _WIN32
689 && (!isalpha(argv[i][0] & 255) || argv[i][1] != ':')
690 #endif /* _WIN32 */
691 )
692 {
693 snprintf(testname, sizeof(testname), "%s/ipptool/%s", cg->cups_datadir, argv[i]);
694 if (access(testname, 0))
695 testfile = argv[i];
696 else
697 testfile = testname;
698 }
699 else
700 testfile = argv[i];
701
702 if (access(testfile, 0))
703 {
704 _cupsLangPrintf(stderr, _("%s: Unable to open \"%s\": %s"), "ipptool", testfile, strerror(errno));
705 status = 1;
706 }
707 else if (!do_tests(testfile, &data))
708 status = 1;
709 }
710 }
711
712 if (!data.vars->uri || !testfile)
713 usage();
714
715 /*
716 * Loop if the interval is set...
717 */
718
719 if (data.output == IPPTOOL_OUTPUT_PLIST)
720 print_xml_trailer(&data, !status, NULL);
721 else if (interval > 0 && repeat > 0)
722 {
723 while (repeat > 1)
724 {
725 usleep((useconds_t)interval);
726 do_tests(testfile, &data);
727 repeat --;
728 }
729 }
730 else if (interval > 0)
731 {
732 for (;;)
733 {
734 usleep((useconds_t)interval);
735 do_tests(testfile, &data);
736 }
737 }
738
739 if ((data.output == IPPTOOL_OUTPUT_TEST || (data.output == IPPTOOL_OUTPUT_PLIST && data.outfile)) && data.test_count > 1)
740 {
741 /*
742 * Show a summary report if there were multiple tests...
743 */
744
745 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);
746 }
747
748 cupsFileClose(data.outfile);
749
750 /*
751 * Exit...
752 */
753
754 return (status);
755 }
756
757
758 /*
759 * 'add_stringf()' - Add a formatted string to an array.
760 */
761
762 static void
add_stringf(cups_array_t * a,const char * s,...)763 add_stringf(cups_array_t *a, /* I - Array */
764 const char *s, /* I - Printf-style format string */
765 ...) /* I - Additional args as needed */
766 {
767 char buffer[10240]; /* Format buffer */
768 va_list ap; /* Argument pointer */
769
770
771 /*
772 * Don't bother is the array is NULL...
773 */
774
775 if (!a)
776 return;
777
778 /*
779 * Format the message...
780 */
781
782 va_start(ap, s);
783 vsnprintf(buffer, sizeof(buffer), s, ap);
784 va_end(ap);
785
786 /*
787 * Add it to the array...
788 */
789
790 cupsArrayAdd(a, buffer);
791 }
792
793
794 /*
795 * 'compare_uris()' - Compare two URIs...
796 */
797
798 static int /* O - Result of comparison */
compare_uris(const char * a,const char * b)799 compare_uris(const char *a, /* I - First URI */
800 const char *b) /* I - Second URI */
801 {
802 char ascheme[32], /* Components of first URI */
803 auserpass[256],
804 ahost[256],
805 aresource[256];
806 int aport;
807 char bscheme[32], /* Components of second URI */
808 buserpass[256],
809 bhost[256],
810 bresource[256];
811 int bport;
812 char *ptr; /* Pointer into string */
813 int result; /* Result of comparison */
814
815
816 /*
817 * Separate the URIs into their components...
818 */
819
820 if (httpSeparateURI(HTTP_URI_CODING_ALL, a, ascheme, sizeof(ascheme), auserpass, sizeof(auserpass), ahost, sizeof(ahost), &aport, aresource, sizeof(aresource)) < HTTP_URI_STATUS_OK)
821 return (-1);
822
823 if (httpSeparateURI(HTTP_URI_CODING_ALL, b, bscheme, sizeof(bscheme), buserpass, sizeof(buserpass), bhost, sizeof(bhost), &bport, bresource, sizeof(bresource)) < HTTP_URI_STATUS_OK)
824 return (-1);
825
826 /*
827 * Strip trailing dots from the host components, if present...
828 */
829
830 if ((ptr = ahost + strlen(ahost) - 1) > ahost && *ptr == '.')
831 *ptr = '\0';
832
833 if ((ptr = bhost + strlen(bhost) - 1) > bhost && *ptr == '.')
834 *ptr = '\0';
835
836 /*
837 * Compare each component...
838 */
839
840 if ((result = _cups_strcasecmp(ascheme, bscheme)) != 0)
841 return (result);
842
843 if ((result = strcmp(auserpass, buserpass)) != 0)
844 return (result);
845
846 if ((result = _cups_strcasecmp(ahost, bhost)) != 0)
847 return (result);
848
849 if (aport != bport)
850 return (aport - bport);
851
852 if (!_cups_strcasecmp(ascheme, "mailto") || !_cups_strcasecmp(ascheme, "urn"))
853 return (_cups_strcasecmp(aresource, bresource));
854 else
855 return (strcmp(aresource, bresource));
856 }
857
858
859 /*
860 * 'copy_hex_string()' - Copy an octetString to a C string and encode as hex if
861 * needed.
862 */
863
864 static void
copy_hex_string(char * buffer,unsigned char * data,int datalen,size_t bufsize)865 copy_hex_string(char *buffer, /* I - String buffer */
866 unsigned char *data, /* I - octetString data */
867 int datalen, /* I - octetString length */
868 size_t bufsize) /* I - Size of string buffer */
869 {
870 char *bufptr, /* Pointer into string buffer */
871 *bufend = buffer + bufsize - 2;
872 /* End of string buffer */
873 unsigned char *dataptr, /* Pointer into octetString data */
874 *dataend = data + datalen;
875 /* End of octetString data */
876 static const char *hexdigits = "0123456789ABCDEF";
877 /* Hex digits */
878
879
880 /*
881 * First see if there are any non-ASCII bytes in the octetString...
882 */
883
884 for (dataptr = data; dataptr < dataend; dataptr ++)
885 if (*dataptr < 0x20 || *dataptr >= 0x7f)
886 break;
887
888 if (dataptr < dataend)
889 {
890 /*
891 * Yes, encode as hex...
892 */
893
894 *buffer = '<';
895
896 for (bufptr = buffer + 1, dataptr = data; bufptr < bufend && dataptr < dataend; dataptr ++)
897 {
898 *bufptr++ = hexdigits[*dataptr >> 4];
899 *bufptr++ = hexdigits[*dataptr & 15];
900 }
901
902 if (bufptr < bufend)
903 *bufptr++ = '>';
904
905 *bufptr = '\0';
906 }
907 else
908 {
909 /*
910 * No, copy as a string...
911 */
912
913 if ((size_t)datalen > bufsize)
914 datalen = (int)bufsize - 1;
915
916 memcpy(buffer, data, (size_t)datalen);
917 buffer[datalen] = '\0';
918 }
919 }
920
921
922 /*
923 * 'do_monitor_printer_state()' - Do the MONITOR-PRINTER-STATE tests in the background.
924 */
925
926 static void * // O - Thread exit status
do_monitor_printer_state(ipptool_test_t * data)927 do_monitor_printer_state(
928 ipptool_test_t *data) // I - Test data
929 {
930 int i, j; // Looping vars
931 char scheme[32], // URI scheme
932 userpass[32], // URI username:password
933 host[256], // URI hostname/IP address
934 resource[256]; // URI resource path
935 int port; // URI port number
936 http_encryption_t encryption; // Encryption to use
937 http_t *http; // Connection to printer
938 ipp_t *request, // IPP request
939 *response = NULL; // IPP response
940 http_status_t status; // Request status
941 ipp_attribute_t *found; // Found attribute
942 ipptool_expect_t *expect; // Current EXPECT test
943 char buffer[131072]; // Copy buffer
944 int num_pattrs; // Number of printer attributes
945 const char *pattrs[100]; // Printer attributes we care about
946
947
948 // Connect to the printer...
949 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)
950 {
951 print_fatal_error(data, "Bad printer URI \"%s\".", data->monitor_uri);
952 return (NULL);
953 }
954
955 if (!_cups_strcasecmp(scheme, "https") || !_cups_strcasecmp(scheme, "ipps") || port == 443)
956 encryption = HTTP_ENCRYPTION_ALWAYS;
957 else
958 encryption = data->encryption;
959
960 if ((http = httpConnect2(host, port, NULL, data->family, encryption, 1, 30000, NULL)) == NULL)
961 {
962 print_fatal_error(data, "Unable to connect to \"%s\" on port %d - %s", host, port, cupsLastErrorString());
963 return (0);
964 }
965
966 #ifdef HAVE_LIBZ
967 httpSetDefaultField(http, HTTP_FIELD_ACCEPT_ENCODING, "deflate, gzip, identity");
968 #else
969 httpSetDefaultField(http, HTTP_FIELD_ACCEPT_ENCODING, "identity");
970 #endif /* HAVE_LIBZ */
971
972 if (data->timeout > 0.0)
973 httpSetTimeout(http, data->timeout, timeout_cb, NULL);
974
975 // Wait for the initial delay as needed...
976 if (data->monitor_delay)
977 usleep(data->monitor_delay);
978
979 // Create a query request that we'll reuse...
980 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
981 ippSetRequestId(request, data->request_id * 100 - 1);
982 ippSetVersion(request, data->version / 10, data->version % 10);
983 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, data->monitor_uri);
984 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
985
986 for (i = data->num_monitor_expects, expect = data->monitor_expects, num_pattrs = 0; i > 0; i --, expect ++)
987 {
988 // Add EXPECT attribute names...
989 for (j = 0; j < num_pattrs; j ++)
990 {
991 if (!strcmp(expect->name, pattrs[j]))
992 break;
993 }
994
995 if (j >= num_pattrs && num_pattrs < (int)(sizeof(pattrs) / sizeof(pattrs[0])))
996 pattrs[num_pattrs ++] = expect->name;
997 }
998
999 if (num_pattrs > 0)
1000 ippAddStrings(request, IPP_TAG_OPERATION, IPP_CONST_TAG(IPP_TAG_KEYWORD), "requested-attributes", num_pattrs, NULL, pattrs);
1001
1002 // Loop until we need to stop...
1003 while (!data->monitor_done && !Cancel)
1004 {
1005 // Poll the printer state...
1006 ippSetRequestId(request, ippGetRequestId(request) + 1);
1007
1008 if ((status = cupsSendRequest(http, request, resource, ippLength(request))) != HTTP_STATUS_ERROR)
1009 {
1010 response = cupsGetResponse(http, resource);
1011 status = httpGetStatus(http);
1012 }
1013
1014 if (!data->monitor_done && !Cancel && status == HTTP_STATUS_ERROR && httpError(data->http) != EINVAL &&
1015 #ifdef _WIN32
1016 httpError(data->http) != WSAETIMEDOUT)
1017 #else
1018 httpError(data->http) != ETIMEDOUT)
1019 #endif // _WIN32
1020 {
1021 if (httpReconnect2(http, 30000, NULL))
1022 break;
1023 }
1024 else if (status == HTTP_STATUS_ERROR || status == HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED)
1025 {
1026 break;
1027 }
1028 else if (status != HTTP_STATUS_OK)
1029 {
1030 httpFlush(http);
1031
1032 if (status == HTTP_STATUS_UNAUTHORIZED)
1033 continue;
1034
1035 break;
1036 }
1037
1038 for (i = data->num_monitor_expects, expect = data->monitor_expects; i > 0; i --, expect ++)
1039 {
1040 if (expect->if_defined && !_ippVarsGet(data->vars, expect->if_defined))
1041 continue;
1042
1043 if (expect->if_not_defined && _ippVarsGet(data->vars, expect->if_not_defined))
1044 continue;
1045
1046 found = ippFindAttribute(response, expect->name, IPP_TAG_ZERO);
1047
1048 if ((found && expect->not_expect) ||
1049 (!found && !(expect->not_expect || expect->optional)) ||
1050 (found && !expect_matches(expect, found)) ||
1051 (expect->in_group && ippGetGroupTag(found) != expect->in_group) ||
1052 (expect->with_distinct && !with_distinct_values(NULL, found)))
1053 {
1054 if (expect->define_no_match)
1055 {
1056 _ippVarsSet(data->vars, expect->define_no_match, "1");
1057 data->monitor_done = 1;
1058 }
1059 break;
1060 }
1061
1062 if (found)
1063 ippAttributeString(found, buffer, sizeof(buffer));
1064
1065 if (found && !with_value(data, NULL, expect->with_value, expect->with_flags, found, buffer, sizeof(buffer)))
1066 {
1067 if (expect->define_no_match)
1068 {
1069 _ippVarsSet(data->vars, expect->define_no_match, "1");
1070 data->monitor_done = 1;
1071 }
1072 break;
1073 }
1074
1075 if (found && expect->count > 0 && ippGetCount(found) != expect->count)
1076 {
1077 if (expect->define_no_match)
1078 {
1079 _ippVarsSet(data->vars, expect->define_no_match, "1");
1080 data->monitor_done = 1;
1081 }
1082 break;
1083 }
1084
1085 if (found && expect->display_match && (data->output == IPPTOOL_OUTPUT_TEST || (data->output == IPPTOOL_OUTPUT_PLIST && data->outfile != cupsFileStdout())))
1086 cupsFilePrintf(cupsFileStdout(), "CONT]\n\n%s\n\n %-68.68s [", expect->display_match, data->name);
1087
1088 if (found && expect->define_match)
1089 {
1090 _ippVarsSet(data->vars, expect->define_match, "1");
1091 data->monitor_done = 1;
1092 }
1093
1094 if (found && expect->define_value)
1095 {
1096 if (!expect->with_value)
1097 {
1098 int last = ippGetCount(found) - 1;
1099 // Last element in attribute
1100
1101 switch (ippGetValueTag(found))
1102 {
1103 case IPP_TAG_ENUM :
1104 case IPP_TAG_INTEGER :
1105 snprintf(buffer, sizeof(buffer), "%d", ippGetInteger(found, last));
1106 break;
1107
1108 case IPP_TAG_BOOLEAN :
1109 if (ippGetBoolean(found, last))
1110 strlcpy(buffer, "true", sizeof(buffer));
1111 else
1112 strlcpy(buffer, "false", sizeof(buffer));
1113 break;
1114
1115 case IPP_TAG_CHARSET :
1116 case IPP_TAG_KEYWORD :
1117 case IPP_TAG_LANGUAGE :
1118 case IPP_TAG_MIMETYPE :
1119 case IPP_TAG_NAME :
1120 case IPP_TAG_NAMELANG :
1121 case IPP_TAG_TEXT :
1122 case IPP_TAG_TEXTLANG :
1123 case IPP_TAG_URI :
1124 case IPP_TAG_URISCHEME :
1125 strlcpy(buffer, ippGetString(found, last, NULL), sizeof(buffer));
1126 break;
1127
1128 default :
1129 ippAttributeString(found, buffer, sizeof(buffer));
1130 break;
1131 }
1132 }
1133
1134 _ippVarsSet(data->vars, expect->define_value, buffer);
1135 data->monitor_done = 1;
1136 }
1137 }
1138
1139 if (i == 0)
1140 data->monitor_done = 1; // All tests passed
1141
1142 ippDelete(response);
1143 response = NULL;
1144
1145 // Sleep between requests...
1146 if (data->monitor_done || Cancel)
1147 break;
1148
1149 usleep(data->monitor_interval);
1150 }
1151
1152 // Close the connection to the printer and return...
1153 httpClose(http);
1154 ippDelete(request);
1155 ippDelete(response);
1156
1157 return (NULL);
1158 }
1159
1160
1161 /*
1162 * 'do_test()' - Do a single test from the test file.
1163 */
1164
1165 static int /* O - 1 on success, 0 on failure */
do_test(_ipp_file_t * f,ipptool_test_t * data)1166 do_test(_ipp_file_t *f, /* I - IPP data file */
1167 ipptool_test_t *data) /* I - Test data */
1168
1169 {
1170 int i, /* Looping var */
1171 status_ok, /* Did we get a matching status? */
1172 repeat_count = 0, /* Repeat count */
1173 repeat_test; /* Repeat the test? */
1174 ipptool_expect_t *expect; /* Current expected attribute */
1175 ipp_t *request, /* IPP request */
1176 *response; /* IPP response */
1177 size_t length; /* Length of IPP request */
1178 http_status_t status; /* HTTP status */
1179 cups_array_t *a; /* Duplicate attribute array */
1180 ipp_tag_t group; /* Current group */
1181 ipp_attribute_t *attrptr, /* Attribute pointer */
1182 *found; /* Found attribute */
1183 char temp[1024]; /* Temporary string */
1184 cups_file_t *reqfile; /* File to send */
1185 ssize_t bytes; /* Bytes read/written */
1186 char buffer[1024 * 1024]; /* Copy buffer */
1187 size_t widths[200]; /* Width of columns */
1188 const char *error; /* Current error */
1189
1190
1191 if (Cancel)
1192 return (0);
1193
1194 /*
1195 * Show any PAUSE message, as needed...
1196 */
1197
1198 if (data->pause[0])
1199 {
1200 if (!data->skip_test && !data->pass_test)
1201 pause_message(data->pause);
1202
1203 data->pause[0] = '\0';
1204 }
1205
1206 /*
1207 * Start the background thread as needed...
1208 */
1209
1210 if (data->monitor_uri)
1211 {
1212 data->monitor_done = 0;
1213 data->monitor_thread = _cupsThreadCreate((_cups_thread_func_t)do_monitor_printer_state, data);
1214 }
1215
1216 /*
1217 * Take over control of the attributes in the request...
1218 */
1219
1220 request = f->attrs;
1221 f->attrs = NULL;
1222
1223 /*
1224 * Submit the IPP request...
1225 */
1226
1227 data->test_count ++;
1228
1229 ippSetVersion(request, data->version / 10, data->version % 10);
1230 ippSetRequestId(request, data->request_id);
1231
1232 if (data->output == IPPTOOL_OUTPUT_PLIST)
1233 {
1234 cupsFilePuts(data->outfile, "<dict>\n");
1235 cupsFilePuts(data->outfile, "<key>Name</key>\n");
1236 print_xml_string(data->outfile, "string", data->name);
1237 if (data->file_id[0])
1238 {
1239 cupsFilePuts(data->outfile, "<key>FileId</key>\n");
1240 print_xml_string(data->outfile, "string", data->file_id);
1241 }
1242 if (data->test_id[0])
1243 {
1244 cupsFilePuts(data->outfile, "<key>TestId</key>\n");
1245 print_xml_string(data->outfile, "string", data->test_id);
1246 }
1247 cupsFilePuts(data->outfile, "<key>Version</key>\n");
1248 cupsFilePrintf(data->outfile, "<string>%d.%d</string>\n", data->version / 10, data->version % 10);
1249 cupsFilePuts(data->outfile, "<key>Operation</key>\n");
1250 print_xml_string(data->outfile, "string", ippOpString(ippGetOperation(request)));
1251 cupsFilePuts(data->outfile, "<key>RequestId</key>\n");
1252 cupsFilePrintf(data->outfile, "<integer>%d</integer>\n", data->request_id);
1253 cupsFilePuts(data->outfile, "<key>RequestAttributes</key>\n");
1254 cupsFilePuts(data->outfile, "<array>\n");
1255 if (ippFirstAttribute(request))
1256 {
1257 cupsFilePuts(data->outfile, "<dict>\n");
1258 for (attrptr = ippFirstAttribute(request), group = ippGetGroupTag(attrptr); attrptr; attrptr = ippNextAttribute(request))
1259 print_attr(data->outfile, data->output, attrptr, &group);
1260 cupsFilePuts(data->outfile, "</dict>\n");
1261 }
1262 cupsFilePuts(data->outfile, "</array>\n");
1263 }
1264
1265 if (data->output == IPPTOOL_OUTPUT_TEST || (data->output == IPPTOOL_OUTPUT_PLIST && data->outfile != cupsFileStdout()))
1266 {
1267 if (data->verbosity)
1268 {
1269 cupsFilePrintf(cupsFileStdout(), " %s:\n", ippOpString(ippGetOperation(request)));
1270
1271 for (attrptr = ippFirstAttribute(request); attrptr; attrptr = ippNextAttribute(request))
1272 print_attr(cupsFileStdout(), IPPTOOL_OUTPUT_TEST, attrptr, NULL);
1273 }
1274
1275 cupsFilePrintf(cupsFileStdout(), " %-68.68s [", data->name);
1276 }
1277
1278 if ((data->skip_previous && !data->prev_pass) || data->skip_test || data->pass_test)
1279 {
1280 if (!data->pass_test)
1281 data->skip_count ++;
1282
1283 ippDelete(request);
1284 request = NULL;
1285 response = NULL;
1286
1287 if (data->output == IPPTOOL_OUTPUT_PLIST)
1288 {
1289 cupsFilePuts(data->outfile, "<key>Successful</key>\n");
1290 cupsFilePuts(data->outfile, "<true />\n");
1291 cupsFilePuts(data->outfile, "<key>Skipped</key>\n");
1292 if (data->pass_test)
1293 cupsFilePuts(data->outfile, "<false />\n");
1294 else
1295 cupsFilePuts(data->outfile, "<true />\n");
1296 cupsFilePuts(data->outfile, "<key>StatusCode</key>\n");
1297 if (data->pass_test)
1298 print_xml_string(data->outfile, "string", "pass");
1299 else
1300 print_xml_string(data->outfile, "string", "skip");
1301 cupsFilePuts(data->outfile, "<key>ResponseAttributes</key>\n");
1302 cupsFilePuts(data->outfile, "<dict />\n");
1303 }
1304
1305 if (data->output == IPPTOOL_OUTPUT_TEST || (data->output == IPPTOOL_OUTPUT_PLIST && data->outfile != cupsFileStdout()))
1306 {
1307 if (data->pass_test)
1308 cupsFilePuts(cupsFileStdout(), "PASS]\n");
1309 else
1310 cupsFilePuts(cupsFileStdout(), "SKIP]\n");
1311 }
1312
1313 goto skip_error;
1314 }
1315
1316 data->vars->password_tries = 0;
1317
1318 do
1319 {
1320 if (data->delay > 0)
1321 usleep(data->delay);
1322
1323 data->delay = data->repeat_interval;
1324 repeat_count ++;
1325
1326 status = HTTP_STATUS_OK;
1327
1328 if (data->transfer == IPPTOOL_TRANSFER_CHUNKED || (data->transfer == IPPTOOL_TRANSFER_AUTO && data->file[0]))
1329 {
1330 /*
1331 * Send request using chunking - a 0 length means "chunk".
1332 */
1333
1334 length = 0;
1335 }
1336 else
1337 {
1338 /*
1339 * Send request using content length...
1340 */
1341
1342 length = ippLength(request);
1343
1344 if (data->file[0] && (reqfile = cupsFileOpen(data->file, "r")) != NULL)
1345 {
1346 /*
1347 * Read the file to get the uncompressed file size...
1348 */
1349
1350 while ((bytes = cupsFileRead(reqfile, buffer, sizeof(buffer))) > 0)
1351 length += (size_t)bytes;
1352
1353 cupsFileClose(reqfile);
1354 }
1355 }
1356
1357 /*
1358 * Send the request...
1359 */
1360
1361 data->prev_pass = 1;
1362 repeat_test = 0;
1363 response = NULL;
1364
1365 if (status != HTTP_STATUS_ERROR)
1366 {
1367 while (!response && !Cancel && data->prev_pass)
1368 {
1369 ippSetRequestId(request, ++ data->request_id);
1370
1371 status = cupsSendRequest(data->http, request, data->resource, length);
1372
1373 #ifdef HAVE_LIBZ
1374 if (data->compression[0])
1375 httpSetField(data->http, HTTP_FIELD_CONTENT_ENCODING, data->compression);
1376 #endif /* HAVE_LIBZ */
1377
1378 if (!Cancel && status == HTTP_STATUS_CONTINUE && ippGetState(request) == IPP_DATA && data->file[0])
1379 {
1380 if ((reqfile = cupsFileOpen(data->file, "r")) != NULL)
1381 {
1382 while (!Cancel && (bytes = cupsFileRead(reqfile, buffer, sizeof(buffer))) > 0)
1383 {
1384 if ((status = cupsWriteRequestData(data->http, buffer, (size_t)bytes)) != HTTP_STATUS_CONTINUE)
1385 break;
1386 }
1387
1388 cupsFileClose(reqfile);
1389 }
1390 else
1391 {
1392 snprintf(buffer, sizeof(buffer), "%s: %s", data->file, strerror(errno));
1393 _cupsSetError(IPP_INTERNAL_ERROR, buffer, 0);
1394
1395 status = HTTP_STATUS_ERROR;
1396 }
1397 }
1398
1399 /*
1400 * Get the server's response...
1401 */
1402
1403 if (!Cancel && status != HTTP_STATUS_ERROR)
1404 {
1405 response = cupsGetResponse(data->http, data->resource);
1406 status = httpGetStatus(data->http);
1407 }
1408
1409 if (!Cancel && status == HTTP_STATUS_ERROR && httpError(data->http) != EINVAL &&
1410 #ifdef _WIN32
1411 httpError(data->http) != WSAETIMEDOUT)
1412 #else
1413 httpError(data->http) != ETIMEDOUT)
1414 #endif /* _WIN32 */
1415 {
1416 if (httpReconnect2(data->http, 30000, NULL))
1417 data->prev_pass = 0;
1418 }
1419 else if (status == HTTP_STATUS_ERROR || status == HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED)
1420 {
1421 data->prev_pass = 0;
1422 break;
1423 }
1424 else if (status != HTTP_STATUS_OK)
1425 {
1426 httpFlush(data->http);
1427
1428 if (status == HTTP_STATUS_UNAUTHORIZED)
1429 continue;
1430
1431 break;
1432 }
1433 }
1434 }
1435
1436 if (!Cancel && status == HTTP_STATUS_ERROR && httpError(data->http) != EINVAL &&
1437 #ifdef _WIN32
1438 httpError(data->http) != WSAETIMEDOUT)
1439 #else
1440 httpError(data->http) != ETIMEDOUT)
1441 #endif /* _WIN32 */
1442 {
1443 if (httpReconnect2(data->http, 30000, NULL))
1444 data->prev_pass = 0;
1445 }
1446 else if (status == HTTP_STATUS_ERROR)
1447 {
1448 if (!Cancel)
1449 httpReconnect2(data->http, 30000, NULL);
1450
1451 data->prev_pass = 0;
1452 }
1453 else if (status != HTTP_STATUS_OK)
1454 {
1455 httpFlush(data->http);
1456 data->prev_pass = 0;
1457 }
1458
1459 /*
1460 * Check results of request...
1461 */
1462
1463 cupsArrayClear(data->errors);
1464
1465 if (httpGetVersion(data->http) != HTTP_1_1)
1466 {
1467 int version = (int)httpGetVersion(data->http);
1468
1469 add_stringf(data->errors, "Bad HTTP version (%d.%d)", version / 100, version % 100);
1470 }
1471
1472 if (data->validate_headers)
1473 {
1474 const char *header; /* HTTP header value */
1475
1476 if ((header = httpGetField(data->http, HTTP_FIELD_CONTENT_TYPE)) == NULL || _cups_strcasecmp(header, "application/ipp"))
1477 add_stringf(data->errors, "Bad HTTP Content-Type in response (%s)", header && *header ? header : "<missing>");
1478
1479 if ((header = httpGetField(data->http, HTTP_FIELD_DATE)) != NULL && *header && httpGetDateTime(header) == 0)
1480 add_stringf(data->errors, "Bad HTTP Date in response (%s)", header);
1481 }
1482
1483 if (!response)
1484 {
1485 /*
1486 * No response, log error...
1487 */
1488
1489 add_stringf(data->errors, "IPP request failed with status %s (%s)", ippErrorString(cupsLastError()), cupsLastErrorString());
1490 }
1491 else
1492 {
1493 /*
1494 * Collect common attribute values...
1495 */
1496
1497 if ((attrptr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) != NULL)
1498 {
1499 snprintf(temp, sizeof(temp), "%d", ippGetInteger(attrptr, 0));
1500 _ippVarsSet(data->vars, "job-id", temp);
1501 }
1502
1503 if ((attrptr = ippFindAttribute(response, "job-uri", IPP_TAG_URI)) != NULL)
1504 _ippVarsSet(data->vars, "job-uri", ippGetString(attrptr, 0, NULL));
1505
1506 if ((attrptr = ippFindAttribute(response, "notify-subscription-id", IPP_TAG_INTEGER)) != NULL)
1507 {
1508 snprintf(temp, sizeof(temp), "%d", ippGetInteger(attrptr, 0));
1509 _ippVarsSet(data->vars, "notify-subscription-id", temp);
1510 }
1511
1512 /*
1513 * Check response, validating groups and attributes and logging errors
1514 * as needed...
1515 */
1516
1517 if (ippGetState(response) != IPP_DATA)
1518 add_stringf(data->errors, "Missing end-of-attributes-tag in response (RFC 2910 section 3.5.1)");
1519
1520 if (data->version)
1521 {
1522 int major, minor; /* IPP version */
1523
1524 major = ippGetVersion(response, &minor);
1525
1526 if (major != (data->version / 10) || minor != (data->version % 10))
1527 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);
1528 }
1529
1530 if (ippGetRequestId(response) != data->request_id)
1531 add_stringf(data->errors, "Bad request ID %d in response - expected %d (RFC 8011 section 4.1.1)", ippGetRequestId(response), data->request_id);
1532
1533 attrptr = ippFirstAttribute(response);
1534 if (!attrptr)
1535 {
1536 add_stringf(data->errors, "Missing first attribute \"attributes-charset (charset)\" in group operation-attributes-tag (RFC 8011 section 4.1.4).");
1537 }
1538 else
1539 {
1540 if (!ippGetName(attrptr) || ippGetValueTag(attrptr) != IPP_TAG_CHARSET || ippGetGroupTag(attrptr) != IPP_TAG_OPERATION || ippGetCount(attrptr) != 1 ||strcmp(ippGetName(attrptr), "attributes-charset"))
1541 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)));
1542
1543 attrptr = ippNextAttribute(response);
1544 if (!attrptr)
1545 add_stringf(data->errors, "Missing second attribute \"attributes-natural-language (naturalLanguage)\" in group operation-attributes-tag (RFC 8011 section 4.1.4).");
1546 else if (!ippGetName(attrptr) || ippGetValueTag(attrptr) != IPP_TAG_LANGUAGE || ippGetGroupTag(attrptr) != IPP_TAG_OPERATION || ippGetCount(attrptr) != 1 || strcmp(ippGetName(attrptr), "attributes-natural-language"))
1547 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)));
1548 }
1549
1550 if ((attrptr = ippFindAttribute(response, "status-message", IPP_TAG_ZERO)) != NULL)
1551 {
1552 const char *status_message = ippGetString(attrptr, 0, NULL);
1553 /* String value */
1554
1555 if (ippGetValueTag(attrptr) != IPP_TAG_TEXT)
1556 add_stringf(data->errors, "status-message (text(255)) has wrong value tag %s (RFC 8011 section 4.1.6.2).", ippTagString(ippGetValueTag(attrptr)));
1557 if (ippGetGroupTag(attrptr) != IPP_TAG_OPERATION)
1558 add_stringf(data->errors, "status-message (text(255)) has wrong group tag %s (RFC 8011 section 4.1.6.2).", ippTagString(ippGetGroupTag(attrptr)));
1559 if (ippGetCount(attrptr) != 1)
1560 add_stringf(data->errors, "status-message (text(255)) has %d values (RFC 8011 section 4.1.6.2).", ippGetCount(attrptr));
1561 if (status_message && strlen(status_message) > 255)
1562 add_stringf(data->errors, "status-message (text(255)) has bad length %d (RFC 8011 section 4.1.6.2).", (int)strlen(status_message));
1563 }
1564
1565 if ((attrptr = ippFindAttribute(response, "detailed-status-message",
1566 IPP_TAG_ZERO)) != NULL)
1567 {
1568 const char *detailed_status_message = ippGetString(attrptr, 0, NULL);
1569 /* String value */
1570
1571 if (ippGetValueTag(attrptr) != IPP_TAG_TEXT)
1572 add_stringf(data->errors, "detailed-status-message (text(MAX)) has wrong value tag %s (RFC 8011 section 4.1.6.3).", ippTagString(ippGetValueTag(attrptr)));
1573 if (ippGetGroupTag(attrptr) != IPP_TAG_OPERATION)
1574 add_stringf(data->errors, "detailed-status-message (text(MAX)) has wrong group tag %s (RFC 8011 section 4.1.6.3).", ippTagString(ippGetGroupTag(attrptr)));
1575 if (ippGetCount(attrptr) != 1)
1576 add_stringf(data->errors, "detailed-status-message (text(MAX)) has %d values (RFC 8011 section 4.1.6.3).", ippGetCount(attrptr));
1577 if (detailed_status_message && strlen(detailed_status_message) > 1023)
1578 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));
1579 }
1580
1581 a = cupsArrayNew((cups_array_func_t)strcmp, NULL);
1582
1583 for (attrptr = ippFirstAttribute(response), group = ippGetGroupTag(attrptr);
1584 attrptr;
1585 attrptr = ippNextAttribute(response))
1586 {
1587 if (ippGetGroupTag(attrptr) != group)
1588 {
1589 int out_of_order = 0; /* Are attribute groups out-of-order? */
1590 cupsArrayClear(a);
1591
1592 switch (ippGetGroupTag(attrptr))
1593 {
1594 case IPP_TAG_ZERO :
1595 break;
1596
1597 case IPP_TAG_OPERATION :
1598 out_of_order = 1;
1599 break;
1600
1601 case IPP_TAG_UNSUPPORTED_GROUP :
1602 if (group != IPP_TAG_OPERATION)
1603 out_of_order = 1;
1604 break;
1605
1606 case IPP_TAG_JOB :
1607 case IPP_TAG_PRINTER :
1608 if (group != IPP_TAG_OPERATION && group != IPP_TAG_UNSUPPORTED_GROUP)
1609 out_of_order = 1;
1610 break;
1611
1612 case IPP_TAG_SUBSCRIPTION :
1613 if (group > ippGetGroupTag(attrptr) && group != IPP_TAG_DOCUMENT)
1614 out_of_order = 1;
1615 break;
1616
1617 default :
1618 if (group > ippGetGroupTag(attrptr))
1619 out_of_order = 1;
1620 break;
1621 }
1622
1623 if (out_of_order)
1624 add_stringf(data->errors, "Attribute groups out of order (%s < %s)", ippTagString(ippGetGroupTag(attrptr)), ippTagString(group));
1625
1626 if (ippGetGroupTag(attrptr) != IPP_TAG_ZERO)
1627 group = ippGetGroupTag(attrptr);
1628 }
1629
1630 if (!ippValidateAttribute(attrptr))
1631 cupsArrayAdd(data->errors, (void *)cupsLastErrorString());
1632
1633 if (ippGetName(attrptr))
1634 {
1635 if (cupsArrayFind(a, (void *)ippGetName(attrptr)) && data->output < IPPTOOL_OUTPUT_LIST)
1636 add_stringf(data->errors, "Duplicate \"%s\" attribute in %s group", ippGetName(attrptr), ippTagString(group));
1637
1638 cupsArrayAdd(a, (void *)ippGetName(attrptr));
1639 }
1640 }
1641
1642 cupsArrayDelete(a);
1643
1644 /*
1645 * Now check the test-defined expected status-code and attribute
1646 * values...
1647 */
1648
1649 if (ippGetStatusCode(response) == IPP_STATUS_ERROR_BUSY && data->repeat_on_busy)
1650 {
1651 // Repeat on a server-error-busy status code...
1652 status_ok = 1;
1653 repeat_test = 1;
1654 }
1655 else
1656 {
1657 for (i = 0, status_ok = 0; i < data->num_statuses; i ++)
1658 {
1659 if (data->statuses[i].if_defined &&
1660 !_ippVarsGet(data->vars, data->statuses[i].if_defined))
1661 continue;
1662
1663 if (data->statuses[i].if_not_defined &&
1664 _ippVarsGet(data->vars, data->statuses[i].if_not_defined))
1665 continue;
1666
1667 if (ippGetStatusCode(response) == data->statuses[i].status)
1668 {
1669 status_ok = 1;
1670
1671 if (data->statuses[i].repeat_match && repeat_count < data->statuses[i].repeat_limit)
1672 repeat_test = 1;
1673
1674 if (data->statuses[i].define_match)
1675 _ippVarsSet(data->vars, data->statuses[i].define_match, "1");
1676 }
1677 else
1678 {
1679 if (data->statuses[i].repeat_no_match && repeat_count < data->statuses[i].repeat_limit)
1680 repeat_test = 1;
1681
1682 if (data->statuses[i].define_no_match)
1683 {
1684 _ippVarsSet(data->vars, data->statuses[i].define_no_match, "1");
1685 status_ok = 1;
1686 }
1687 }
1688 }
1689 }
1690
1691 if (!status_ok && data->num_statuses > 0)
1692 {
1693 for (i = 0; i < data->num_statuses; i ++)
1694 {
1695 if (data->statuses[i].if_defined &&
1696 !_ippVarsGet(data->vars, data->statuses[i].if_defined))
1697 continue;
1698
1699 if (data->statuses[i].if_not_defined &&
1700 _ippVarsGet(data->vars, data->statuses[i].if_not_defined))
1701 continue;
1702
1703 if (!data->statuses[i].repeat_match || repeat_count >= data->statuses[i].repeat_limit)
1704 add_stringf(data->errors, "EXPECTED: STATUS %s (got %s)", ippErrorString(data->statuses[i].status), ippErrorString(cupsLastError()));
1705 }
1706
1707 if ((attrptr = ippFindAttribute(response, "status-message", IPP_TAG_TEXT)) != NULL)
1708 add_stringf(data->errors, "status-message=\"%s\"", ippGetString(attrptr, 0, NULL));
1709 }
1710
1711 for (i = data->num_expects, expect = data->expects; i > 0; i --, expect ++)
1712 {
1713 ipp_attribute_t *group_found; /* Found parent attribute for group tests */
1714
1715 if (expect->if_defined && !_ippVarsGet(data->vars, expect->if_defined))
1716 continue;
1717
1718 if (expect->if_not_defined &&
1719 _ippVarsGet(data->vars, expect->if_not_defined))
1720 continue;
1721
1722 if ((found = ippFindAttribute(response, expect->name, IPP_TAG_ZERO)) != NULL && expect->in_group && expect->in_group != ippGetGroupTag(found))
1723 {
1724 while ((found = ippFindNextAttribute(response, expect->name, IPP_TAG_ZERO)) != NULL)
1725 if (expect->in_group == ippGetGroupTag(found))
1726 break;
1727 }
1728
1729 do
1730 {
1731 group_found = found;
1732
1733 if (expect->in_group && strchr(expect->name, '/'))
1734 {
1735 char group_name[256],/* Parent attribute name */
1736 *group_ptr; /* Pointer into parent attribute name */
1737
1738 strlcpy(group_name, expect->name, sizeof(group_name));
1739 if ((group_ptr = strchr(group_name, '/')) != NULL)
1740 *group_ptr = '\0';
1741
1742 group_found = ippFindAttribute(response, group_name, IPP_TAG_ZERO);
1743 }
1744
1745 if ((found && expect->not_expect) ||
1746 (!found && !(expect->not_expect || expect->optional)) ||
1747 (found && !expect_matches(expect, found)) ||
1748 (group_found && expect->in_group && ippGetGroupTag(group_found) != expect->in_group) ||
1749 (expect->with_distinct && !with_distinct_values(NULL, found)))
1750 {
1751 if (expect->define_no_match)
1752 _ippVarsSet(data->vars, expect->define_no_match, "1");
1753 else if (!expect->define_match && !expect->define_value)
1754 {
1755 if (found && expect->not_expect && !expect->with_value && !expect->with_value_from)
1756 add_stringf(data->errors, "NOT EXPECTED: %s", expect->name);
1757 else if (!found && !(expect->not_expect || expect->optional))
1758 add_stringf(data->errors, "EXPECTED: %s", expect->name);
1759 else if (found)
1760 {
1761 if (!expect_matches(expect, found))
1762 add_stringf(data->errors, "EXPECTED: %s OF-TYPE %s (got %s)",
1763 expect->name, expect->of_type,
1764 ippTagString(ippGetValueTag(found)));
1765
1766 if (expect->in_group && ippGetGroupTag(group_found) != expect->in_group)
1767 add_stringf(data->errors, "EXPECTED: %s IN-GROUP %s (got %s).",
1768 expect->name, ippTagString(expect->in_group),
1769 ippTagString(ippGetGroupTag(group_found)));
1770
1771 if (expect->with_distinct)
1772 with_distinct_values(data->errors, found);
1773 }
1774 }
1775
1776 if (expect->repeat_no_match && repeat_count < expect->repeat_limit)
1777 repeat_test = 1;
1778 break;
1779 }
1780
1781 if (found)
1782 ippAttributeString(found, buffer, sizeof(buffer));
1783
1784 if (found && expect->with_value_from && !with_value_from(NULL, ippFindAttribute(response, expect->with_value_from, IPP_TAG_ZERO), found, buffer, sizeof(buffer)))
1785 {
1786 if (expect->define_no_match)
1787 _ippVarsSet(data->vars, expect->define_no_match, "1");
1788 else if (!expect->define_match && !expect->define_value && ((!expect->repeat_match && !expect->repeat_no_match) || repeat_count >= expect->repeat_limit))
1789 {
1790 add_stringf(data->errors, "EXPECTED: %s WITH-VALUES-FROM %s", expect->name, expect->with_value_from);
1791
1792 with_value_from(data->errors, ippFindAttribute(response, expect->with_value_from, IPP_TAG_ZERO), found, buffer, sizeof(buffer));
1793 }
1794
1795 if (expect->repeat_no_match && repeat_count < expect->repeat_limit)
1796 repeat_test = 1;
1797
1798 break;
1799 }
1800 else if (found && !with_value(data, NULL, expect->with_value, expect->with_flags, found, buffer, sizeof(buffer)))
1801 {
1802 if (expect->define_no_match)
1803 _ippVarsSet(data->vars, expect->define_no_match, "1");
1804 else if (!expect->define_match && !expect->define_value &&
1805 !expect->repeat_match && (!expect->repeat_no_match || repeat_count >= expect->repeat_limit))
1806 {
1807 if (expect->with_flags & IPPTOOL_WITH_REGEX)
1808 add_stringf(data->errors, "EXPECTED: %s %s /%s/", expect->name, with_flags_string(expect->with_flags), expect->with_value);
1809 else
1810 add_stringf(data->errors, "EXPECTED: %s %s \"%s\"", expect->name, with_flags_string(expect->with_flags), expect->with_value);
1811
1812 with_value(data, data->errors, expect->with_value, expect->with_flags, found, buffer, sizeof(buffer));
1813 }
1814
1815 if (expect->repeat_no_match &&
1816 repeat_count < expect->repeat_limit)
1817 repeat_test = 1;
1818
1819 break;
1820 }
1821
1822 if (found && expect->count > 0 && ippGetCount(found) != expect->count)
1823 {
1824 if (expect->define_no_match)
1825 _ippVarsSet(data->vars, expect->define_no_match, "1");
1826 else if (!expect->define_match && !expect->define_value)
1827 {
1828 add_stringf(data->errors, "EXPECTED: %s COUNT %d (got %d)", expect->name, expect->count, ippGetCount(found));
1829 }
1830
1831 if (expect->repeat_no_match &&
1832 repeat_count < expect->repeat_limit)
1833 repeat_test = 1;
1834
1835 break;
1836 }
1837
1838 if (found && expect->same_count_as)
1839 {
1840 attrptr = ippFindAttribute(response, expect->same_count_as,
1841 IPP_TAG_ZERO);
1842
1843 if (!attrptr || ippGetCount(attrptr) != ippGetCount(found))
1844 {
1845 if (expect->define_no_match)
1846 _ippVarsSet(data->vars, expect->define_no_match, "1");
1847 else if (!expect->define_match && !expect->define_value)
1848 {
1849 if (!attrptr)
1850 add_stringf(data->errors, "EXPECTED: %s (%d values) SAME-COUNT-AS %s (not returned)", expect->name, ippGetCount(found), expect->same_count_as);
1851 else if (ippGetCount(attrptr) != ippGetCount(found))
1852 add_stringf(data->errors, "EXPECTED: %s (%d values) SAME-COUNT-AS %s (%d values)", expect->name, ippGetCount(found), expect->same_count_as, ippGetCount(attrptr));
1853 }
1854
1855 if (expect->repeat_no_match &&
1856 repeat_count < expect->repeat_limit)
1857 repeat_test = 1;
1858
1859 break;
1860 }
1861 }
1862
1863 if (found && expect->display_match && (data->output == IPPTOOL_OUTPUT_TEST || (data->output == IPPTOOL_OUTPUT_PLIST && data->outfile != cupsFileStdout())))
1864 cupsFilePrintf(cupsFileStdout(), "\n%s\n\n", expect->display_match);
1865
1866 if (found && expect->define_match)
1867 _ippVarsSet(data->vars, expect->define_match, "1");
1868
1869 if (found && expect->define_value)
1870 {
1871 if (!expect->with_value)
1872 {
1873 int last = ippGetCount(found) - 1;
1874 /* Last element in attribute */
1875
1876 switch (ippGetValueTag(found))
1877 {
1878 case IPP_TAG_ENUM :
1879 case IPP_TAG_INTEGER :
1880 snprintf(buffer, sizeof(buffer), "%d", ippGetInteger(found, last));
1881 break;
1882
1883 case IPP_TAG_BOOLEAN :
1884 if (ippGetBoolean(found, last))
1885 strlcpy(buffer, "true", sizeof(buffer));
1886 else
1887 strlcpy(buffer, "false", sizeof(buffer));
1888 break;
1889
1890 case IPP_TAG_RESOLUTION :
1891 {
1892 int xres, /* Horizontal resolution */
1893 yres; /* Vertical resolution */
1894 ipp_res_t units; /* Resolution units */
1895
1896 xres = ippGetResolution(found, last, &yres, &units);
1897
1898 if (xres == yres)
1899 snprintf(buffer, sizeof(buffer), "%d%s", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
1900 else
1901 snprintf(buffer, sizeof(buffer), "%dx%d%s", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
1902 }
1903 break;
1904
1905 case IPP_TAG_CHARSET :
1906 case IPP_TAG_KEYWORD :
1907 case IPP_TAG_LANGUAGE :
1908 case IPP_TAG_MIMETYPE :
1909 case IPP_TAG_NAME :
1910 case IPP_TAG_NAMELANG :
1911 case IPP_TAG_TEXT :
1912 case IPP_TAG_TEXTLANG :
1913 case IPP_TAG_URI :
1914 case IPP_TAG_URISCHEME :
1915 strlcpy(buffer, ippGetString(found, last, NULL), sizeof(buffer));
1916 break;
1917
1918 default :
1919 ippAttributeString(found, buffer, sizeof(buffer));
1920 break;
1921 }
1922 }
1923
1924 _ippVarsSet(data->vars, expect->define_value, buffer);
1925 }
1926
1927 if (found && expect->repeat_match &&
1928 repeat_count < expect->repeat_limit)
1929 repeat_test = 1;
1930 }
1931 while (expect->expect_all && (found = ippFindNextAttribute(response, expect->name, IPP_TAG_ZERO)) != NULL);
1932 }
1933 }
1934
1935 /*
1936 * If we are going to repeat this test, display intermediate results...
1937 */
1938
1939 if (repeat_test)
1940 {
1941 if (data->output == IPPTOOL_OUTPUT_TEST || (data->output == IPPTOOL_OUTPUT_PLIST && data->outfile != cupsFileStdout()))
1942 {
1943 cupsFilePrintf(cupsFileStdout(), "%04d]\n", repeat_count);
1944 \
1945 if (data->num_displayed > 0)
1946 {
1947 for (attrptr = ippFirstAttribute(response); attrptr; attrptr = ippNextAttribute(response))
1948 {
1949 const char *attrname = ippGetName(attrptr);
1950 if (attrname)
1951 {
1952 for (i = 0; i < data->num_displayed; i ++)
1953 {
1954 if (!strcmp(data->displayed[i], attrname))
1955 {
1956 print_attr(cupsFileStdout(), IPPTOOL_OUTPUT_TEST, attrptr, NULL);
1957 break;
1958 }
1959 }
1960 }
1961 }
1962 }
1963 }
1964
1965 if (data->output == IPPTOOL_OUTPUT_TEST || (data->output == IPPTOOL_OUTPUT_PLIST && data->outfile != cupsFileStdout()))
1966 {
1967 cupsFilePrintf(cupsFileStdout(), " %-68.68s [", data->name);
1968 }
1969
1970 ippDelete(response);
1971 response = NULL;
1972 }
1973 }
1974 while (repeat_test);
1975
1976 ippDelete(request);
1977
1978 request = NULL;
1979
1980 if (cupsArrayCount(data->errors) > 0)
1981 data->prev_pass = data->pass = 0;
1982
1983 if (data->prev_pass)
1984 data->pass_count ++;
1985 else
1986 data->fail_count ++;
1987
1988 if (data->output == IPPTOOL_OUTPUT_PLIST)
1989 {
1990 cupsFilePuts(data->outfile, "<key>Successful</key>\n");
1991 cupsFilePuts(data->outfile, data->prev_pass ? "<true />\n" : "<false />\n");
1992 cupsFilePuts(data->outfile, "<key>StatusCode</key>\n");
1993 print_xml_string(data->outfile, "string", ippErrorString(cupsLastError()));
1994 cupsFilePuts(data->outfile, "<key>ResponseAttributes</key>\n");
1995 cupsFilePuts(data->outfile, "<array>\n");
1996 cupsFilePuts(data->outfile, "<dict>\n");
1997 for (attrptr = ippFirstAttribute(response), group = ippGetGroupTag(attrptr);
1998 attrptr;
1999 attrptr = ippNextAttribute(response))
2000 print_attr(data->outfile, data->output, attrptr, &group);
2001 cupsFilePuts(data->outfile, "</dict>\n");
2002 cupsFilePuts(data->outfile, "</array>\n");
2003 }
2004 else if (data->output == IPPTOOL_OUTPUT_IPPSERVER && response)
2005 {
2006 for (attrptr = ippFirstAttribute(response); attrptr; attrptr = ippNextAttribute(response))
2007 {
2008 if (!ippGetName(attrptr) || ippGetGroupTag(attrptr) != IPP_TAG_PRINTER)
2009 continue;
2010
2011 print_ippserver_attr(data, attrptr, 0);
2012 }
2013 }
2014 else if (data->output == IPPTOOL_OUTPUT_JSON && response)
2015 {
2016 ipp_tag_t cur_tag = IPP_TAG_ZERO, /* Current group tag */
2017 group_tag; /* Attribute's group tag */
2018
2019 cupsFilePuts(data->outfile, "[\n");
2020 attrptr = ippFirstAttribute(response);
2021 while (attrptr)
2022 {
2023 group_tag = ippGetGroupTag(attrptr);
2024
2025 if (group_tag && ippGetName(attrptr))
2026 {
2027 if (group_tag != cur_tag)
2028 {
2029 if (cur_tag)
2030 cupsFilePuts(data->outfile, " },\n");
2031
2032 cupsFilePrintf(data->outfile, " {\n \"group-tag\": \"%s\",\n", ippTagString(group_tag));
2033 cur_tag = group_tag;
2034 }
2035
2036 print_json_attr(data, attrptr, 8);
2037 attrptr = ippNextAttribute(response);
2038 cupsFilePuts(data->outfile, ippGetName(attrptr) && ippGetGroupTag(attrptr) == cur_tag ? ",\n" : "\n");
2039 }
2040 else
2041 {
2042 attrptr = ippNextAttribute(response);
2043 }
2044 }
2045
2046 if (cur_tag)
2047 cupsFilePuts(data->outfile, " }\n");
2048 cupsFilePuts(data->outfile, "]\n");
2049 }
2050
2051 if (data->output == IPPTOOL_OUTPUT_TEST || (data->output == IPPTOOL_OUTPUT_PLIST && data->outfile != cupsFileStdout()))
2052 {
2053 cupsFilePuts(cupsFileStdout(), data->prev_pass ? "PASS]\n" : "FAIL]\n");
2054
2055 if (!data->prev_pass || (data->verbosity && response))
2056 {
2057 cupsFilePrintf(cupsFileStdout(), " RECEIVED: %lu bytes in response\n", (unsigned long)ippLength(response));
2058 cupsFilePrintf(cupsFileStdout(), " status-code = %s (%s)\n", ippErrorString(cupsLastError()), cupsLastErrorString());
2059
2060 if (data->verbosity && response)
2061 {
2062 for (attrptr = ippFirstAttribute(response); attrptr; attrptr = ippNextAttribute(response))
2063 print_attr(cupsFileStdout(), IPPTOOL_OUTPUT_TEST, attrptr, NULL);
2064 }
2065 }
2066 }
2067 else if (!data->prev_pass && data->output != IPPTOOL_OUTPUT_QUIET)
2068 fprintf(stderr, "%s\n", cupsLastErrorString());
2069
2070 if (data->prev_pass && data->output >= IPPTOOL_OUTPUT_LIST && !data->verbosity && data->num_displayed > 0)
2071 {
2072 size_t width; /* Length of value */
2073
2074 for (i = 0; i < data->num_displayed; i ++)
2075 {
2076 widths[i] = strlen(data->displayed[i]);
2077
2078 for (attrptr = ippFindAttribute(response, data->displayed[i], IPP_TAG_ZERO);
2079 attrptr;
2080 attrptr = ippFindNextAttribute(response, data->displayed[i], IPP_TAG_ZERO))
2081 {
2082 width = ippAttributeString(attrptr, NULL, 0);
2083 if (width > widths[i])
2084 widths[i] = width;
2085 }
2086 }
2087
2088 if (data->output == IPPTOOL_OUTPUT_CSV)
2089 print_csv(data, NULL, NULL, data->num_displayed, data->displayed, widths);
2090 else
2091 print_line(data, NULL, NULL, data->num_displayed, data->displayed, widths);
2092
2093 attrptr = ippFirstAttribute(response);
2094
2095 while (attrptr)
2096 {
2097 while (attrptr && ippGetGroupTag(attrptr) <= IPP_TAG_OPERATION)
2098 attrptr = ippNextAttribute(response);
2099
2100 if (attrptr)
2101 {
2102 if (data->output == IPPTOOL_OUTPUT_CSV)
2103 attrptr = print_csv(data, response, attrptr, data->num_displayed, data->displayed, widths);
2104 else
2105 attrptr = print_line(data, response, attrptr, data->num_displayed, data->displayed, widths);
2106
2107 while (attrptr && ippGetGroupTag(attrptr) > IPP_TAG_OPERATION)
2108 attrptr = ippNextAttribute(response);
2109 }
2110 }
2111 }
2112 else if (!data->prev_pass)
2113 {
2114 if (data->output == IPPTOOL_OUTPUT_PLIST)
2115 {
2116 cupsFilePuts(data->outfile, "<key>Errors</key>\n");
2117 cupsFilePuts(data->outfile, "<array>\n");
2118
2119 for (error = (char *)cupsArrayFirst(data->errors);
2120 error;
2121 error = (char *)cupsArrayNext(data->errors))
2122 print_xml_string(data->outfile, "string", error);
2123
2124 cupsFilePuts(data->outfile, "</array>\n");
2125 }
2126
2127 if (data->output == IPPTOOL_OUTPUT_TEST || (data->output == IPPTOOL_OUTPUT_PLIST && data->outfile != cupsFileStdout()))
2128 {
2129 for (error = (char *)cupsArrayFirst(data->errors);
2130 error;
2131 error = (char *)cupsArrayNext(data->errors))
2132 cupsFilePrintf(cupsFileStdout(), " %s\n", error);
2133 }
2134 }
2135
2136 if (data->num_displayed > 0 && !data->verbosity && response && (data->output == IPPTOOL_OUTPUT_TEST || (data->output == IPPTOOL_OUTPUT_PLIST && data->outfile != cupsFileStdout())))
2137 {
2138 for (attrptr = ippFirstAttribute(response); attrptr; attrptr = ippNextAttribute(response))
2139 {
2140 if (ippGetName(attrptr))
2141 {
2142 for (i = 0; i < data->num_displayed; i ++)
2143 {
2144 if (!strcmp(data->displayed[i], ippGetName(attrptr)))
2145 {
2146 print_attr(data->outfile, data->output, attrptr, NULL);
2147 break;
2148 }
2149 }
2150 }
2151 }
2152 }
2153
2154 skip_error:
2155
2156 if (data->monitor_thread)
2157 {
2158 data->monitor_done = 1;
2159 _cupsThreadWait(data->monitor_thread);
2160 }
2161
2162 if (data->output == IPPTOOL_OUTPUT_PLIST)
2163 cupsFilePuts(data->outfile, "</dict>\n");
2164
2165 ippDelete(response);
2166 response = NULL;
2167
2168 for (i = 0; i < data->num_statuses; i ++)
2169 {
2170 free(data->statuses[i].if_defined);
2171 free(data->statuses[i].if_not_defined);
2172 free(data->statuses[i].define_match);
2173 free(data->statuses[i].define_no_match);
2174 }
2175 data->num_statuses = 0;
2176
2177 for (i = data->num_expects, expect = data->expects; i > 0; i --, expect ++)
2178 {
2179 free(expect->name);
2180 free(expect->of_type);
2181 free(expect->same_count_as);
2182 free(expect->if_defined);
2183 free(expect->if_not_defined);
2184 free(expect->with_value);
2185 free(expect->define_match);
2186 free(expect->define_no_match);
2187 free(expect->define_value);
2188 free(expect->display_match);
2189 }
2190 data->num_expects = 0;
2191
2192 for (i = 0; i < data->num_displayed; i ++)
2193 free(data->displayed[i]);
2194 data->num_displayed = 0;
2195
2196 free(data->monitor_uri);
2197 data->monitor_uri = NULL;
2198
2199 for (i = data->num_monitor_expects, expect = data->monitor_expects; i > 0; i --, expect ++)
2200 {
2201 free(expect->name);
2202 free(expect->of_type);
2203 free(expect->same_count_as);
2204 free(expect->if_defined);
2205 free(expect->if_not_defined);
2206 free(expect->with_value);
2207 free(expect->define_match);
2208 free(expect->define_no_match);
2209 free(expect->define_value);
2210 free(expect->display_match);
2211 }
2212 data->num_monitor_expects = 0;
2213
2214 return (data->ignore_errors || data->prev_pass);
2215 }
2216
2217
2218 /*
2219 * 'do_tests()' - Do tests as specified in the test file.
2220 */
2221
2222 static int /* O - 1 on success, 0 on failure */
do_tests(const char * testfile,ipptool_test_t * data)2223 do_tests(const char *testfile, /* I - Test file to use */
2224 ipptool_test_t *data) /* I - Test data */
2225 {
2226 http_encryption_t encryption; /* Encryption mode */
2227
2228
2229 /*
2230 * Connect to the printer/server...
2231 */
2232
2233 if (!_cups_strcasecmp(data->vars->scheme, "https") || !_cups_strcasecmp(data->vars->scheme, "ipps") || data->vars->port == 443)
2234 encryption = HTTP_ENCRYPTION_ALWAYS;
2235 else
2236 encryption = data->encryption;
2237
2238 if ((data->http = httpConnect2(data->vars->host, data->vars->port, NULL, data->family, encryption, 1, 30000, NULL)) == NULL)
2239 {
2240 print_fatal_error(data, "Unable to connect to \"%s\" on port %d - %s", data->vars->host, data->vars->port, cupsLastErrorString());
2241 return (0);
2242 }
2243
2244 #ifdef HAVE_LIBZ
2245 httpSetDefaultField(data->http, HTTP_FIELD_ACCEPT_ENCODING, "deflate, gzip, identity");
2246 #else
2247 httpSetDefaultField(data->http, HTTP_FIELD_ACCEPT_ENCODING, "identity");
2248 #endif /* HAVE_LIBZ */
2249
2250 if (data->timeout > 0.0)
2251 httpSetTimeout(data->http, data->timeout, timeout_cb, NULL);
2252
2253 /*
2254 * Run tests...
2255 */
2256
2257 _ippFileParse(data->vars, testfile, (void *)data);
2258
2259 /*
2260 * Close connection and return...
2261 */
2262
2263 httpClose(data->http);
2264 data->http = NULL;
2265
2266 return (data->pass);
2267 }
2268
2269
2270 /*
2271 * 'error_cb()' - Print/add an error message.
2272 */
2273
2274 static int /* O - 1 to continue, 0 to stop */
error_cb(_ipp_file_t * f,ipptool_test_t * data,const char * error)2275 error_cb(_ipp_file_t *f, /* I - IPP file data */
2276 ipptool_test_t *data, /* I - Test data */
2277 const char *error) /* I - Error message */
2278 {
2279 (void)f;
2280
2281 print_fatal_error(data, "%s", error);
2282
2283 return (1);
2284 }
2285
2286
2287 /*
2288 * 'expect_matches()' - Return true if the tag matches the specification.
2289 */
2290
2291 static int /* O - 1 if matches, 0 otherwise */
expect_matches(ipptool_expect_t * expect,ipp_attribute_t * attr)2292 expect_matches(
2293 ipptool_expect_t *expect, /* I - Expected attribute */
2294 ipp_attribute_t *attr) /* I - Attribute */
2295 {
2296 int i, /* Looping var */
2297 count, /* Number of values */
2298 match; /* Match? */
2299 char *of_type, /* Type name to match */
2300 *paren, /* Pointer to opening parenthesis */
2301 *next, /* Next name to match */
2302 sep; /* Separator character */
2303 ipp_tag_t value_tag; /* Syntax/value tag */
2304 int lower, upper; /* Lower and upper bounds for syntax */
2305
2306
2307 /*
2308 * If we don't expect a particular type, return immediately...
2309 */
2310
2311 if (!expect->of_type)
2312 return (1);
2313
2314 /*
2315 * Parse the "of_type" value since the string can contain multiple attribute
2316 * types separated by "," or "|"...
2317 */
2318
2319 value_tag = ippGetValueTag(attr);
2320 count = ippGetCount(attr);
2321
2322 for (of_type = expect->of_type, match = 0; !match && *of_type; of_type = next)
2323 {
2324 /*
2325 * Find the next separator, and set it (temporarily) to nul if present.
2326 */
2327
2328 for (next = of_type; *next && *next != '|' && *next != ','; next ++);
2329
2330 if ((sep = *next) != '\0')
2331 *next = '\0';
2332
2333 /*
2334 * Support some meta-types to make it easier to write the test file.
2335 */
2336
2337 if ((paren = strchr(of_type, '(')) != NULL)
2338 {
2339 char *ptr; // Pointer into syntax string
2340
2341 *paren = '\0';
2342
2343 if (!strncmp(paren + 1, "MIN:", 4))
2344 {
2345 lower = INT_MIN;
2346 ptr = paren + 5;
2347 }
2348 else if ((ptr = strchr(paren + 1, ':')) != NULL)
2349 {
2350 lower = atoi(paren + 1);
2351 }
2352 else
2353 {
2354 lower = 0;
2355 ptr = paren + 1;
2356 }
2357
2358 if (!strcmp(ptr, "MAX)"))
2359 upper = INT_MAX;
2360 else
2361 upper = atoi(ptr);
2362 }
2363 else
2364 {
2365 lower = INT_MIN;
2366 upper = INT_MAX;
2367 }
2368
2369 if (!strcmp(of_type, "text"))
2370 {
2371 if (upper == INT_MAX)
2372 upper = 1023;
2373
2374 if (value_tag == IPP_TAG_TEXTLANG || value_tag == IPP_TAG_TEXT)
2375 {
2376 for (i = 0; i < count; i ++)
2377 {
2378 if (strlen(ippGetString(attr, i, NULL)) > (size_t)upper)
2379 break;
2380 }
2381
2382 match = (i == count);
2383 }
2384 }
2385 else if (!strcmp(of_type, "name"))
2386 {
2387 if (upper == INT_MAX)
2388 upper = 255;
2389
2390 if (value_tag == IPP_TAG_NAMELANG || value_tag == IPP_TAG_NAME)
2391 {
2392 for (i = 0; i < count; i ++)
2393 {
2394 if (strlen(ippGetString(attr, i, NULL)) > (size_t)upper)
2395 break;
2396 }
2397
2398 match = (i == count);
2399 }
2400 }
2401 else if (!strcmp(of_type, "collection"))
2402 {
2403 match = value_tag == IPP_TAG_BEGIN_COLLECTION;
2404 }
2405 else if (value_tag == ippTagValue(of_type))
2406 {
2407 switch (value_tag)
2408 {
2409 case IPP_TAG_KEYWORD :
2410 case IPP_TAG_URI :
2411 if (upper == INT_MAX)
2412 {
2413 if (value_tag == IPP_TAG_KEYWORD)
2414 upper = 255;
2415 else
2416 upper = 1023;
2417 }
2418
2419 for (i = 0; i < count; i ++)
2420 {
2421 if (strlen(ippGetString(attr, i, NULL)) > (size_t)upper)
2422 break;
2423 }
2424
2425 match = (i == count);
2426 break;
2427
2428 case IPP_TAG_STRING :
2429 if (upper == INT_MAX)
2430 upper = 1023;
2431
2432 for (i = 0; i < count; i ++)
2433 {
2434 int datalen; // Length of octetString value
2435
2436 ippGetOctetString(attr, i, &datalen);
2437
2438 if (datalen > upper)
2439 break;
2440 }
2441
2442 match = (i == count);
2443 break;
2444
2445 case IPP_TAG_INTEGER :
2446 for (i = 0; i < count; i ++)
2447 {
2448 int value = ippGetInteger(attr, i);
2449 // Integer value
2450
2451 if (value < lower || value > upper)
2452 break;
2453 }
2454
2455 match = (i == count);
2456 break;
2457
2458 case IPP_TAG_RANGE :
2459 for (i = 0; i < count; i ++)
2460 {
2461 int vupper, vlower = ippGetRange(attr, i, &vupper);
2462 // Range value
2463
2464 if (vlower < lower || vlower > upper || vupper < lower || vupper > upper)
2465 break;
2466 }
2467
2468 match = (i == count);
2469 break;
2470
2471 default :
2472 // No other constraints, so this is a match
2473 match = 1;
2474 break;
2475 }
2476 }
2477
2478 /*
2479 * Restore the separators if we have them...
2480 */
2481
2482 if (paren)
2483 *paren = '(';
2484
2485 if (sep)
2486 *next++ = sep;
2487 }
2488
2489 return (match);
2490 }
2491
2492
2493 /*
2494 * 'get_filename()' - Get a filename based on the current test file.
2495 */
2496
2497 static char * /* O - Filename */
get_filename(const char * testfile,char * dst,const char * src,size_t dstsize)2498 get_filename(const char *testfile, /* I - Current test file */
2499 char *dst, /* I - Destination filename */
2500 const char *src, /* I - Source filename */
2501 size_t dstsize) /* I - Size of destination buffer */
2502 {
2503 char *dstptr; /* Pointer into destination */
2504 _cups_globals_t *cg = _cupsGlobals();
2505 /* Global data */
2506
2507
2508 if (*src == '<' && src[strlen(src) - 1] == '>')
2509 {
2510 /*
2511 * Map <filename> to CUPS_DATADIR/ipptool/filename...
2512 */
2513
2514 snprintf(dst, dstsize, "%s/ipptool/%s", cg->cups_datadir, src + 1);
2515 dstptr = dst + strlen(dst) - 1;
2516 if (*dstptr == '>')
2517 *dstptr = '\0';
2518 }
2519 else if (!access(src, R_OK) || *src == '/'
2520 #ifdef _WIN32
2521 || (isalpha(*src & 255) && src[1] == ':')
2522 #endif /* _WIN32 */
2523 )
2524 {
2525 /*
2526 * Use the path as-is...
2527 */
2528
2529 strlcpy(dst, src, dstsize);
2530 }
2531 else
2532 {
2533 /*
2534 * Make path relative to testfile...
2535 */
2536
2537 strlcpy(dst, testfile, dstsize);
2538 if ((dstptr = strrchr(dst, '/')) != NULL)
2539 dstptr ++;
2540 else
2541 dstptr = dst; /* Should never happen */
2542
2543 strlcpy(dstptr, src, dstsize - (size_t)(dstptr - dst));
2544
2545 #if _WIN32
2546 if (_access(dst, 0))
2547 {
2548 /*
2549 * Not available relative to the testfile, see if it can be found on the
2550 * desktop...
2551 */
2552 const char *userprofile = getenv("USERPROFILE");
2553 /* User home directory */
2554
2555 if (userprofile)
2556 snprintf(dst, dstsize, "%s/Desktop/%s", userprofile, src);
2557 }
2558 #endif /* _WIN32 */
2559 }
2560
2561 return (dst);
2562 }
2563
2564
2565 /*
2566 * 'get_string()' - Get a pointer to a string value or the portion of interest.
2567 */
2568
2569 static const char * /* O - Pointer to string */
get_string(ipp_attribute_t * attr,int element,int flags,char * buffer,size_t bufsize)2570 get_string(ipp_attribute_t *attr, /* I - IPP attribute */
2571 int element, /* I - Element to fetch */
2572 int flags, /* I - Value ("with") flags */
2573 char *buffer, /* I - Temporary buffer */
2574 size_t bufsize) /* I - Size of temporary buffer */
2575 {
2576 const char *value; /* Value */
2577 char *ptr, /* Pointer into value */
2578 scheme[256], /* URI scheme */
2579 userpass[256], /* Username/password */
2580 hostname[256], /* Hostname */
2581 resource[1024]; /* Resource */
2582 int port; /* Port number */
2583
2584
2585 value = ippGetString(attr, element, NULL);
2586
2587 if (flags & IPPTOOL_WITH_HOSTNAME)
2588 {
2589 if (httpSeparateURI(HTTP_URI_CODING_ALL, value, scheme, sizeof(scheme), userpass, sizeof(userpass), buffer, (int)bufsize, &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
2590 buffer[0] = '\0';
2591
2592 ptr = buffer + strlen(buffer) - 1;
2593 if (ptr >= buffer && *ptr == '.')
2594 *ptr = '\0'; /* Drop trailing "." */
2595
2596 return (buffer);
2597 }
2598 else if (flags & IPPTOOL_WITH_RESOURCE)
2599 {
2600 if (httpSeparateURI(HTTP_URI_CODING_ALL, value, scheme, sizeof(scheme), userpass, sizeof(userpass), hostname, sizeof(hostname), &port, buffer, (int)bufsize) < HTTP_URI_STATUS_OK)
2601 buffer[0] = '\0';
2602
2603 return (buffer);
2604 }
2605 else if (flags & IPPTOOL_WITH_SCHEME)
2606 {
2607 if (httpSeparateURI(HTTP_URI_CODING_ALL, value, buffer, (int)bufsize, userpass, sizeof(userpass), hostname, sizeof(hostname), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
2608 buffer[0] = '\0';
2609
2610 return (buffer);
2611 }
2612 else if (ippGetValueTag(attr) == IPP_TAG_URI && (!strncmp(value, "ipp://", 6) || !strncmp(value, "http://", 7) || !strncmp(value, "ipps://", 7) || !strncmp(value, "https://", 8)))
2613 {
2614 http_uri_status_t status = httpSeparateURI(HTTP_URI_CODING_ALL, value, scheme, sizeof(scheme), userpass, sizeof(userpass), hostname, sizeof(hostname), &port, resource, sizeof(resource));
2615
2616 if (status < HTTP_URI_STATUS_OK)
2617 {
2618 /*
2619 * Bad URI...
2620 */
2621
2622 buffer[0] = '\0';
2623 }
2624 else
2625 {
2626 /*
2627 * Normalize URI with no trailing dot...
2628 */
2629
2630 if ((ptr = hostname + strlen(hostname) - 1) >= hostname && *ptr == '.')
2631 *ptr = '\0';
2632
2633 httpAssembleURI(HTTP_URI_CODING_ALL, buffer, (int)bufsize, scheme, userpass, hostname, port, resource);
2634 }
2635
2636 return (buffer);
2637 }
2638 else
2639 return (value);
2640 }
2641
2642
2643 /*
2644 * 'init_data()' - Initialize test data.
2645 */
2646
2647 static void
init_data(ipptool_test_t * data)2648 init_data(ipptool_test_t *data) /* I - Data */
2649 {
2650 memset(data, 0, sizeof(ipptool_test_t));
2651
2652 data->output = IPPTOOL_OUTPUT_LIST;
2653 data->outfile = cupsFileStdout();
2654 data->family = AF_UNSPEC;
2655 data->def_transfer = IPPTOOL_TRANSFER_AUTO;
2656 data->def_version = 11;
2657 data->errors = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free);
2658 data->pass = 1;
2659 data->prev_pass = 1;
2660 data->request_id = (CUPS_RAND() % 1000) * 137;
2661 data->show_header = 1;
2662 }
2663
2664
2665 /*
2666 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
2667 * value.
2668 */
2669
2670 static char * /* O - ISO 8601 date/time string */
iso_date(const ipp_uchar_t * date)2671 iso_date(const ipp_uchar_t *date) /* I - IPP (RFC 1903) date/time value */
2672 {
2673 time_t utctime; /* UTC time since 1970 */
2674 struct tm utcdate; /* UTC date/time */
2675 static char buffer[255]; /* String buffer */
2676
2677
2678 utctime = ippDateToTime(date);
2679 gmtime_r(&utctime, &utcdate);
2680
2681 snprintf(buffer, sizeof(buffer), "%04d-%02d-%02dT%02d:%02d:%02dZ",
2682 utcdate.tm_year + 1900, utcdate.tm_mon + 1, utcdate.tm_mday,
2683 utcdate.tm_hour, utcdate.tm_min, utcdate.tm_sec);
2684
2685 return (buffer);
2686 }
2687
2688
2689 /*
2690 * 'parse_monitor_printer_state()' - Parse the MONITOR-PRINTER-STATE directive.
2691 *
2692 * MONITOR-PRINTER-STATE [printer-uri] {
2693 * DELAY nnn
2694 * EXPECT attribute-name ...
2695 * }
2696 */
2697
2698 static int /* O - 1 to continue, 0 to stop */
parse_monitor_printer_state(_ipp_file_t * f,ipptool_test_t * data)2699 parse_monitor_printer_state(
2700 _ipp_file_t *f, /* I - IPP file data */
2701 ipptool_test_t *data) /* I - Test data */
2702 {
2703 char token[256], /* Token string */
2704 name[1024], /* Name string */
2705 temp[1024], /* Temporary string */
2706 value[1024], /* Value string */
2707 *ptr; /* Pointer into value */
2708
2709
2710 if (!_ippFileReadToken(f, temp, sizeof(temp)))
2711 {
2712 print_fatal_error(data, "Missing printer URI on line %d of \"%s\".", f->linenum, f->filename);
2713 return (0);
2714 }
2715
2716 if (strcmp(temp, "{"))
2717 {
2718 // Got a printer URI so copy it...
2719 _ippVarsExpand(data->vars, value, temp, sizeof(value));
2720 data->monitor_uri = strdup(value);
2721
2722 // Then see if we have an opening brace...
2723 if (!_ippFileReadToken(f, temp, sizeof(temp)) || strcmp(temp, "{"))
2724 {
2725 print_fatal_error(data, "Missing opening brace on line %d of \"%s\".", f->linenum, f->filename);
2726 return (0);
2727 }
2728 }
2729 else
2730 {
2731 // Use the default printer URI...
2732 data->monitor_uri = strdup(data->vars->uri);
2733 }
2734
2735 // Loop until we get a closing brace...
2736 while (_ippFileReadToken(f, token, sizeof(token)))
2737 {
2738 if (_cups_strcasecmp(token, "COUNT") &&
2739 _cups_strcasecmp(token, "DEFINE-MATCH") &&
2740 _cups_strcasecmp(token, "DEFINE-NO-MATCH") &&
2741 _cups_strcasecmp(token, "DEFINE-VALUE") &&
2742 _cups_strcasecmp(token, "DISPLAY-MATCH") &&
2743 _cups_strcasecmp(token, "IF-DEFINED") &&
2744 _cups_strcasecmp(token, "IF-NOT-DEFINED") &&
2745 _cups_strcasecmp(token, "IN-GROUP") &&
2746 _cups_strcasecmp(token, "OF-TYPE") &&
2747 _cups_strcasecmp(token, "WITH-DISTINCT-VALUES") &&
2748 _cups_strcasecmp(token, "WITH-VALUE"))
2749 data->last_expect = NULL;
2750
2751 if (!strcmp(token, "}"))
2752 return (1);
2753 else if (!_cups_strcasecmp(token, "EXPECT"))
2754 {
2755 /*
2756 * Expected attributes...
2757 */
2758
2759 if (data->num_monitor_expects >= (int)(sizeof(data->monitor_expects) / sizeof(data->monitor_expects[0])))
2760 {
2761 print_fatal_error(data, "Too many EXPECT's on line %d of \"%s\".", f->linenum, f->filename);
2762 return (0);
2763 }
2764
2765 if (!_ippFileReadToken(f, name, sizeof(name)))
2766 {
2767 print_fatal_error(data, "Missing EXPECT name on line %d of \"%s\".", f->linenum, f->filename);
2768 return (0);
2769 }
2770
2771 data->last_expect = data->monitor_expects + data->num_monitor_expects;
2772 data->num_monitor_expects ++;
2773
2774 memset(data->last_expect, 0, sizeof(ipptool_expect_t));
2775 data->last_expect->repeat_limit = 1000;
2776
2777 if (name[0] == '!')
2778 {
2779 data->last_expect->not_expect = 1;
2780 data->last_expect->name = strdup(name + 1);
2781 }
2782 else if (name[0] == '?')
2783 {
2784 data->last_expect->optional = 1;
2785 data->last_expect->name = strdup(name + 1);
2786 }
2787 else
2788 data->last_expect->name = strdup(name);
2789 }
2790 else if (!_cups_strcasecmp(token, "COUNT"))
2791 {
2792 int count; /* Count value */
2793
2794 if (!_ippFileReadToken(f, temp, sizeof(temp)))
2795 {
2796 print_fatal_error(data, "Missing COUNT number on line %d of \"%s\".", f->linenum, f->filename);
2797 return (0);
2798 }
2799
2800 if ((count = atoi(temp)) <= 0)
2801 {
2802 print_fatal_error(data, "Bad COUNT \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
2803 return (0);
2804 }
2805
2806 if (data->last_expect)
2807 {
2808 data->last_expect->count = count;
2809 }
2810 else
2811 {
2812 print_fatal_error(data, "COUNT without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
2813 return (0);
2814 }
2815 }
2816 else if (!_cups_strcasecmp(token, "DEFINE-MATCH"))
2817 {
2818 if (!_ippFileReadToken(f, temp, sizeof(temp)))
2819 {
2820 print_fatal_error(data, "Missing DEFINE-MATCH variable on line %d of \"%s\".", f->linenum, f->filename);
2821 return (0);
2822 }
2823
2824 if (data->last_expect)
2825 {
2826 data->last_expect->define_match = strdup(temp);
2827 }
2828 else
2829 {
2830 print_fatal_error(data, "DEFINE-MATCH without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
2831 return (0);
2832 }
2833 }
2834 else if (!_cups_strcasecmp(token, "DEFINE-NO-MATCH"))
2835 {
2836 if (!_ippFileReadToken(f, temp, sizeof(temp)))
2837 {
2838 print_fatal_error(data, "Missing DEFINE-NO-MATCH variable on line %d of \"%s\".", f->linenum, f->filename);
2839 return (0);
2840 }
2841
2842 if (data->last_expect)
2843 {
2844 data->last_expect->define_no_match = strdup(temp);
2845 }
2846 else
2847 {
2848 print_fatal_error(data, "DEFINE-NO-MATCH without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
2849 return (0);
2850 }
2851 }
2852 else if (!_cups_strcasecmp(token, "DEFINE-VALUE"))
2853 {
2854 if (!_ippFileReadToken(f, temp, sizeof(temp)))
2855 {
2856 print_fatal_error(data, "Missing DEFINE-VALUE variable on line %d of \"%s\".", f->linenum, f->filename);
2857 return (0);
2858 }
2859
2860 if (data->last_expect)
2861 {
2862 data->last_expect->define_value = strdup(temp);
2863 }
2864 else
2865 {
2866 print_fatal_error(data, "DEFINE-VALUE without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
2867 return (0);
2868 }
2869 }
2870 else if (!_cups_strcasecmp(token, "DISPLAY-MATCH"))
2871 {
2872 if (!_ippFileReadToken(f, temp, sizeof(temp)))
2873 {
2874 print_fatal_error(data, "Missing DISPLAY-MATCH message on line %d of \"%s\".", f->linenum, f->filename);
2875 return (0);
2876 }
2877
2878 if (data->last_expect)
2879 {
2880 data->last_expect->display_match = strdup(temp);
2881 }
2882 else
2883 {
2884 print_fatal_error(data, "DISPLAY-MATCH without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
2885 return (0);
2886 }
2887 }
2888 else if (!_cups_strcasecmp(token, "DELAY"))
2889 {
2890 /*
2891 * Delay before operation...
2892 */
2893
2894 double dval; /* Delay value */
2895
2896 if (!_ippFileReadToken(f, temp, sizeof(temp)))
2897 {
2898 print_fatal_error(data, "Missing DELAY value on line %d of \"%s\".", f->linenum, f->filename);
2899 return (0);
2900 }
2901
2902 _ippVarsExpand(data->vars, value, temp, sizeof(value));
2903
2904 if ((dval = _cupsStrScand(value, &ptr, localeconv())) < 0.0 || (*ptr && *ptr != ','))
2905 {
2906 print_fatal_error(data, "Bad DELAY value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
2907 return (0);
2908 }
2909
2910 data->monitor_delay = (useconds_t)(1000000.0 * dval);
2911
2912 if (*ptr == ',')
2913 {
2914 if ((dval = _cupsStrScand(ptr + 1, &ptr, localeconv())) <= 0.0 || *ptr)
2915 {
2916 print_fatal_error(data, "Bad DELAY value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
2917 return (0);
2918 }
2919
2920 data->monitor_interval = (useconds_t)(1000000.0 * dval);
2921 }
2922 else
2923 data->monitor_interval = data->monitor_delay;
2924 }
2925 else if (!_cups_strcasecmp(token, "OF-TYPE"))
2926 {
2927 if (!_ippFileReadToken(f, temp, sizeof(temp)))
2928 {
2929 print_fatal_error(data, "Missing OF-TYPE value tag(s) on line %d of \"%s\".", f->linenum, f->filename);
2930 return (0);
2931 }
2932
2933 if (data->last_expect)
2934 {
2935 data->last_expect->of_type = strdup(temp);
2936 }
2937 else
2938 {
2939 print_fatal_error(data, "OF-TYPE without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
2940 return (0);
2941 }
2942 }
2943 else if (!_cups_strcasecmp(token, "IN-GROUP"))
2944 {
2945 ipp_tag_t in_group; /* IN-GROUP value */
2946
2947 if (!_ippFileReadToken(f, temp, sizeof(temp)))
2948 {
2949 print_fatal_error(data, "Missing IN-GROUP group tag on line %d of \"%s\".", f->linenum, f->filename);
2950 return (0);
2951 }
2952
2953 if ((in_group = ippTagValue(temp)) == IPP_TAG_ZERO || in_group >= IPP_TAG_UNSUPPORTED_VALUE)
2954 {
2955 print_fatal_error(data, "Bad IN-GROUP group tag \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
2956 return (0);
2957 }
2958 else if (data->last_expect)
2959 {
2960 data->last_expect->in_group = in_group;
2961 }
2962 else
2963 {
2964 print_fatal_error(data, "IN-GROUP without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
2965 return (0);
2966 }
2967 }
2968 else if (!_cups_strcasecmp(token, "IF-DEFINED"))
2969 {
2970 if (!_ippFileReadToken(f, temp, sizeof(temp)))
2971 {
2972 print_fatal_error(data, "Missing IF-DEFINED name on line %d of \"%s\".", f->linenum, f->filename);
2973 return (0);
2974 }
2975
2976 if (data->last_expect)
2977 {
2978 data->last_expect->if_defined = strdup(temp);
2979 }
2980 else
2981 {
2982 print_fatal_error(data, "IF-DEFINED without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
2983 return (0);
2984 }
2985 }
2986 else if (!_cups_strcasecmp(token, "IF-NOT-DEFINED"))
2987 {
2988 if (!_ippFileReadToken(f, temp, sizeof(temp)))
2989 {
2990 print_fatal_error(data, "Missing IF-NOT-DEFINED name on line %d of \"%s\".", f->linenum, f->filename);
2991 return (0);
2992 }
2993
2994 if (data->last_expect)
2995 {
2996 data->last_expect->if_not_defined = strdup(temp);
2997 }
2998 else
2999 {
3000 print_fatal_error(data, "IF-NOT-DEFINED without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
3001 return (0);
3002 }
3003 }
3004 else if (!_cups_strcasecmp(token, "WITH-DISTINCT-VALUES"))
3005 {
3006 if (data->last_expect)
3007 {
3008 data->last_expect->with_distinct = 1;
3009 }
3010 else
3011 {
3012 print_fatal_error(data, "%s without a preceding EXPECT on line %d of \"%s\".", token, f->linenum, f->filename);
3013 return (0);
3014 }
3015 }
3016 else if (!_cups_strcasecmp(token, "WITH-VALUE"))
3017 {
3018 off_t lastpos; /* Last file position */
3019 int lastline; /* Last line number */
3020
3021 if (!_ippFileReadToken(f, temp, sizeof(temp)))
3022 {
3023 print_fatal_error(data, "Missing %s value on line %d of \"%s\".", token, f->linenum, f->filename);
3024 return (0);
3025 }
3026
3027 /*
3028 * Read additional comma-delimited values - needed since legacy test files
3029 * will have unquoted WITH-VALUE values with commas...
3030 */
3031
3032 ptr = temp + strlen(temp);
3033
3034 for (;;)
3035 {
3036 lastpos = cupsFileTell(f->fp);
3037 lastline = f->linenum;
3038 ptr += strlen(ptr);
3039
3040 if (!_ippFileReadToken(f, ptr, (sizeof(temp) - (size_t)(ptr - temp))))
3041 break;
3042
3043 if (!strcmp(ptr, ","))
3044 {
3045 /*
3046 * Append a value...
3047 */
3048
3049 ptr += strlen(ptr);
3050
3051 if (!_ippFileReadToken(f, ptr, (sizeof(temp) - (size_t)(ptr - temp))))
3052 break;
3053 }
3054 else
3055 {
3056 /*
3057 * Not another value, stop here...
3058 */
3059
3060 cupsFileSeek(f->fp, lastpos);
3061 f->linenum = lastline;
3062 *ptr = '\0';
3063 break;
3064 }
3065 }
3066
3067 if (data->last_expect)
3068 {
3069 /*
3070 * Expand any variables in the value and then save it.
3071 */
3072
3073 _ippVarsExpand(data->vars, value, temp, sizeof(value));
3074
3075 ptr = value + strlen(value) - 1;
3076
3077 if (value[0] == '/' && ptr > value && *ptr == '/')
3078 {
3079 /*
3080 * WITH-VALUE is a POSIX extended regular expression.
3081 */
3082
3083 data->last_expect->with_value = calloc(1, (size_t)(ptr - value));
3084 data->last_expect->with_flags |= IPPTOOL_WITH_REGEX;
3085
3086 if (data->last_expect->with_value)
3087 memcpy(data->last_expect->with_value, value + 1, (size_t)(ptr - value - 1));
3088 }
3089 else
3090 {
3091 /*
3092 * WITH-VALUE is a literal value...
3093 */
3094
3095 for (ptr = value; *ptr; ptr ++)
3096 {
3097 if (*ptr == '\\' && ptr[1])
3098 {
3099 /*
3100 * Remove \ from \foo...
3101 */
3102
3103 _cups_strcpy(ptr, ptr + 1);
3104 }
3105 }
3106
3107 data->last_expect->with_value = strdup(value);
3108 data->last_expect->with_flags |= IPPTOOL_WITH_LITERAL;
3109 }
3110 }
3111 else
3112 {
3113 print_fatal_error(data, "%s without a preceding EXPECT on line %d of \"%s\".", token, f->linenum, f->filename);
3114 return (0);
3115 }
3116 }
3117 }
3118
3119 print_fatal_error(data, "Missing closing brace on line %d of \"%s\".", f->linenum, f->filename);
3120
3121 return (0);
3122 }
3123
3124
3125 /*
3126 * 'pause_message()' - Display the message and pause until the user presses a key.
3127 */
3128
3129 static void
pause_message(const char * message)3130 pause_message(const char *message) /* I - Message */
3131 {
3132 #ifdef _WIN32
3133 HANDLE tty; /* Console handle */
3134 DWORD mode; /* Console mode */
3135 char key; /* Key press */
3136 DWORD bytes; /* Bytes read for key press */
3137
3138
3139 /*
3140 * Disable input echo and set raw input...
3141 */
3142
3143 if ((tty = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE)
3144 return;
3145
3146 if (!GetConsoleMode(tty, &mode))
3147 return;
3148
3149 if (!SetConsoleMode(tty, 0))
3150 return;
3151
3152 #else
3153 int tty; /* /dev/tty - never read from stdin */
3154 struct termios original, /* Original input mode */
3155 noecho; /* No echo input mode */
3156 char key; /* Current key press */
3157
3158
3159 /*
3160 * Disable input echo and set raw input...
3161 */
3162
3163 if ((tty = open("/dev/tty", O_RDONLY)) < 0)
3164 return;
3165
3166 if (tcgetattr(tty, &original))
3167 {
3168 close(tty);
3169 return;
3170 }
3171
3172 noecho = original;
3173 noecho.c_lflag &= (tcflag_t)~(ICANON | ECHO | ECHOE | ISIG);
3174
3175 if (tcsetattr(tty, TCSAFLUSH, &noecho))
3176 {
3177 close(tty);
3178 return;
3179 }
3180 #endif /* _WIN32 */
3181
3182 /*
3183 * Display the prompt...
3184 */
3185
3186 cupsFilePrintf(cupsFileStdout(), "\n%s\n\n---- PRESS ANY KEY ----", message);
3187
3188 #ifdef _WIN32
3189 /*
3190 * Read a key...
3191 */
3192
3193 ReadFile(tty, &key, 1, &bytes, NULL);
3194
3195 /*
3196 * Cleanup...
3197 */
3198
3199 SetConsoleMode(tty, mode);
3200
3201 #else
3202 /*
3203 * Read a key...
3204 */
3205
3206 read(tty, &key, 1);
3207
3208 /*
3209 * Cleanup...
3210 */
3211
3212 tcsetattr(tty, TCSAFLUSH, &original);
3213 close(tty);
3214 #endif /* _WIN32 */
3215
3216 /*
3217 * Erase the "press any key" prompt...
3218 */
3219
3220 cupsFilePuts(cupsFileStdout(), "\r \r");
3221 }
3222
3223
3224 /*
3225 * 'print_attr()' - Print an attribute on the screen.
3226 */
3227
3228 static void
print_attr(cups_file_t * outfile,ipptool_output_t output,ipp_attribute_t * attr,ipp_tag_t * group)3229 print_attr(cups_file_t *outfile, /* I - Output file */
3230 ipptool_output_t output, /* I - Output format */
3231 ipp_attribute_t *attr, /* I - Attribute to print */
3232 ipp_tag_t *group) /* IO - Current group */
3233 {
3234 int i, /* Looping var */
3235 count; /* Number of values */
3236 ipp_attribute_t *colattr; /* Collection attribute */
3237
3238
3239 if (output == IPPTOOL_OUTPUT_PLIST)
3240 {
3241 if (!ippGetName(attr) || (group && *group != ippGetGroupTag(attr)))
3242 {
3243 if (ippGetGroupTag(attr) != IPP_TAG_ZERO)
3244 {
3245 cupsFilePuts(outfile, "</dict>\n");
3246 cupsFilePuts(outfile, "<dict>\n");
3247 }
3248
3249 if (group)
3250 *group = ippGetGroupTag(attr);
3251 }
3252
3253 if (!ippGetName(attr))
3254 return;
3255
3256 print_xml_string(outfile, "key", ippGetName(attr));
3257 if ((count = ippGetCount(attr)) > 1)
3258 cupsFilePuts(outfile, "<array>\n");
3259
3260 switch (ippGetValueTag(attr))
3261 {
3262 case IPP_TAG_INTEGER :
3263 case IPP_TAG_ENUM :
3264 for (i = 0; i < count; i ++)
3265 cupsFilePrintf(outfile, "<integer>%d</integer>\n", ippGetInteger(attr, i));
3266 break;
3267
3268 case IPP_TAG_BOOLEAN :
3269 for (i = 0; i < count; i ++)
3270 cupsFilePuts(outfile, ippGetBoolean(attr, i) ? "<true />\n" : "<false />\n");
3271 break;
3272
3273 case IPP_TAG_RANGE :
3274 for (i = 0; i < count; i ++)
3275 {
3276 int lower, upper; /* Lower and upper ranges */
3277
3278 lower = ippGetRange(attr, i, &upper);
3279 cupsFilePrintf(outfile, "<dict><key>lower</key><integer>%d</integer><key>upper</key><integer>%d</integer></dict>\n", lower, upper);
3280 }
3281 break;
3282
3283 case IPP_TAG_RESOLUTION :
3284 for (i = 0; i < count; i ++)
3285 {
3286 int xres, yres; /* Resolution values */
3287 ipp_res_t units; /* Resolution units */
3288
3289 xres = ippGetResolution(attr, i, &yres, &units);
3290 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");
3291 }
3292 break;
3293
3294 case IPP_TAG_DATE :
3295 for (i = 0; i < count; i ++)
3296 cupsFilePrintf(outfile, "<date>%s</date>\n", iso_date(ippGetDate(attr, i)));
3297 break;
3298
3299 case IPP_TAG_STRING :
3300 for (i = 0; i < count; i ++)
3301 {
3302 int datalen; /* Length of data */
3303 void *data = ippGetOctetString(attr, i, &datalen);
3304 /* Data */
3305 char buffer[IPP_MAX_LENGTH * 5 / 4 + 1];
3306 /* Base64 output buffer */
3307
3308 cupsFilePrintf(outfile, "<data>%s</data>\n", httpEncode64_2(buffer, sizeof(buffer), data, datalen));
3309 }
3310 break;
3311
3312 case IPP_TAG_TEXT :
3313 case IPP_TAG_NAME :
3314 case IPP_TAG_KEYWORD :
3315 case IPP_TAG_URI :
3316 case IPP_TAG_URISCHEME :
3317 case IPP_TAG_CHARSET :
3318 case IPP_TAG_LANGUAGE :
3319 case IPP_TAG_MIMETYPE :
3320 for (i = 0; i < count; i ++)
3321 print_xml_string(outfile, "string", ippGetString(attr, i, NULL));
3322 break;
3323
3324 case IPP_TAG_TEXTLANG :
3325 case IPP_TAG_NAMELANG :
3326 for (i = 0; i < count; i ++)
3327 {
3328 const char *s, /* String */
3329 *lang; /* Language */
3330
3331 s = ippGetString(attr, i, &lang);
3332 cupsFilePuts(outfile, "<dict><key>language</key><string>");
3333 print_xml_string(outfile, NULL, lang);
3334 cupsFilePuts(outfile, "</string><key>string</key><string>");
3335 print_xml_string(outfile, NULL, s);
3336 cupsFilePuts(outfile, "</string></dict>\n");
3337 }
3338 break;
3339
3340 case IPP_TAG_BEGIN_COLLECTION :
3341 for (i = 0; i < count; i ++)
3342 {
3343 ipp_t *col = ippGetCollection(attr, i);
3344 /* Collection value */
3345
3346 cupsFilePuts(outfile, "<dict>\n");
3347 for (colattr = ippFirstAttribute(col); colattr; colattr = ippNextAttribute(col))
3348 print_attr(outfile, output, colattr, NULL);
3349 cupsFilePuts(outfile, "</dict>\n");
3350 }
3351 break;
3352
3353 default :
3354 cupsFilePrintf(outfile, "<string><<%s>></string>\n", ippTagString(ippGetValueTag(attr)));
3355 break;
3356 }
3357
3358 if (count > 1)
3359 cupsFilePuts(outfile, "</array>\n");
3360 }
3361 else
3362 {
3363 char buffer[131072]; /* Value buffer */
3364
3365 if (output == IPPTOOL_OUTPUT_TEST)
3366 {
3367 if (!ippGetName(attr))
3368 {
3369 cupsFilePuts(outfile, " -- separator --\n");
3370 return;
3371 }
3372
3373 cupsFilePrintf(outfile, " %s (%s%s) = ", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)));
3374 }
3375
3376 ippAttributeString(attr, buffer, sizeof(buffer));
3377 cupsFilePrintf(outfile, "%s\n", buffer);
3378 }
3379 }
3380
3381
3382 /*
3383 * 'print_csv()' - Print a line of CSV text.
3384 */
3385
3386 static ipp_attribute_t * /* O - Next attribute */
print_csv(ipptool_test_t * data,ipp_t * ipp,ipp_attribute_t * attr,int num_displayed,char ** displayed,size_t * widths)3387 print_csv(
3388 ipptool_test_t *data, /* I - Test data */
3389 ipp_t *ipp, /* I - Response message */
3390 ipp_attribute_t *attr, /* I - First attribute for line */
3391 int num_displayed, /* I - Number of attributes to display */
3392 char **displayed, /* I - Attributes to display */
3393 size_t *widths) /* I - Column widths */
3394 {
3395 int i; /* Looping var */
3396 size_t maxlength; /* Max length of all columns */
3397 ipp_attribute_t *current = attr; /* Current attribute */
3398 char *values[MAX_DISPLAY], /* Strings to display */
3399 *valptr; /* Pointer into value */
3400
3401 /*
3402 * Get the maximum string length we have to show and allocate...
3403 */
3404
3405 for (i = 1, maxlength = widths[0]; i < num_displayed; i ++)
3406 if (widths[i] > maxlength)
3407 maxlength = widths[i];
3408
3409 maxlength += 2;
3410
3411 /*
3412 * Loop through the attributes to display...
3413 */
3414
3415 if (attr)
3416 {
3417 // Collect the values...
3418 memset(values, 0, sizeof(values));
3419
3420 for (; current; current = ippNextAttribute(ipp))
3421 {
3422 if (!ippGetName(current))
3423 break;
3424
3425 for (i = 0; i < num_displayed; i ++)
3426 {
3427 if (!strcmp(ippGetName(current), displayed[i]))
3428 {
3429 if ((values[i] = (char *)calloc(1, maxlength)) != NULL)
3430 ippAttributeString(current, values[i], maxlength);
3431 break;
3432 }
3433 }
3434 }
3435
3436 // Output the line...
3437 for (i = 0; i < num_displayed; i ++)
3438 {
3439 if (i)
3440 cupsFilePutChar(data->outfile, ',');
3441
3442 if (!values[i])
3443 continue;
3444
3445 if (strchr(values[i], ',') != NULL || strchr(values[i], '\"') != NULL || strchr(values[i], '\\') != NULL)
3446 {
3447 // Quoted value...
3448 cupsFilePutChar(data->outfile, '\"');
3449 for (valptr = values[i]; *valptr; valptr ++)
3450 {
3451 if (*valptr == '\\' || *valptr == '\"')
3452 cupsFilePutChar(data->outfile, '\\');
3453 cupsFilePutChar(data->outfile, *valptr);
3454 }
3455 cupsFilePutChar(data->outfile, '\"');
3456 }
3457 else
3458 {
3459 // Unquoted value...
3460 cupsFilePuts(data->outfile, values[i]);
3461 }
3462
3463 free(values[i]);
3464 }
3465 cupsFilePutChar(data->outfile, '\n');
3466 }
3467 else
3468 {
3469 // Show column headings...
3470 for (i = 0; i < num_displayed; i ++)
3471 {
3472 if (i)
3473 cupsFilePutChar(data->outfile, ',');
3474
3475 cupsFilePuts(data->outfile, displayed[i]);
3476 }
3477 cupsFilePutChar(data->outfile, '\n');
3478 }
3479
3480 return (current);
3481 }
3482
3483
3484 /*
3485 * 'print_fatal_error()' - Print a fatal error message.
3486 */
3487
3488 static void
print_fatal_error(ipptool_test_t * data,const char * s,...)3489 print_fatal_error(
3490 ipptool_test_t *data, /* I - Test data */
3491 const char *s, /* I - Printf-style format string */
3492 ...) /* I - Additional arguments as needed */
3493 {
3494 char buffer[10240]; /* Format buffer */
3495 va_list ap; /* Pointer to arguments */
3496
3497
3498 /*
3499 * Format the error message...
3500 */
3501
3502 va_start(ap, s);
3503 vsnprintf(buffer, sizeof(buffer), s, ap);
3504 va_end(ap);
3505
3506 /*
3507 * Then output it...
3508 */
3509
3510 if (data->output == IPPTOOL_OUTPUT_PLIST)
3511 {
3512 print_xml_header(data);
3513 print_xml_trailer(data, 0, buffer);
3514 }
3515
3516 _cupsLangPrintf(stderr, "ipptool: %s", buffer);
3517 }
3518
3519
3520 /*
3521 * 'print_ippserver_attr()' - Print an attribute suitable for use by ippserver.
3522 */
3523
3524 static void
print_ippserver_attr(ipptool_test_t * data,ipp_attribute_t * attr,int indent)3525 print_ippserver_attr(
3526 ipptool_test_t *data, /* I - Test data */
3527 ipp_attribute_t *attr, /* I - Attribute to print */
3528 int indent) /* I - Indentation level */
3529 {
3530 int i, /* Looping var */
3531 count = ippGetCount(attr);
3532 /* Number of values */
3533 ipp_attribute_t *colattr; /* Collection attribute */
3534
3535
3536 if (indent == 0)
3537 cupsFilePrintf(data->outfile, "ATTR %s %s", ippTagString(ippGetValueTag(attr)), ippGetName(attr));
3538 else
3539 cupsFilePrintf(data->outfile, "%*sMEMBER %s %s", indent, "", ippTagString(ippGetValueTag(attr)), ippGetName(attr));
3540
3541 switch (ippGetValueTag(attr))
3542 {
3543 case IPP_TAG_INTEGER :
3544 case IPP_TAG_ENUM :
3545 for (i = 0; i < count; i ++)
3546 cupsFilePrintf(data->outfile, "%s%d", i ? "," : " ", ippGetInteger(attr, i));
3547 break;
3548
3549 case IPP_TAG_BOOLEAN :
3550 cupsFilePuts(data->outfile, ippGetBoolean(attr, 0) ? " true" : " false");
3551
3552 for (i = 1; i < count; i ++)
3553 cupsFilePuts(data->outfile, ippGetBoolean(attr, 1) ? ",true" : ",false");
3554 break;
3555
3556 case IPP_TAG_RANGE :
3557 for (i = 0; i < count; i ++)
3558 {
3559 int upper, lower = ippGetRange(attr, i, &upper);
3560
3561 cupsFilePrintf(data->outfile, "%s%d-%d", i ? "," : " ", lower, upper);
3562 }
3563 break;
3564
3565 case IPP_TAG_RESOLUTION :
3566 for (i = 0; i < count; i ++)
3567 {
3568 ipp_res_t units;
3569 int yres, xres = ippGetResolution(attr, i, &yres, &units);
3570
3571 cupsFilePrintf(data->outfile, "%s%dx%d%s", i ? "," : " ", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
3572 }
3573 break;
3574
3575 case IPP_TAG_DATE :
3576 for (i = 0; i < count; i ++)
3577 cupsFilePrintf(data->outfile, "%s%s", i ? "," : " ", iso_date(ippGetDate(attr, i)));
3578 break;
3579
3580 case IPP_TAG_STRING :
3581 for (i = 0; i < count; i ++)
3582 {
3583 int len;
3584 const char *s = (const char *)ippGetOctetString(attr, i, &len);
3585
3586 cupsFilePuts(data->outfile, i ? "," : " ");
3587 print_ippserver_string(data, s, (size_t)len);
3588 }
3589 break;
3590
3591 case IPP_TAG_TEXT :
3592 case IPP_TAG_TEXTLANG :
3593 case IPP_TAG_NAME :
3594 case IPP_TAG_NAMELANG :
3595 case IPP_TAG_KEYWORD :
3596 case IPP_TAG_URI :
3597 case IPP_TAG_URISCHEME :
3598 case IPP_TAG_CHARSET :
3599 case IPP_TAG_LANGUAGE :
3600 case IPP_TAG_MIMETYPE :
3601 for (i = 0; i < count; i ++)
3602 {
3603 const char *s = ippGetString(attr, i, NULL);
3604
3605 cupsFilePuts(data->outfile, i ? "," : " ");
3606 print_ippserver_string(data, s, strlen(s));
3607 }
3608 break;
3609
3610 case IPP_TAG_BEGIN_COLLECTION :
3611 for (i = 0; i < count; i ++)
3612 {
3613 ipp_t *col = ippGetCollection(attr, i);
3614
3615 cupsFilePuts(data->outfile, i ? ",{\n" : " {\n");
3616 for (colattr = ippFirstAttribute(col); colattr; colattr = ippNextAttribute(col))
3617 print_ippserver_attr(data, colattr, indent + 4);
3618 cupsFilePrintf(data->outfile, "%*s}", indent, "");
3619 }
3620 break;
3621
3622 default :
3623 /* Out-of-band value */
3624 break;
3625 }
3626
3627 cupsFilePuts(data->outfile, "\n");
3628 }
3629
3630
3631 /*
3632 * 'print_ippserver_string()' - Print a string suitable for use by ippserver.
3633 */
3634
3635 static void
print_ippserver_string(ipptool_test_t * data,const char * s,size_t len)3636 print_ippserver_string(
3637 ipptool_test_t *data, /* I - Test data */
3638 const char *s, /* I - String to print */
3639 size_t len) /* I - Length of string */
3640 {
3641 cupsFilePutChar(data->outfile, '\"');
3642 while (len > 0)
3643 {
3644 if (*s == '\"' || *s == '\\')
3645 cupsFilePutChar(data->outfile, '\\');
3646 cupsFilePutChar(data->outfile, *s);
3647
3648 s ++;
3649 len --;
3650 }
3651 cupsFilePutChar(data->outfile, '\"');
3652 }
3653
3654
3655 /*
3656 * 'print_json_attr()' - Print an attribute in JSON format.
3657 */
3658
3659 static void
print_json_attr(ipptool_test_t * data,ipp_attribute_t * attr,int indent)3660 print_json_attr(
3661 ipptool_test_t *data, /* I - Test data */
3662 ipp_attribute_t *attr, /* I - IPP attribute */
3663 int indent) /* I - Indentation */
3664 {
3665 const char *name = ippGetName(attr);
3666 /* Name of attribute */
3667 int i, /* Looping var */
3668 count = ippGetCount(attr);
3669 /* Number of values */
3670 ipp_attribute_t *colattr; /* Collection attribute */
3671
3672
3673 cupsFilePrintf(data->outfile, "%*s", indent, "");
3674 print_json_string(data, name, strlen(name));
3675
3676 switch (ippGetValueTag(attr))
3677 {
3678 case IPP_TAG_INTEGER :
3679 case IPP_TAG_ENUM :
3680 if (count == 1)
3681 {
3682 cupsFilePrintf(data->outfile, ": %d", ippGetInteger(attr, 0));
3683 }
3684 else
3685 {
3686 cupsFilePuts(data->outfile, ": [\n");
3687 for (i = 0; i < count; i ++)
3688 cupsFilePrintf(data->outfile, "%*s%d%s", indent + 4, "", ippGetInteger(attr, i), (i + 1) < count ? ",\n" : "\n");
3689 cupsFilePrintf(data->outfile, "%*s]", indent, "");
3690 }
3691 break;
3692
3693 case IPP_TAG_BOOLEAN :
3694 if (count == 1)
3695 {
3696 cupsFilePrintf(data->outfile, ": %s", ippGetBoolean(attr, 0) ? "true" : "false");
3697 }
3698 else
3699 {
3700 cupsFilePuts(data->outfile, ": [\n");
3701 for (i = 0; i < count; i ++)
3702 cupsFilePrintf(data->outfile, "%*s%s%s", indent + 4, "", ippGetBoolean(attr, i) ? "true" : "false", (i + 1) < count ? ",\n" : "\n");
3703 cupsFilePrintf(data->outfile, "%*s]", indent, "");
3704 }
3705 break;
3706
3707 case IPP_TAG_RANGE :
3708 if (count == 1)
3709 {
3710 int upper, lower = ippGetRange(attr, 0, &upper);
3711
3712 cupsFilePrintf(data->outfile, ": {\n%*s\"lower\": %d,\n%*s\"upper\":%d\n%*s}", indent + 4, "", lower, indent + 4, "", upper, indent, "");
3713 }
3714 else
3715 {
3716 cupsFilePuts(data->outfile, ": [\n");
3717 for (i = 0; i < count; i ++)
3718 {
3719 int upper, lower = ippGetRange(attr, i, &upper);
3720
3721 cupsFilePrintf(data->outfile, "%*s{\n%*s\"lower\": %d,\n%*s\"upper\":%d\n%*s},\n", indent + 4, "", indent + 8, "", lower, indent + 8, "", upper, indent + 4, "");
3722 }
3723 cupsFilePrintf(data->outfile, "%*s]", indent, "");
3724 }
3725 break;
3726
3727 case IPP_TAG_RESOLUTION :
3728 if (count == 1)
3729 {
3730 ipp_res_t units;
3731 int yres, xres = ippGetResolution(attr, 0, &yres, &units);
3732
3733 cupsFilePrintf(data->outfile, ": {\n%*s\"units\": \"%s\",\n%*s\"xres\": %d,\n%*s\"yres\":%d\n%*s}", indent + 4, "", units == IPP_RES_PER_INCH ? "dpi" : "dpcm", indent + 4, "", xres, indent + 4, "", yres, indent, "");
3734 }
3735 else
3736 {
3737 cupsFilePuts(data->outfile, ": [\n");
3738 for (i = 0; i < count; i ++)
3739 {
3740 ipp_res_t units;
3741 int yres, xres = ippGetResolution(attr, i, &yres, &units);
3742
3743 cupsFilePrintf(data->outfile, "%*s{\n%*s\"units\": \"%s\",\n%*s\"xres\": %d,\n%*s\"yres\":%d\n%*s},\n", indent + 4, "", indent + 8, "", units == IPP_RES_PER_INCH ? "dpi" : "dpcm", indent + 8, "", xres, indent + 8, "", yres, indent + 4, "");
3744 }
3745 cupsFilePrintf(data->outfile, "%*s]", indent, "");
3746 }
3747 break;
3748
3749 case IPP_TAG_DATE :
3750 if (count == 1)
3751 {
3752 cupsFilePrintf(data->outfile, ": \"%s\"", iso_date(ippGetDate(attr, 0)));
3753 }
3754 else
3755 {
3756 cupsFilePuts(data->outfile, ": [\n");
3757 for (i = 0; i < count; i ++)
3758 cupsFilePrintf(data->outfile, "%*s\"%s\"%s", indent + 4, "", iso_date(ippGetDate(attr, i)), (i + 1) < count ? ",\n" : "\n");
3759 cupsFilePrintf(data->outfile, "%*s]", indent, "");
3760 }
3761 break;
3762
3763 case IPP_TAG_STRING :
3764 if (count == 1)
3765 {
3766 int len;
3767 const char *s = (const char *)ippGetOctetString(attr, 0, &len);
3768
3769 cupsFilePuts(data->outfile, ": \"");
3770 while (len > 0)
3771 {
3772 cupsFilePrintf(data->outfile, "%02X", *s++ & 255);
3773 len --;
3774 }
3775 cupsFilePuts(data->outfile, "\"");
3776 }
3777 else
3778 {
3779 cupsFilePuts(data->outfile, ": [\n");
3780 for (i = 0; i < count; i ++)
3781 {
3782 int len;
3783 const char *s = (const char *)ippGetOctetString(attr, i, &len);
3784
3785 cupsFilePrintf(data->outfile, "%*s\"", indent + 4, "");
3786 while (len > 0)
3787 {
3788 cupsFilePrintf(data->outfile, "%02X", *s++ & 255);
3789 len --;
3790 }
3791 cupsFilePuts(data->outfile, (i + 1) < count ? "\",\n" : "\"\n");
3792 }
3793 cupsFilePrintf(data->outfile, "%*s]", indent, "");
3794 }
3795 break;
3796
3797 case IPP_TAG_TEXT :
3798 case IPP_TAG_TEXTLANG :
3799 case IPP_TAG_NAME :
3800 case IPP_TAG_NAMELANG :
3801 case IPP_TAG_KEYWORD :
3802 case IPP_TAG_URI :
3803 case IPP_TAG_URISCHEME :
3804 case IPP_TAG_CHARSET :
3805 case IPP_TAG_LANGUAGE :
3806 case IPP_TAG_MIMETYPE :
3807 if (count == 1)
3808 {
3809 const char *s = ippGetString(attr, 0, NULL);
3810
3811 cupsFilePuts(data->outfile, ": ");
3812 print_json_string(data, s, strlen(s));
3813 }
3814 else
3815 {
3816 cupsFilePuts(data->outfile, ": [\n");
3817 for (i = 0; i < count; i ++)
3818 {
3819 const char *s = ippGetString(attr, i, NULL);
3820
3821 cupsFilePrintf(data->outfile, "%*s", indent + 4, "");
3822 print_json_string(data, s, strlen(s));
3823 cupsFilePuts(data->outfile, (i + 1) < count ? ",\n" : "\n");
3824 }
3825 cupsFilePrintf(data->outfile, "%*s]", indent, "");
3826 }
3827 break;
3828
3829 case IPP_TAG_BEGIN_COLLECTION :
3830 if (count == 1)
3831 {
3832 ipp_t *col = ippGetCollection(attr, 0);
3833
3834 cupsFilePuts(data->outfile, ": {\n");
3835 colattr = ippFirstAttribute(col);
3836 while (colattr)
3837 {
3838 print_json_attr(data, colattr, indent + 4);
3839 colattr = ippNextAttribute(col);
3840 cupsFilePuts(data->outfile, colattr ? ",\n" : "\n");
3841 }
3842 cupsFilePrintf(data->outfile, "%*s}", indent, "");
3843 }
3844 else
3845 {
3846 cupsFilePuts(data->outfile, ": [\n");
3847 for (i = 0; i < count; i ++)
3848 {
3849 ipp_t *col = ippGetCollection(attr, i);
3850
3851 cupsFilePrintf(data->outfile, "%*s{\n", indent + 4, "");
3852 colattr = ippFirstAttribute(col);
3853 while (colattr)
3854 {
3855 print_json_attr(data, colattr, indent + 8);
3856 colattr = ippNextAttribute(col);
3857 cupsFilePuts(data->outfile, colattr ? ",\n" : "\n");
3858 }
3859 cupsFilePrintf(data->outfile, "%*s}%s", indent + 4, "", (i + 1) < count ? ",\n" : "\n");
3860 }
3861 cupsFilePrintf(data->outfile, "%*s]", indent, "");
3862 }
3863 break;
3864
3865 default :
3866 /* Out-of-band value */
3867 cupsFilePrintf(data->outfile, ": null");
3868 break;
3869 }
3870 }
3871
3872
3873 /*
3874 * 'print_json_string()' - Print a string in JSON format.
3875 */
3876
3877 static void
print_json_string(ipptool_test_t * data,const char * s,size_t len)3878 print_json_string(
3879 ipptool_test_t *data, /* I - Test data */
3880 const char *s, /* I - String to print */
3881 size_t len) /* I - Length of string */
3882 {
3883 cupsFilePutChar(data->outfile, '\"');
3884 while (len > 0)
3885 {
3886 switch (*s)
3887 {
3888 case '\"' :
3889 case '\\' :
3890 cupsFilePutChar(data->outfile, '\\');
3891 cupsFilePutChar(data->outfile, *s);
3892 break;
3893
3894 case '\n' :
3895 cupsFilePuts(data->outfile, "\\n");
3896 break;
3897
3898 case '\r' :
3899 cupsFilePuts(data->outfile, "\\r");
3900 break;
3901
3902 case '\t' :
3903 cupsFilePuts(data->outfile, "\\t");
3904 break;
3905
3906 default :
3907 if (*s < ' ' && *s >= 0)
3908 cupsFilePrintf(data->outfile, "\\%03o", *s);
3909 else
3910 cupsFilePutChar(data->outfile, *s);
3911 break;
3912 }
3913
3914 s ++;
3915 len --;
3916 }
3917 cupsFilePutChar(data->outfile, '\"');
3918 }
3919
3920
3921 /*
3922 * 'print_line()' - Print a line of formatted or CSV text.
3923 */
3924
3925 static ipp_attribute_t * /* O - Next attribute */
print_line(ipptool_test_t * data,ipp_t * ipp,ipp_attribute_t * attr,int num_displayed,char ** displayed,size_t * widths)3926 print_line(
3927 ipptool_test_t *data, /* I - Test data */
3928 ipp_t *ipp, /* I - Response message */
3929 ipp_attribute_t *attr, /* I - First attribute for line */
3930 int num_displayed, /* I - Number of attributes to display */
3931 char **displayed, /* I - Attributes to display */
3932 size_t *widths) /* I - Column widths */
3933 {
3934 int i; /* Looping var */
3935 size_t maxlength; /* Max length of all columns */
3936 ipp_attribute_t *current = attr; /* Current attribute */
3937 char *values[MAX_DISPLAY]; /* Strings to display */
3938
3939
3940 /*
3941 * Get the maximum string length we have to show and allocate...
3942 */
3943
3944 for (i = 1, maxlength = widths[0]; i < num_displayed; i ++)
3945 if (widths[i] > maxlength)
3946 maxlength = widths[i];
3947
3948 maxlength += 2;
3949
3950 /*
3951 * Loop through the attributes to display...
3952 */
3953
3954 if (attr)
3955 {
3956 // Collect the values...
3957 memset(values, 0, sizeof(values));
3958
3959 for (; current; current = ippNextAttribute(ipp))
3960 {
3961 if (!ippGetName(current))
3962 break;
3963
3964 for (i = 0; i < num_displayed; i ++)
3965 {
3966 if (!strcmp(ippGetName(current), displayed[i]))
3967 {
3968 if ((values[i] = (char *)calloc(1, maxlength)) != NULL)
3969 ippAttributeString(current, values[i], maxlength);
3970 break;
3971 }
3972 }
3973 }
3974
3975 // Output the line...
3976 for (i = 0; i < num_displayed; i ++)
3977 {
3978 if (i)
3979 cupsFilePutChar(data->outfile, ' ');
3980
3981 cupsFilePrintf(data->outfile, "%*s", (int)-widths[i], values[i] ? values[i] : "");
3982 free(values[i]);
3983 }
3984 cupsFilePutChar(data->outfile, '\n');
3985 }
3986 else
3987 {
3988 // Show column headings...
3989 char *buffer = (char *)malloc(maxlength);
3990 // Buffer for separator lines
3991
3992 if (!buffer)
3993 return (current);
3994
3995 for (i = 0; i < num_displayed; i ++)
3996 {
3997 if (i)
3998 cupsFilePutChar(data->outfile, ' ');
3999
4000 cupsFilePrintf(data->outfile, "%*s", (int)-widths[i], displayed[i]);
4001 }
4002 cupsFilePutChar(data->outfile, '\n');
4003
4004 for (i = 0; i < num_displayed; i ++)
4005 {
4006 if (i)
4007 cupsFilePutChar(data->outfile, ' ');
4008
4009 memset(buffer, '-', widths[i]);
4010 buffer[widths[i]] = '\0';
4011 cupsFilePuts(data->outfile, buffer);
4012 }
4013 cupsFilePutChar(data->outfile, '\n');
4014 free(buffer);
4015 }
4016
4017 return (current);
4018 }
4019
4020
4021 /*
4022 * 'print_xml_header()' - Print a standard XML plist header.
4023 */
4024
4025 static void
print_xml_header(ipptool_test_t * data)4026 print_xml_header(ipptool_test_t *data)/* I - Test data */
4027 {
4028 if (!data->xml_header)
4029 {
4030 cupsFilePuts(data->outfile, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
4031 cupsFilePuts(data->outfile, "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n");
4032 cupsFilePuts(data->outfile, "<plist version=\"1.0\">\n");
4033 cupsFilePuts(data->outfile, "<dict>\n");
4034 cupsFilePuts(data->outfile, "<key>ipptoolVersion</key>\n");
4035 cupsFilePuts(data->outfile, "<string>" CUPS_SVERSION "</string>\n");
4036 cupsFilePuts(data->outfile, "<key>Transfer</key>\n");
4037 cupsFilePrintf(data->outfile, "<string>%s</string>\n", data->transfer == IPPTOOL_TRANSFER_AUTO ? "auto" : data->transfer == IPPTOOL_TRANSFER_CHUNKED ? "chunked" : "length");
4038 cupsFilePuts(data->outfile, "<key>Tests</key>\n");
4039 cupsFilePuts(data->outfile, "<array>\n");
4040
4041 data->xml_header = 1;
4042 }
4043 }
4044
4045
4046 /*
4047 * 'print_xml_string()' - Print an XML string with escaping.
4048 */
4049
4050 static void
print_xml_string(cups_file_t * outfile,const char * element,const char * s)4051 print_xml_string(cups_file_t *outfile, /* I - Test data */
4052 const char *element, /* I - Element name or NULL */
4053 const char *s) /* I - String to print */
4054 {
4055 if (element)
4056 cupsFilePrintf(outfile, "<%s>", element);
4057
4058 while (*s)
4059 {
4060 if (*s == '&')
4061 cupsFilePuts(outfile, "&");
4062 else if (*s == '<')
4063 cupsFilePuts(outfile, "<");
4064 else if (*s == '>')
4065 cupsFilePuts(outfile, ">");
4066 else if ((*s & 0xe0) == 0xc0)
4067 {
4068 /*
4069 * Validate UTF-8 two-byte sequence...
4070 */
4071
4072 if ((s[1] & 0xc0) != 0x80)
4073 {
4074 cupsFilePutChar(outfile, '?');
4075 s ++;
4076 }
4077 else
4078 {
4079 cupsFilePutChar(outfile, *s++);
4080 cupsFilePutChar(outfile, *s);
4081 }
4082 }
4083 else if ((*s & 0xf0) == 0xe0)
4084 {
4085 /*
4086 * Validate UTF-8 three-byte sequence...
4087 */
4088
4089 if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80)
4090 {
4091 cupsFilePutChar(outfile, '?');
4092 s += 2;
4093 }
4094 else
4095 {
4096 cupsFilePutChar(outfile, *s++);
4097 cupsFilePutChar(outfile, *s++);
4098 cupsFilePutChar(outfile, *s);
4099 }
4100 }
4101 else if ((*s & 0xf8) == 0xf0)
4102 {
4103 /*
4104 * Validate UTF-8 four-byte sequence...
4105 */
4106
4107 if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 ||
4108 (s[3] & 0xc0) != 0x80)
4109 {
4110 cupsFilePutChar(outfile, '?');
4111 s += 3;
4112 }
4113 else
4114 {
4115 cupsFilePutChar(outfile, *s++);
4116 cupsFilePutChar(outfile, *s++);
4117 cupsFilePutChar(outfile, *s++);
4118 cupsFilePutChar(outfile, *s);
4119 }
4120 }
4121 else if ((*s & 0x80) || (*s < ' ' && !isspace(*s & 255)))
4122 {
4123 /*
4124 * Invalid control character...
4125 */
4126
4127 cupsFilePutChar(outfile, '?');
4128 }
4129 else
4130 cupsFilePutChar(outfile, *s);
4131
4132 s ++;
4133 }
4134
4135 if (element)
4136 cupsFilePrintf(outfile, "</%s>\n", element);
4137 }
4138
4139
4140 /*
4141 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
4142 */
4143
4144 static void
print_xml_trailer(ipptool_test_t * data,int success,const char * message)4145 print_xml_trailer(
4146 ipptool_test_t *data, /* I - Test data */
4147 int success, /* I - 1 on success, 0 on failure */
4148 const char *message) /* I - Error message or NULL */
4149 {
4150 if (data->xml_header)
4151 {
4152 cupsFilePuts(data->outfile, "</array>\n");
4153 cupsFilePuts(data->outfile, "<key>Successful</key>\n");
4154 cupsFilePuts(data->outfile, success ? "<true />\n" : "<false />\n");
4155 if (message)
4156 {
4157 cupsFilePuts(data->outfile, "<key>ErrorMessage</key>\n");
4158 print_xml_string(data->outfile, "string", message);
4159 }
4160 cupsFilePuts(data->outfile, "</dict>\n");
4161 cupsFilePuts(data->outfile, "</plist>\n");
4162
4163 data->xml_header = 0;
4164 }
4165 }
4166
4167
4168 #ifndef _WIN32
4169 /*
4170 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
4171 */
4172
4173 static void
sigterm_handler(int sig)4174 sigterm_handler(int sig) /* I - Signal number (unused) */
4175 {
4176 (void)sig;
4177
4178 Cancel = 1;
4179
4180 signal(SIGINT, SIG_DFL);
4181 signal(SIGTERM, SIG_DFL);
4182 }
4183 #endif /* !_WIN32 */
4184
4185
4186 /*
4187 * 'timeout_cb()' - Handle HTTP timeouts.
4188 */
4189
4190 static int /* O - 1 to continue, 0 to cancel */
timeout_cb(http_t * http,void * user_data)4191 timeout_cb(http_t *http, /* I - Connection to server */
4192 void *user_data) /* I - User data (unused) */
4193 {
4194 int buffered = 0; /* Bytes buffered but not yet sent */
4195
4196
4197 (void)user_data;
4198
4199 /*
4200 * If the socket still have data waiting to be sent to the printer (as can
4201 * happen if the printer runs out of paper), continue to wait until the output
4202 * buffer is empty...
4203 */
4204
4205 #ifdef SO_NWRITE /* macOS and some versions of Linux */
4206 socklen_t len = sizeof(buffered); /* Size of return value */
4207
4208 if (getsockopt(httpGetFd(http), SOL_SOCKET, SO_NWRITE, &buffered, &len))
4209 buffered = 0;
4210
4211 #elif defined(SIOCOUTQ) /* Others except Windows */
4212 if (ioctl(httpGetFd(http), SIOCOUTQ, &buffered))
4213 buffered = 0;
4214
4215 #else /* Windows (not possible) */
4216 (void)http;
4217 #endif /* SO_NWRITE */
4218
4219 return (buffered > 0);
4220 }
4221
4222
4223 /*
4224 * 'token_cb()' - Parse test file-specific tokens and run tests.
4225 */
4226
4227 static int /* O - 1 to continue, 0 to stop */
token_cb(_ipp_file_t * f,_ipp_vars_t * vars,ipptool_test_t * data,const char * token)4228 token_cb(_ipp_file_t *f, /* I - IPP file data */
4229 _ipp_vars_t *vars, /* I - IPP variables */
4230 ipptool_test_t *data, /* I - Test data */
4231 const char *token) /* I - Current token */
4232 {
4233 char name[1024], /* Name string */
4234 temp[1024], /* Temporary string */
4235 value[1024], /* Value string */
4236 *ptr; /* Pointer into value */
4237
4238
4239 if (!token)
4240 {
4241 /*
4242 * Initialize state as needed (nothing for now...)
4243 */
4244
4245 return (1);
4246 }
4247 else if (f->attrs)
4248 {
4249 /*
4250 * Parse until we see a close brace...
4251 */
4252
4253 if (_cups_strcasecmp(token, "COUNT") &&
4254 _cups_strcasecmp(token, "DEFINE-MATCH") &&
4255 _cups_strcasecmp(token, "DEFINE-NO-MATCH") &&
4256 _cups_strcasecmp(token, "DEFINE-VALUE") &&
4257 _cups_strcasecmp(token, "DISPLAY-MATCH") &&
4258 _cups_strcasecmp(token, "IF-DEFINED") &&
4259 _cups_strcasecmp(token, "IF-NOT-DEFINED") &&
4260 _cups_strcasecmp(token, "IN-GROUP") &&
4261 _cups_strcasecmp(token, "OF-TYPE") &&
4262 _cups_strcasecmp(token, "REPEAT-LIMIT") &&
4263 _cups_strcasecmp(token, "REPEAT-MATCH") &&
4264 _cups_strcasecmp(token, "REPEAT-NO-MATCH") &&
4265 _cups_strcasecmp(token, "SAME-COUNT-AS") &&
4266 _cups_strcasecmp(token, "WITH-ALL-VALUES") &&
4267 _cups_strcasecmp(token, "WITH-ALL-HOSTNAMES") &&
4268 _cups_strcasecmp(token, "WITH-ALL-RESOURCES") &&
4269 _cups_strcasecmp(token, "WITH-ALL-SCHEMES") &&
4270 _cups_strcasecmp(token, "WITH-DISTINCT-VALUES") &&
4271 _cups_strcasecmp(token, "WITH-HOSTNAME") &&
4272 _cups_strcasecmp(token, "WITH-RESOURCE") &&
4273 _cups_strcasecmp(token, "WITH-SCHEME") &&
4274 _cups_strcasecmp(token, "WITH-VALUE") &&
4275 _cups_strcasecmp(token, "WITH-VALUE-FROM"))
4276 data->last_expect = NULL;
4277
4278 if (_cups_strcasecmp(token, "DEFINE-MATCH") &&
4279 _cups_strcasecmp(token, "DEFINE-NO-MATCH") &&
4280 _cups_strcasecmp(token, "IF-DEFINED") &&
4281 _cups_strcasecmp(token, "IF-NOT-DEFINED") &&
4282 _cups_strcasecmp(token, "REPEAT-LIMIT") &&
4283 _cups_strcasecmp(token, "REPEAT-MATCH") &&
4284 _cups_strcasecmp(token, "REPEAT-NO-MATCH"))
4285 data->last_status = NULL;
4286
4287 if (!strcmp(token, "}"))
4288 {
4289 return (do_test(f, data));
4290 }
4291 else if (!strcmp(token, "MONITOR-PRINTER-STATE"))
4292 {
4293 if (data->monitor_uri)
4294 {
4295 print_fatal_error(data, "Extra MONITOR-PRINTER-STATE seen on line %d of \"%s\".", f->linenum, f->filename);
4296 return (0);
4297 }
4298
4299 return (parse_monitor_printer_state(f, data));
4300 }
4301 else if (!strcmp(token, "COMPRESSION"))
4302 {
4303 /*
4304 * COMPRESSION none
4305 * COMPRESSION deflate
4306 * COMPRESSION gzip
4307 */
4308
4309 if (_ippFileReadToken(f, temp, sizeof(temp)))
4310 {
4311 _ippVarsExpand(vars, data->compression, temp, sizeof(data->compression));
4312 #ifdef HAVE_LIBZ
4313 if (strcmp(data->compression, "none") && strcmp(data->compression, "deflate") &&
4314 strcmp(data->compression, "gzip"))
4315 #else
4316 if (strcmp(data->compression, "none"))
4317 #endif /* HAVE_LIBZ */
4318 {
4319 print_fatal_error(data, "Unsupported COMPRESSION value \"%s\" on line %d of \"%s\".", data->compression, f->linenum, f->filename);
4320 return (0);
4321 }
4322
4323 if (!strcmp(data->compression, "none"))
4324 data->compression[0] = '\0';
4325 }
4326 else
4327 {
4328 print_fatal_error(data, "Missing COMPRESSION value on line %d of \"%s\".", f->linenum, f->filename);
4329 return (0);
4330 }
4331 }
4332 else if (!strcmp(token, "DEFINE"))
4333 {
4334 /*
4335 * DEFINE name value
4336 */
4337
4338 if (_ippFileReadToken(f, name, sizeof(name)) && _ippFileReadToken(f, temp, sizeof(temp)))
4339 {
4340 _ippVarsExpand(vars, value, temp, sizeof(value));
4341 _ippVarsSet(vars, name, value);
4342 }
4343 else
4344 {
4345 print_fatal_error(data, "Missing DEFINE name and/or value on line %d of \"%s\".", f->linenum, f->filename);
4346 return (0);
4347 }
4348 }
4349 else if (!strcmp(token, "IGNORE-ERRORS"))
4350 {
4351 /*
4352 * IGNORE-ERRORS yes
4353 * IGNORE-ERRORS no
4354 */
4355
4356 if (_ippFileReadToken(f, temp, sizeof(temp)) && (!_cups_strcasecmp(temp, "yes") || !_cups_strcasecmp(temp, "no")))
4357 {
4358 data->ignore_errors = !_cups_strcasecmp(temp, "yes");
4359 }
4360 else
4361 {
4362 print_fatal_error(data, "Missing IGNORE-ERRORS value on line %d of \"%s\".", f->linenum, f->filename);
4363 return (0);
4364 }
4365 }
4366 else if (!_cups_strcasecmp(token, "NAME"))
4367 {
4368 /*
4369 * Name of test...
4370 */
4371
4372 if (_ippFileReadToken(f, temp, sizeof(temp)))
4373 {
4374 _ippVarsExpand(vars, data->name, temp, sizeof(data->name));
4375 }
4376 else
4377 {
4378 print_fatal_error(data, "Missing NAME string on line %d of \"%s\".", f->linenum, f->filename);
4379 return (0);
4380 }
4381 }
4382 else if (!_cups_strcasecmp(token, "PAUSE"))
4383 {
4384 /*
4385 * Pause with a message...
4386 */
4387
4388 if (_ippFileReadToken(f, temp, sizeof(temp)))
4389 {
4390 strlcpy(data->pause, temp, sizeof(data->pause));
4391 }
4392 else
4393 {
4394 print_fatal_error(data, "Missing PAUSE message on line %d of \"%s\".", f->linenum, f->filename);
4395 return (0);
4396 }
4397 }
4398 else if (!strcmp(token, "REQUEST-ID"))
4399 {
4400 /*
4401 * REQUEST-ID #
4402 * REQUEST-ID random
4403 */
4404
4405 if (_ippFileReadToken(f, temp, sizeof(temp)))
4406 {
4407 if (isdigit(temp[0] & 255))
4408 {
4409 data->request_id = atoi(temp) - 1;
4410 }
4411 else if (!_cups_strcasecmp(temp, "random"))
4412 {
4413 data->request_id = (CUPS_RAND() % 1000) * 137;
4414 }
4415 else
4416 {
4417 print_fatal_error(data, "Bad REQUEST-ID value \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
4418 return (0);
4419 }
4420 }
4421 else
4422 {
4423 print_fatal_error(data, "Missing REQUEST-ID value on line %d of \"%s\".", f->linenum, f->filename);
4424 return (0);
4425 }
4426 }
4427 else if (!strcmp(token, "PASS-IF-DEFINED"))
4428 {
4429 /*
4430 * PASS-IF-DEFINED variable
4431 */
4432
4433 if (_ippFileReadToken(f, name, sizeof(name)))
4434 {
4435 if (_ippVarsGet(vars, name))
4436 data->pass_test = 1;
4437 }
4438 else
4439 {
4440 print_fatal_error(data, "Missing PASS-IF-DEFINED value on line %d of \"%s\".", f->linenum, f->filename);
4441 return (0);
4442 }
4443 }
4444 else if (!strcmp(token, "PASS-IF-NOT-DEFINED"))
4445 {
4446 /*
4447 * PASS-IF-NOT-DEFINED variable
4448 */
4449
4450 if (_ippFileReadToken(f, name, sizeof(name)))
4451 {
4452 if (!_ippVarsGet(vars, name))
4453 data->pass_test = 1;
4454 }
4455 else
4456 {
4457 print_fatal_error(data, "Missing PASS-IF-NOT-DEFINED value on line %d of \"%s\".", f->linenum, f->filename);
4458 return (0);
4459 }
4460 }
4461 else if (!strcmp(token, "SKIP-IF-DEFINED"))
4462 {
4463 /*
4464 * SKIP-IF-DEFINED variable
4465 */
4466
4467 if (_ippFileReadToken(f, name, sizeof(name)))
4468 {
4469 if (_ippVarsGet(vars, name))
4470 data->skip_test = 1;
4471 }
4472 else
4473 {
4474 print_fatal_error(data, "Missing SKIP-IF-DEFINED value on line %d of \"%s\".", f->linenum, f->filename);
4475 return (0);
4476 }
4477 }
4478 else if (!strcmp(token, "SKIP-IF-MISSING"))
4479 {
4480 /*
4481 * SKIP-IF-MISSING filename
4482 */
4483
4484 if (_ippFileReadToken(f, temp, sizeof(temp)))
4485 {
4486 char filename[1024]; /* Filename */
4487
4488 _ippVarsExpand(vars, value, temp, sizeof(value));
4489 get_filename(f->filename, filename, temp, sizeof(filename));
4490
4491 if (access(filename, R_OK))
4492 data->skip_test = 1;
4493 }
4494 else
4495 {
4496 print_fatal_error(data, "Missing SKIP-IF-MISSING filename on line %d of \"%s\".", f->linenum, f->filename);
4497 return (0);
4498 }
4499 }
4500 else if (!strcmp(token, "SKIP-IF-NOT-DEFINED"))
4501 {
4502 /*
4503 * SKIP-IF-NOT-DEFINED variable
4504 */
4505
4506 if (_ippFileReadToken(f, name, sizeof(name)))
4507 {
4508 if (!_ippVarsGet(vars, name))
4509 data->skip_test = 1;
4510 }
4511 else
4512 {
4513 print_fatal_error(data, "Missing SKIP-IF-NOT-DEFINED value on line %d of \"%s\".", f->linenum, f->filename);
4514 return (0);
4515 }
4516 }
4517 else if (!strcmp(token, "SKIP-PREVIOUS-ERROR"))
4518 {
4519 /*
4520 * SKIP-PREVIOUS-ERROR yes
4521 * SKIP-PREVIOUS-ERROR no
4522 */
4523
4524 if (_ippFileReadToken(f, temp, sizeof(temp)) && (!_cups_strcasecmp(temp, "yes") || !_cups_strcasecmp(temp, "no")))
4525 {
4526 data->skip_previous = !_cups_strcasecmp(temp, "yes");
4527 }
4528 else
4529 {
4530 print_fatal_error(data, "Missing SKIP-PREVIOUS-ERROR value on line %d of \"%s\".", f->linenum, f->filename);
4531 return (0);
4532 }
4533 }
4534 else if (!strcmp(token, "TEST-ID"))
4535 {
4536 /*
4537 * TEST-ID "string"
4538 */
4539
4540 if (_ippFileReadToken(f, temp, sizeof(temp)))
4541 {
4542 _ippVarsExpand(vars, data->test_id, temp, sizeof(data->test_id));
4543 }
4544 else
4545 {
4546 print_fatal_error(data, "Missing TEST-ID value on line %d of \"%s\".", f->linenum, f->filename);
4547 return (0);
4548 }
4549 }
4550 else if (!strcmp(token, "TRANSFER"))
4551 {
4552 /*
4553 * TRANSFER auto
4554 * TRANSFER chunked
4555 * TRANSFER length
4556 */
4557
4558 if (_ippFileReadToken(f, temp, sizeof(temp)))
4559 {
4560 if (!strcmp(temp, "auto"))
4561 {
4562 data->transfer = IPPTOOL_TRANSFER_AUTO;
4563 }
4564 else if (!strcmp(temp, "chunked"))
4565 {
4566 data->transfer = IPPTOOL_TRANSFER_CHUNKED;
4567 }
4568 else if (!strcmp(temp, "length"))
4569 {
4570 data->transfer = IPPTOOL_TRANSFER_LENGTH;
4571 }
4572 else
4573 {
4574 print_fatal_error(data, "Bad TRANSFER value \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
4575 return (0);
4576 }
4577 }
4578 else
4579 {
4580 print_fatal_error(data, "Missing TRANSFER value on line %d of \"%s\".", f->linenum, f->filename);
4581 return (0);
4582 }
4583 }
4584 else if (!_cups_strcasecmp(token, "VERSION"))
4585 {
4586 if (_ippFileReadToken(f, temp, sizeof(temp)))
4587 {
4588 if (!strcmp(temp, "0.0"))
4589 {
4590 data->version = 0;
4591 }
4592 else if (!strcmp(temp, "1.0"))
4593 {
4594 data->version = 10;
4595 }
4596 else if (!strcmp(temp, "1.1"))
4597 {
4598 data->version = 11;
4599 }
4600 else if (!strcmp(temp, "2.0"))
4601 {
4602 data->version = 20;
4603 }
4604 else if (!strcmp(temp, "2.1"))
4605 {
4606 data->version = 21;
4607 }
4608 else if (!strcmp(temp, "2.2"))
4609 {
4610 data->version = 22;
4611 }
4612 else
4613 {
4614 print_fatal_error(data, "Bad VERSION \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
4615 return (0);
4616 }
4617 }
4618 else
4619 {
4620 print_fatal_error(data, "Missing VERSION number on line %d of \"%s\".", f->linenum, f->filename);
4621 return (0);
4622 }
4623 }
4624 else if (!_cups_strcasecmp(token, "RESOURCE"))
4625 {
4626 /*
4627 * Resource name...
4628 */
4629
4630 if (!_ippFileReadToken(f, data->resource, sizeof(data->resource)))
4631 {
4632 print_fatal_error(data, "Missing RESOURCE path on line %d of \"%s\".", f->linenum, f->filename);
4633 return (0);
4634 }
4635 }
4636 else if (!_cups_strcasecmp(token, "OPERATION"))
4637 {
4638 /*
4639 * Operation...
4640 */
4641
4642 ipp_op_t op; /* Operation code */
4643
4644 if (!_ippFileReadToken(f, temp, sizeof(temp)))
4645 {
4646 print_fatal_error(data, "Missing OPERATION code on line %d of \"%s\".", f->linenum, f->filename);
4647 return (0);
4648 }
4649
4650 _ippVarsExpand(vars, value, temp, sizeof(value));
4651
4652 if ((op = ippOpValue(value)) == (ipp_op_t)-1 && (op = (ipp_op_t)strtol(value, NULL, 0)) == 0)
4653 {
4654 print_fatal_error(data, "Bad OPERATION code \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
4655 return (0);
4656 }
4657
4658 ippSetOperation(f->attrs, op);
4659 }
4660 else if (!_cups_strcasecmp(token, "GROUP"))
4661 {
4662 /*
4663 * Attribute group...
4664 */
4665
4666 ipp_tag_t group_tag; /* Group tag */
4667
4668 if (!_ippFileReadToken(f, temp, sizeof(temp)))
4669 {
4670 print_fatal_error(data, "Missing GROUP tag on line %d of \"%s\".", f->linenum, f->filename);
4671 return (0);
4672 }
4673
4674 if ((group_tag = ippTagValue(temp)) == IPP_TAG_ZERO || group_tag >= IPP_TAG_UNSUPPORTED_VALUE)
4675 {
4676 print_fatal_error(data, "Bad GROUP tag \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
4677 return (0);
4678 }
4679
4680 if (group_tag == f->group_tag)
4681 ippAddSeparator(f->attrs);
4682
4683 f->group_tag = group_tag;
4684 }
4685 else if (!_cups_strcasecmp(token, "DELAY"))
4686 {
4687 /*
4688 * Delay before operation...
4689 */
4690
4691 double dval; /* Delay value */
4692
4693 if (!_ippFileReadToken(f, temp, sizeof(temp)))
4694 {
4695 print_fatal_error(data, "Missing DELAY value on line %d of \"%s\".", f->linenum, f->filename);
4696 return (0);
4697 }
4698
4699 _ippVarsExpand(vars, value, temp, sizeof(value));
4700
4701 if ((dval = _cupsStrScand(value, &ptr, localeconv())) < 0.0 || (*ptr && *ptr != ','))
4702 {
4703 print_fatal_error(data, "Bad DELAY value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
4704 return (0);
4705 }
4706
4707 data->delay = (useconds_t)(1000000.0 * dval);
4708
4709 if (*ptr == ',')
4710 {
4711 if ((dval = _cupsStrScand(ptr + 1, &ptr, localeconv())) <= 0.0 || *ptr)
4712 {
4713 print_fatal_error(data, "Bad DELAY value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
4714 return (0);
4715 }
4716
4717 data->repeat_interval = (useconds_t)(1000000.0 * dval);
4718 }
4719 else
4720 data->repeat_interval = data->delay;
4721 }
4722 else if (!_cups_strcasecmp(token, "FILE"))
4723 {
4724 /*
4725 * File...
4726 */
4727
4728 if (!_ippFileReadToken(f, temp, sizeof(temp)))
4729 {
4730 print_fatal_error(data, "Missing FILE filename on line %d of \"%s\".", f->linenum, f->filename);
4731 return (0);
4732 }
4733
4734 _ippVarsExpand(vars, value, temp, sizeof(value));
4735 get_filename(f->filename, data->file, value, sizeof(data->file));
4736
4737 if (access(data->file, R_OK))
4738 {
4739 print_fatal_error(data, "Filename \"%s\" (mapped to \"%s\") on line %d of \"%s\" cannot be read.", value, data->file, f->linenum, f->filename);
4740 return (0);
4741 }
4742 }
4743 else if (!_cups_strcasecmp(token, "STATUS"))
4744 {
4745 /*
4746 * Status...
4747 */
4748
4749 if (data->num_statuses >= (int)(sizeof(data->statuses) / sizeof(data->statuses[0])))
4750 {
4751 print_fatal_error(data, "Too many STATUS's on line %d of \"%s\".", f->linenum, f->filename);
4752 return (0);
4753 }
4754
4755 if (!_ippFileReadToken(f, temp, sizeof(temp)))
4756 {
4757 print_fatal_error(data, "Missing STATUS code on line %d of \"%s\".", f->linenum, f->filename);
4758 return (0);
4759 }
4760
4761 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)
4762 {
4763 print_fatal_error(data, "Bad STATUS code \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
4764 return (0);
4765 }
4766
4767 data->last_status = data->statuses + data->num_statuses;
4768 data->num_statuses ++;
4769
4770 data->last_status->define_match = NULL;
4771 data->last_status->define_no_match = NULL;
4772 data->last_status->if_defined = NULL;
4773 data->last_status->if_not_defined = NULL;
4774 data->last_status->repeat_limit = 1000;
4775 data->last_status->repeat_match = 0;
4776 data->last_status->repeat_no_match = 0;
4777 }
4778 else if (!_cups_strcasecmp(token, "EXPECT") || !_cups_strcasecmp(token, "EXPECT-ALL"))
4779 {
4780 /*
4781 * Expected attributes...
4782 */
4783
4784 int expect_all = !_cups_strcasecmp(token, "EXPECT-ALL");
4785
4786 if (data->num_expects >= (int)(sizeof(data->expects) / sizeof(data->expects[0])))
4787 {
4788 print_fatal_error(data, "Too many EXPECT's on line %d of \"%s\".", f->linenum, f->filename);
4789 return (0);
4790 }
4791
4792 if (!_ippFileReadToken(f, name, sizeof(name)))
4793 {
4794 print_fatal_error(data, "Missing EXPECT name on line %d of \"%s\".", f->linenum, f->filename);
4795 return (0);
4796 }
4797
4798 data->last_expect = data->expects + data->num_expects;
4799 data->num_expects ++;
4800
4801 memset(data->last_expect, 0, sizeof(ipptool_expect_t));
4802 data->last_expect->repeat_limit = 1000;
4803 data->last_expect->expect_all = expect_all;
4804
4805 if (name[0] == '!')
4806 {
4807 data->last_expect->not_expect = 1;
4808 data->last_expect->name = strdup(name + 1);
4809 }
4810 else if (name[0] == '?')
4811 {
4812 data->last_expect->optional = 1;
4813 data->last_expect->name = strdup(name + 1);
4814 }
4815 else
4816 data->last_expect->name = strdup(name);
4817 }
4818 else if (!_cups_strcasecmp(token, "COUNT"))
4819 {
4820 int count; /* Count value */
4821
4822 if (!_ippFileReadToken(f, temp, sizeof(temp)))
4823 {
4824 print_fatal_error(data, "Missing COUNT number on line %d of \"%s\".", f->linenum, f->filename);
4825 return (0);
4826 }
4827
4828 if ((count = atoi(temp)) <= 0)
4829 {
4830 print_fatal_error(data, "Bad COUNT \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
4831 return (0);
4832 }
4833
4834 if (data->last_expect)
4835 {
4836 data->last_expect->count = count;
4837 }
4838 else
4839 {
4840 print_fatal_error(data, "COUNT without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
4841 return (0);
4842 }
4843 }
4844 else if (!_cups_strcasecmp(token, "DEFINE-MATCH"))
4845 {
4846 if (!_ippFileReadToken(f, temp, sizeof(temp)))
4847 {
4848 print_fatal_error(data, "Missing DEFINE-MATCH variable on line %d of \"%s\".", f->linenum, f->filename);
4849 return (0);
4850 }
4851
4852 if (data->last_expect)
4853 {
4854 data->last_expect->define_match = strdup(temp);
4855 }
4856 else if (data->last_status)
4857 {
4858 data->last_status->define_match = strdup(temp);
4859 }
4860 else
4861 {
4862 print_fatal_error(data, "DEFINE-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f->linenum, f->filename);
4863 return (0);
4864 }
4865 }
4866 else if (!_cups_strcasecmp(token, "DEFINE-NO-MATCH"))
4867 {
4868 if (!_ippFileReadToken(f, temp, sizeof(temp)))
4869 {
4870 print_fatal_error(data, "Missing DEFINE-NO-MATCH variable on line %d of \"%s\".", f->linenum, f->filename);
4871 return (0);
4872 }
4873
4874 if (data->last_expect)
4875 {
4876 data->last_expect->define_no_match = strdup(temp);
4877 }
4878 else if (data->last_status)
4879 {
4880 data->last_status->define_no_match = strdup(temp);
4881 }
4882 else
4883 {
4884 print_fatal_error(data, "DEFINE-NO-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f->linenum, f->filename);
4885 return (0);
4886 }
4887 }
4888 else if (!_cups_strcasecmp(token, "DEFINE-VALUE"))
4889 {
4890 if (!_ippFileReadToken(f, temp, sizeof(temp)))
4891 {
4892 print_fatal_error(data, "Missing DEFINE-VALUE variable on line %d of \"%s\".", f->linenum, f->filename);
4893 return (0);
4894 }
4895
4896 if (data->last_expect)
4897 {
4898 data->last_expect->define_value = strdup(temp);
4899 }
4900 else
4901 {
4902 print_fatal_error(data, "DEFINE-VALUE without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
4903 return (0);
4904 }
4905 }
4906 else if (!_cups_strcasecmp(token, "DISPLAY-MATCH"))
4907 {
4908 if (!_ippFileReadToken(f, temp, sizeof(temp)))
4909 {
4910 print_fatal_error(data, "Missing DISPLAY-MATCH mesaage on line %d of \"%s\".", f->linenum, f->filename);
4911 return (0);
4912 }
4913
4914 if (data->last_expect)
4915 {
4916 data->last_expect->display_match = strdup(temp);
4917 }
4918 else
4919 {
4920 print_fatal_error(data, "DISPLAY-MATCH without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
4921 return (0);
4922 }
4923 }
4924 else if (!_cups_strcasecmp(token, "OF-TYPE"))
4925 {
4926 if (!_ippFileReadToken(f, temp, sizeof(temp)))
4927 {
4928 print_fatal_error(data, "Missing OF-TYPE value tag(s) on line %d of \"%s\".", f->linenum, f->filename);
4929 return (0);
4930 }
4931
4932 if (data->last_expect)
4933 {
4934 data->last_expect->of_type = strdup(temp);
4935 }
4936 else
4937 {
4938 print_fatal_error(data, "OF-TYPE without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
4939 return (0);
4940 }
4941 }
4942 else if (!_cups_strcasecmp(token, "IN-GROUP"))
4943 {
4944 ipp_tag_t in_group; /* IN-GROUP value */
4945
4946 if (!_ippFileReadToken(f, temp, sizeof(temp)))
4947 {
4948 print_fatal_error(data, "Missing IN-GROUP group tag on line %d of \"%s\".", f->linenum, f->filename);
4949 return (0);
4950 }
4951
4952 if ((in_group = ippTagValue(temp)) == IPP_TAG_ZERO || in_group >= IPP_TAG_UNSUPPORTED_VALUE)
4953 {
4954 print_fatal_error(data, "Bad IN-GROUP group tag \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
4955 return (0);
4956 }
4957 else if (data->last_expect)
4958 {
4959 data->last_expect->in_group = in_group;
4960 }
4961 else
4962 {
4963 print_fatal_error(data, "IN-GROUP without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
4964 return (0);
4965 }
4966 }
4967 else if (!_cups_strcasecmp(token, "REPEAT-LIMIT"))
4968 {
4969 if (!_ippFileReadToken(f, temp, sizeof(temp)))
4970 {
4971 print_fatal_error(data, "Missing REPEAT-LIMIT value on line %d of \"%s\".", f->linenum, f->filename);
4972 return (0);
4973 }
4974 else if (atoi(temp) <= 0)
4975 {
4976 print_fatal_error(data, "Bad REPEAT-LIMIT value on line %d of \"%s\".", f->linenum, f->filename);
4977 return (0);
4978 }
4979
4980 if (data->last_status)
4981 {
4982 data->last_status->repeat_limit = atoi(temp);
4983 }
4984 else if (data->last_expect)
4985 {
4986 data->last_expect->repeat_limit = atoi(temp);
4987 }
4988 else
4989 {
4990 print_fatal_error(data, "REPEAT-LIMIT without a preceding EXPECT or STATUS on line %d of \"%s\".", f->linenum, f->filename);
4991 return (0);
4992 }
4993 }
4994 else if (!_cups_strcasecmp(token, "REPEAT-MATCH"))
4995 {
4996 if (data->last_status)
4997 {
4998 data->last_status->repeat_match = 1;
4999 }
5000 else if (data->last_expect)
5001 {
5002 data->last_expect->repeat_match = 1;
5003 }
5004 else
5005 {
5006 print_fatal_error(data, "REPEAT-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f->linenum, f->filename);
5007 return (0);
5008 }
5009 }
5010 else if (!_cups_strcasecmp(token, "REPEAT-NO-MATCH"))
5011 {
5012 if (data->last_status)
5013 {
5014 data->last_status->repeat_no_match = 1;
5015 }
5016 else if (data->last_expect)
5017 {
5018 data->last_expect->repeat_no_match = 1;
5019 }
5020 else
5021 {
5022 print_fatal_error(data, "REPEAT-NO-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f->linenum, f->filename);
5023 return (0);
5024 }
5025 }
5026 else if (!_cups_strcasecmp(token, "SAME-COUNT-AS"))
5027 {
5028 if (!_ippFileReadToken(f, temp, sizeof(temp)))
5029 {
5030 print_fatal_error(data, "Missing SAME-COUNT-AS name on line %d of \"%s\".", f->linenum, f->filename);
5031 return (0);
5032 }
5033
5034 if (data->last_expect)
5035 {
5036 data->last_expect->same_count_as = strdup(temp);
5037 }
5038 else
5039 {
5040 print_fatal_error(data, "SAME-COUNT-AS without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
5041 return (0);
5042 }
5043 }
5044 else if (!_cups_strcasecmp(token, "IF-DEFINED"))
5045 {
5046 if (!_ippFileReadToken(f, temp, sizeof(temp)))
5047 {
5048 print_fatal_error(data, "Missing IF-DEFINED name on line %d of \"%s\".", f->linenum, f->filename);
5049 return (0);
5050 }
5051
5052 if (data->last_expect)
5053 {
5054 data->last_expect->if_defined = strdup(temp);
5055 }
5056 else if (data->last_status)
5057 {
5058 data->last_status->if_defined = strdup(temp);
5059 }
5060 else
5061 {
5062 print_fatal_error(data, "IF-DEFINED without a preceding EXPECT or STATUS on line %d of \"%s\".", f->linenum, f->filename);
5063 return (0);
5064 }
5065 }
5066 else if (!_cups_strcasecmp(token, "IF-NOT-DEFINED"))
5067 {
5068 if (!_ippFileReadToken(f, temp, sizeof(temp)))
5069 {
5070 print_fatal_error(data, "Missing IF-NOT-DEFINED name on line %d of \"%s\".", f->linenum, f->filename);
5071 return (0);
5072 }
5073
5074 if (data->last_expect)
5075 {
5076 data->last_expect->if_not_defined = strdup(temp);
5077 }
5078 else if (data->last_status)
5079 {
5080 data->last_status->if_not_defined = strdup(temp);
5081 }
5082 else
5083 {
5084 print_fatal_error(data, "IF-NOT-DEFINED without a preceding EXPECT or STATUS on line %d of \"%s\".", f->linenum, f->filename);
5085 return (0);
5086 }
5087 }
5088 else if (!_cups_strcasecmp(token, "WITH-DISTINCT-VALUES"))
5089 {
5090 if (data->last_expect)
5091 {
5092 data->last_expect->with_distinct = 1;
5093 }
5094 else
5095 {
5096 print_fatal_error(data, "%s without a preceding EXPECT on line %d of \"%s\".", token, f->linenum, f->filename);
5097 return (0);
5098 }
5099 }
5100 else if (!_cups_strcasecmp(token, "WITH-ALL-VALUES") ||
5101 !_cups_strcasecmp(token, "WITH-ALL-HOSTNAMES") ||
5102 !_cups_strcasecmp(token, "WITH-ALL-RESOURCES") ||
5103 !_cups_strcasecmp(token, "WITH-ALL-SCHEMES") ||
5104 !_cups_strcasecmp(token, "WITH-HOSTNAME") ||
5105 !_cups_strcasecmp(token, "WITH-RESOURCE") ||
5106 !_cups_strcasecmp(token, "WITH-SCHEME") ||
5107 !_cups_strcasecmp(token, "WITH-VALUE"))
5108 {
5109 off_t lastpos; /* Last file position */
5110 int lastline; /* Last line number */
5111
5112 if (data->last_expect)
5113 {
5114 if (!_cups_strcasecmp(token, "WITH-ALL-HOSTNAMES") || !_cups_strcasecmp(token, "WITH-HOSTNAME"))
5115 data->last_expect->with_flags = IPPTOOL_WITH_HOSTNAME;
5116 else if (!_cups_strcasecmp(token, "WITH-ALL-RESOURCES") || !_cups_strcasecmp(token, "WITH-RESOURCE"))
5117 data->last_expect->with_flags = IPPTOOL_WITH_RESOURCE;
5118 else if (!_cups_strcasecmp(token, "WITH-ALL-SCHEMES") || !_cups_strcasecmp(token, "WITH-SCHEME"))
5119 data->last_expect->with_flags = IPPTOOL_WITH_SCHEME;
5120
5121 if (!_cups_strncasecmp(token, "WITH-ALL-", 9))
5122 data->last_expect->with_flags |= IPPTOOL_WITH_ALL;
5123 }
5124
5125 if (!_ippFileReadToken(f, temp, sizeof(temp)))
5126 {
5127 print_fatal_error(data, "Missing %s value on line %d of \"%s\".", token, f->linenum, f->filename);
5128 return (0);
5129 }
5130
5131 /*
5132 * Read additional comma-delimited values - needed since legacy test files
5133 * will have unquoted WITH-VALUE values with commas...
5134 */
5135
5136 ptr = temp + strlen(temp);
5137
5138 for (;;)
5139 {
5140 lastpos = cupsFileTell(f->fp);
5141 lastline = f->linenum;
5142 ptr += strlen(ptr);
5143
5144 if (!_ippFileReadToken(f, ptr, (sizeof(temp) - (size_t)(ptr - temp))))
5145 break;
5146
5147 if (!strcmp(ptr, ","))
5148 {
5149 /*
5150 * Append a value...
5151 */
5152
5153 ptr += strlen(ptr);
5154
5155 if (!_ippFileReadToken(f, ptr, (sizeof(temp) - (size_t)(ptr - temp))))
5156 break;
5157 }
5158 else
5159 {
5160 /*
5161 * Not another value, stop here...
5162 */
5163
5164 cupsFileSeek(f->fp, lastpos);
5165 f->linenum = lastline;
5166 *ptr = '\0';
5167 break;
5168 }
5169 }
5170
5171 if (data->last_expect)
5172 {
5173 /*
5174 * Expand any variables in the value and then save it.
5175 */
5176
5177 _ippVarsExpand(vars, value, temp, sizeof(value));
5178
5179 ptr = value + strlen(value) - 1;
5180
5181 if (value[0] == '/' && ptr > value && *ptr == '/')
5182 {
5183 /*
5184 * WITH-VALUE is a POSIX extended regular expression.
5185 */
5186
5187 data->last_expect->with_value = calloc(1, (size_t)(ptr - value));
5188 data->last_expect->with_flags |= IPPTOOL_WITH_REGEX;
5189
5190 if (data->last_expect->with_value)
5191 memcpy(data->last_expect->with_value, value + 1, (size_t)(ptr - value - 1));
5192 }
5193 else
5194 {
5195 /*
5196 * WITH-VALUE is a literal value...
5197 */
5198
5199 for (ptr = value; *ptr; ptr ++)
5200 {
5201 if (*ptr == '\\' && ptr[1])
5202 {
5203 /*
5204 * Remove \ from \foo...
5205 */
5206
5207 _cups_strcpy(ptr, ptr + 1);
5208 }
5209 }
5210
5211 data->last_expect->with_value = strdup(value);
5212 data->last_expect->with_flags |= IPPTOOL_WITH_LITERAL;
5213 }
5214 }
5215 else
5216 {
5217 print_fatal_error(data, "%s without a preceding EXPECT on line %d of \"%s\".", token, f->linenum, f->filename);
5218 return (0);
5219 }
5220 }
5221 else if (!_cups_strcasecmp(token, "WITH-VALUE-FROM"))
5222 {
5223 if (!_ippFileReadToken(f, temp, sizeof(temp)))
5224 {
5225 print_fatal_error(data, "Missing %s value on line %d of \"%s\".", token, f->linenum, f->filename);
5226 return (0);
5227 }
5228
5229 if (data->last_expect)
5230 {
5231 /*
5232 * Expand any variables in the value and then save it.
5233 */
5234
5235 _ippVarsExpand(vars, value, temp, sizeof(value));
5236
5237 data->last_expect->with_value_from = strdup(value);
5238 data->last_expect->with_flags = IPPTOOL_WITH_LITERAL;
5239 }
5240 else
5241 {
5242 print_fatal_error(data, "%s without a preceding EXPECT on line %d of \"%s\".", token, f->linenum, f->filename);
5243 return (0);
5244 }
5245 }
5246 else if (!_cups_strcasecmp(token, "DISPLAY"))
5247 {
5248 /*
5249 * Display attributes...
5250 */
5251
5252 if (data->num_displayed >= (int)(sizeof(data->displayed) / sizeof(data->displayed[0])))
5253 {
5254 print_fatal_error(data, "Too many DISPLAY's on line %d of \"%s\".", f->linenum, f->filename);
5255 return (0);
5256 }
5257
5258 if (!_ippFileReadToken(f, temp, sizeof(temp)))
5259 {
5260 print_fatal_error(data, "Missing DISPLAY name on line %d of \"%s\".", f->linenum, f->filename);
5261 return (0);
5262 }
5263
5264 data->displayed[data->num_displayed] = strdup(temp);
5265 data->num_displayed ++;
5266 }
5267 else
5268 {
5269 print_fatal_error(data, "Unexpected token %s seen on line %d of \"%s\".", token, f->linenum, f->filename);
5270 return (0);
5271 }
5272 }
5273 else
5274 {
5275 /*
5276 * Scan for the start of a test (open brace)...
5277 */
5278
5279 if (!strcmp(token, "{"))
5280 {
5281 /*
5282 * Start new test...
5283 */
5284
5285 if (data->show_header)
5286 {
5287 if (data->output == IPPTOOL_OUTPUT_PLIST)
5288 print_xml_header(data);
5289
5290 if (data->output == IPPTOOL_OUTPUT_TEST || (data->output == IPPTOOL_OUTPUT_PLIST && data->outfile != cupsFileStdout()))
5291 cupsFilePrintf(cupsFileStdout(), "\"%s\":\n", f->filename);
5292
5293 data->show_header = 0;
5294 }
5295
5296 data->compression[0] = '\0';
5297 data->delay = 0;
5298 data->num_expects = 0;
5299 data->last_expect = NULL;
5300 data->file[0] = '\0';
5301 data->ignore_errors = data->def_ignore_errors;
5302 strlcpy(data->name, f->filename, sizeof(data->name));
5303 if ((ptr = strrchr(data->name, '.')) != NULL)
5304 *ptr = '\0';
5305 data->repeat_interval = 5000000;
5306 strlcpy(data->resource, data->vars->resource, sizeof(data->resource));
5307 data->skip_previous = 0;
5308 data->pass_test = 0;
5309 data->skip_test = 0;
5310 data->num_statuses = 0;
5311 data->last_status = NULL;
5312 data->test_id[0] = '\0';
5313 data->transfer = data->def_transfer;
5314 data->version = data->def_version;
5315
5316 free(data->monitor_uri);
5317 data->monitor_uri = NULL;
5318 data->monitor_delay = 0;
5319 data->monitor_interval = 5000000;
5320 data->num_monitor_expects = 0;
5321
5322 _ippVarsSet(vars, "date-current", iso_date(ippTimeToDate(time(NULL))));
5323
5324 f->attrs = ippNew();
5325 f->group_tag = IPP_TAG_ZERO;
5326 }
5327 else if (!strcmp(token, "DEFINE"))
5328 {
5329 /*
5330 * DEFINE name value
5331 */
5332
5333 if (_ippFileReadToken(f, name, sizeof(name)) && _ippFileReadToken(f, temp, sizeof(temp)))
5334 {
5335 _ippVarsSet(vars, "date-current", iso_date(ippTimeToDate(time(NULL))));
5336 _ippVarsExpand(vars, value, temp, sizeof(value));
5337 _ippVarsSet(vars, name, value);
5338 }
5339 else
5340 {
5341 print_fatal_error(data, "Missing DEFINE name and/or value on line %d of \"%s\".", f->linenum, f->filename);
5342 return (0);
5343 }
5344 }
5345 else if (!strcmp(token, "DEFINE-DEFAULT"))
5346 {
5347 /*
5348 * DEFINE-DEFAULT name value
5349 */
5350
5351 if (_ippFileReadToken(f, name, sizeof(name)) && _ippFileReadToken(f, temp, sizeof(temp)))
5352 {
5353 if (!_ippVarsGet(vars, name))
5354 {
5355 _ippVarsSet(vars, "date-current", iso_date(ippTimeToDate(time(NULL))));
5356 _ippVarsExpand(vars, value, temp, sizeof(value));
5357 _ippVarsSet(vars, name, value);
5358 }
5359 }
5360 else
5361 {
5362 print_fatal_error(data, "Missing DEFINE-DEFAULT name and/or value on line %d of \"%s\".", f->linenum, f->filename);
5363 return (0);
5364 }
5365 }
5366 else if (!strcmp(token, "FILE-ID"))
5367 {
5368 /*
5369 * FILE-ID "string"
5370 */
5371
5372 if (_ippFileReadToken(f, temp, sizeof(temp)))
5373 {
5374 _ippVarsSet(vars, "date-current", iso_date(ippTimeToDate(time(NULL))));
5375 _ippVarsExpand(vars, data->file_id, temp, sizeof(data->file_id));
5376 }
5377 else
5378 {
5379 print_fatal_error(data, "Missing FILE-ID value on line %d of \"%s\".", f->linenum, f->filename);
5380 return (0);
5381 }
5382 }
5383 else if (!strcmp(token, "IGNORE-ERRORS"))
5384 {
5385 /*
5386 * IGNORE-ERRORS yes
5387 * IGNORE-ERRORS no
5388 */
5389
5390 if (_ippFileReadToken(f, temp, sizeof(temp)) && (!_cups_strcasecmp(temp, "yes") || !_cups_strcasecmp(temp, "no")))
5391 {
5392 data->def_ignore_errors = !_cups_strcasecmp(temp, "yes");
5393 }
5394 else
5395 {
5396 print_fatal_error(data, "Missing IGNORE-ERRORS value on line %d of \"%s\".", f->linenum, f->filename);
5397 return (0);
5398 }
5399 }
5400 else if (!strcmp(token, "INCLUDE"))
5401 {
5402 /*
5403 * INCLUDE "filename"
5404 * INCLUDE <filename>
5405 */
5406
5407 if (_ippFileReadToken(f, temp, sizeof(temp)))
5408 {
5409 /*
5410 * Map the filename to and then run the tests...
5411 */
5412
5413 ipptool_test_t inc_data; /* Data for included file */
5414 char filename[1024]; /* Mapped filename */
5415
5416 memcpy(&inc_data, data, sizeof(inc_data));
5417 inc_data.http = NULL;
5418 inc_data.pass = 1;
5419 inc_data.prev_pass = 1;
5420 inc_data.show_header = 1;
5421
5422 if (!do_tests(get_filename(f->filename, filename, temp, sizeof(filename)), &inc_data) && data->stop_after_include_error)
5423 {
5424 data->pass = data->prev_pass = 0;
5425 return (0);
5426 }
5427 }
5428 else
5429 {
5430 print_fatal_error(data, "Missing INCLUDE filename on line %d of \"%s\".", f->linenum, f->filename);
5431 return (0);
5432 }
5433
5434 data->show_header = 1;
5435 }
5436 else if (!strcmp(token, "INCLUDE-IF-DEFINED"))
5437 {
5438 /*
5439 * INCLUDE-IF-DEFINED name "filename"
5440 * INCLUDE-IF-DEFINED name <filename>
5441 */
5442
5443 if (_ippFileReadToken(f, name, sizeof(name)) && _ippFileReadToken(f, temp, sizeof(temp)))
5444 {
5445 /*
5446 * Map the filename to and then run the tests...
5447 */
5448
5449 ipptool_test_t inc_data; /* Data for included file */
5450 char filename[1024]; /* Mapped filename */
5451
5452 memcpy(&inc_data, data, sizeof(inc_data));
5453 inc_data.http = NULL;
5454 inc_data.pass = 1;
5455 inc_data.prev_pass = 1;
5456 inc_data.show_header = 1;
5457
5458 if (!do_tests(get_filename(f->filename, filename, temp, sizeof(filename)), &inc_data) && data->stop_after_include_error)
5459 {
5460 data->pass = data->prev_pass = 0;
5461 return (0);
5462 }
5463 }
5464 else
5465 {
5466 print_fatal_error(data, "Missing INCLUDE-IF-DEFINED name or filename on line %d of \"%s\".", f->linenum, f->filename);
5467 return (0);
5468 }
5469
5470 data->show_header = 1;
5471 }
5472 else if (!strcmp(token, "INCLUDE-IF-NOT-DEFINED"))
5473 {
5474 /*
5475 * INCLUDE-IF-NOT-DEFINED name "filename"
5476 * INCLUDE-IF-NOT-DEFINED name <filename>
5477 */
5478
5479 if (_ippFileReadToken(f, name, sizeof(name)) && _ippFileReadToken(f, temp, sizeof(temp)))
5480 {
5481 /*
5482 * Map the filename to and then run the tests...
5483 */
5484
5485 ipptool_test_t inc_data; /* Data for included file */
5486 char filename[1024]; /* Mapped filename */
5487
5488 memcpy(&inc_data, data, sizeof(inc_data));
5489 inc_data.http = NULL;
5490 inc_data.pass = 1;
5491 inc_data.prev_pass = 1;
5492 inc_data.show_header = 1;
5493
5494 if (!do_tests(get_filename(f->filename, filename, temp, sizeof(filename)), &inc_data) && data->stop_after_include_error)
5495 {
5496 data->pass = data->prev_pass = 0;
5497 return (0);
5498 }
5499 }
5500 else
5501 {
5502 print_fatal_error(data, "Missing INCLUDE-IF-NOT-DEFINED name or filename on line %d of \"%s\".", f->linenum, f->filename);
5503 return (0);
5504 }
5505
5506 data->show_header = 1;
5507 }
5508 else if (!strcmp(token, "SKIP-IF-DEFINED"))
5509 {
5510 /*
5511 * SKIP-IF-DEFINED variable
5512 */
5513
5514 if (_ippFileReadToken(f, name, sizeof(name)))
5515 {
5516 if (_ippVarsGet(vars, name))
5517 data->skip_test = 1;
5518 }
5519 else
5520 {
5521 print_fatal_error(data, "Missing SKIP-IF-DEFINED variable on line %d of \"%s\".", f->linenum, f->filename);
5522 return (0);
5523 }
5524 }
5525 else if (!strcmp(token, "SKIP-IF-NOT-DEFINED"))
5526 {
5527 /*
5528 * SKIP-IF-NOT-DEFINED variable
5529 */
5530
5531 if (_ippFileReadToken(f, name, sizeof(name)))
5532 {
5533 if (!_ippVarsGet(vars, name))
5534 data->skip_test = 1;
5535 }
5536 else
5537 {
5538 print_fatal_error(data, "Missing SKIP-IF-NOT-DEFINED variable on line %d of \"%s\".", f->linenum, f->filename);
5539 return (0);
5540 }
5541 }
5542 else if (!strcmp(token, "STOP-AFTER-INCLUDE-ERROR"))
5543 {
5544 /*
5545 * STOP-AFTER-INCLUDE-ERROR yes
5546 * STOP-AFTER-INCLUDE-ERROR no
5547 */
5548
5549 if (_ippFileReadToken(f, temp, sizeof(temp)) && (!_cups_strcasecmp(temp, "yes") || !_cups_strcasecmp(temp, "no")))
5550 {
5551 data->stop_after_include_error = !_cups_strcasecmp(temp, "yes");
5552 }
5553 else
5554 {
5555 print_fatal_error(data, "Missing STOP-AFTER-INCLUDE-ERROR value on line %d of \"%s\".", f->linenum, f->filename);
5556 return (0);
5557 }
5558 }
5559 else if (!strcmp(token, "TRANSFER"))
5560 {
5561 /*
5562 * TRANSFER auto
5563 * TRANSFER chunked
5564 * TRANSFER length
5565 */
5566
5567 if (_ippFileReadToken(f, temp, sizeof(temp)))
5568 {
5569 if (!strcmp(temp, "auto"))
5570 data->def_transfer = IPPTOOL_TRANSFER_AUTO;
5571 else if (!strcmp(temp, "chunked"))
5572 data->def_transfer = IPPTOOL_TRANSFER_CHUNKED;
5573 else if (!strcmp(temp, "length"))
5574 data->def_transfer = IPPTOOL_TRANSFER_LENGTH;
5575 else
5576 {
5577 print_fatal_error(data, "Bad TRANSFER value \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
5578 return (0);
5579 }
5580 }
5581 else
5582 {
5583 print_fatal_error(data, "Missing TRANSFER value on line %d of \"%s\".", f->linenum, f->filename);
5584 return (0);
5585 }
5586 }
5587 else if (!strcmp(token, "VERSION"))
5588 {
5589 if (_ippFileReadToken(f, temp, sizeof(temp)))
5590 {
5591 if (!strcmp(temp, "1.0"))
5592 data->def_version = 10;
5593 else if (!strcmp(temp, "1.1"))
5594 data->def_version = 11;
5595 else if (!strcmp(temp, "2.0"))
5596 data->def_version = 20;
5597 else if (!strcmp(temp, "2.1"))
5598 data->def_version = 21;
5599 else if (!strcmp(temp, "2.2"))
5600 data->def_version = 22;
5601 else
5602 {
5603 print_fatal_error(data, "Bad VERSION \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
5604 return (0);
5605 }
5606 }
5607 else
5608 {
5609 print_fatal_error(data, "Missing VERSION number on line %d of \"%s\".", f->linenum, f->filename);
5610 return (0);
5611 }
5612 }
5613 else
5614 {
5615 print_fatal_error(data, "Unexpected token %s seen on line %d of \"%s\".", token, f->linenum, f->filename);
5616 return (0);
5617 }
5618 }
5619
5620 return (1);
5621 }
5622
5623
5624 /*
5625 * 'usage()' - Show program usage.
5626 */
5627
5628 static void
usage(void)5629 usage(void)
5630 {
5631 _cupsLangPuts(stderr, _("Usage: ipptool [options] URI filename [ ... filenameN ]"));
5632 _cupsLangPuts(stderr, _("Options:"));
5633 _cupsLangPuts(stderr, _("--ippserver filename Produce ippserver attribute file"));
5634 _cupsLangPuts(stderr, _("--stop-after-include-error\n"
5635 " Stop tests after a failed INCLUDE"));
5636 _cupsLangPuts(stderr, _("--version Show version"));
5637 _cupsLangPuts(stderr, _("-4 Connect using IPv4"));
5638 _cupsLangPuts(stderr, _("-6 Connect using IPv6"));
5639 _cupsLangPuts(stderr, _("-C Send requests using chunking (default)"));
5640 _cupsLangPuts(stderr, _("-E Test with encryption using HTTP Upgrade to TLS"));
5641 _cupsLangPuts(stderr, _("-I Ignore errors"));
5642 _cupsLangPuts(stderr, _("-L Send requests using content-length"));
5643 _cupsLangPuts(stderr, _("-P filename.plist Produce XML plist to a file and test report to standard output"));
5644 _cupsLangPuts(stderr, _("-R Repeat tests on server-error-busy"));
5645 _cupsLangPuts(stderr, _("-S Test with encryption using HTTPS"));
5646 _cupsLangPuts(stderr, _("-T seconds Set the receive/send timeout in seconds"));
5647 _cupsLangPuts(stderr, _("-V version Set default IPP version"));
5648 _cupsLangPuts(stderr, _("-X Produce XML plist instead of plain text"));
5649 _cupsLangPuts(stderr, _("-c Produce CSV output"));
5650 _cupsLangPuts(stderr, _("-d name=value Set named variable to value"));
5651 _cupsLangPuts(stderr, _("-f filename Set default request filename"));
5652 _cupsLangPuts(stderr, _("-h Validate HTTP response headers"));
5653 _cupsLangPuts(stderr, _("-i seconds Repeat the last file with the given time interval"));
5654 _cupsLangPuts(stderr, _("-l Produce plain text output"));
5655 _cupsLangPuts(stderr, _("-n count Repeat the last file the given number of times"));
5656 _cupsLangPuts(stderr, _("-q Run silently"));
5657 _cupsLangPuts(stderr, _("-t Produce a test report"));
5658 _cupsLangPuts(stderr, _("-v Be verbose"));
5659
5660 exit(1);
5661 }
5662
5663
5664 /*
5665 * 'with_distinct_values()' - Verify that an attribute contains unique values.
5666 */
5667
5668 static int // O - 1 if distinct, 0 if duplicate
with_distinct_values(cups_array_t * errors,ipp_attribute_t * attr)5669 with_distinct_values(
5670 cups_array_t *errors, // I - Array of errors
5671 ipp_attribute_t *attr) // I - Attribute to test
5672 {
5673 int i, // Looping var
5674 count; // Number of values
5675 ipp_tag_t value_tag; // Value syntax
5676 const char *value; // Current value
5677 char buffer[8192]; // Temporary buffer
5678 cups_array_t *values; // Array of values as strings
5679
5680
5681 // If there is only 1 value, it must be distinct
5682 if ((count = ippGetCount(attr)) == 1)
5683 return (1);
5684
5685 // Only check integers, enums, rangeOfInteger, resolution, and nul-terminated
5686 // strings...
5687 switch (value_tag = ippGetValueTag(attr))
5688 {
5689 case IPP_TAG_INTEGER :
5690 case IPP_TAG_ENUM :
5691 case IPP_TAG_RANGE :
5692 case IPP_TAG_RESOLUTION :
5693 case IPP_TAG_KEYWORD :
5694 case IPP_TAG_URISCHEME :
5695 case IPP_TAG_CHARSET :
5696 case IPP_TAG_LANGUAGE :
5697 case IPP_TAG_MIMETYPE :
5698 case IPP_TAG_BEGIN_COLLECTION :
5699 break;
5700
5701 default :
5702 add_stringf(errors, "WITH-DISTINCT-VALUES %s not supported for 1setOf %s", ippGetName(attr), ippTagString(value_tag));
5703 return (0);
5704 }
5705
5706 // Collect values and determine they are all unique...
5707 values = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free);
5708
5709 for (i = 0; i < count; i ++)
5710 {
5711 switch (value_tag)
5712 {
5713 case IPP_TAG_INTEGER :
5714 case IPP_TAG_ENUM :
5715 snprintf(buffer, sizeof(buffer), "%d", ippGetInteger(attr, i));
5716 value = buffer;
5717 break;
5718 case IPP_TAG_RANGE :
5719 {
5720 int upper, lower = ippGetRange(attr, i, &upper);
5721 // Range values
5722
5723 snprintf(buffer, sizeof(buffer), "%d-%d", lower, upper);
5724 value = buffer;
5725 }
5726 break;
5727 case IPP_TAG_RESOLUTION :
5728 {
5729 ipp_res_t units; // Resolution units
5730 int yres, xres = ippGetResolution(attr, i, &yres, &units);
5731 // Resolution values
5732
5733 if (xres == yres)
5734 snprintf(buffer, sizeof(buffer), "%d%s", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
5735 else
5736 snprintf(buffer, sizeof(buffer), "%dx%d%s", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
5737 value = buffer;
5738 }
5739 break;
5740 case IPP_TAG_KEYWORD :
5741 case IPP_TAG_URISCHEME :
5742 case IPP_TAG_CHARSET :
5743 case IPP_TAG_LANGUAGE :
5744 case IPP_TAG_MIMETYPE :
5745 value = ippGetString(attr, i, NULL);
5746 break;
5747 case IPP_TAG_BEGIN_COLLECTION :
5748 {
5749 ipp_t *col = ippGetCollection(attr, i);
5750 // Collection value
5751 ipp_attribute_t *member; // Member attribute
5752 char *bufptr, // Pointer into buffer
5753 *bufend, // End of buffer
5754 prefix; // Prefix character
5755
5756 for (prefix = '{', bufptr = buffer, bufend = buffer + sizeof(buffer) - 2, member = ippFirstAttribute(col); member && bufptr < bufend; member = ippNextAttribute(col))
5757 {
5758 *bufptr++ = prefix;
5759 prefix = ' ';
5760
5761 ippAttributeString(member, bufptr, (size_t)(bufend - bufptr));
5762 bufptr += strlen(bufptr);
5763 }
5764
5765 *bufptr++ = '}';
5766 *bufptr = '\0';
5767 value = buffer;
5768 }
5769 break;
5770 default : // Should never happen
5771 value = "unsupported";
5772 break;
5773 }
5774
5775 if (cupsArrayFind(values, (void *)value))
5776 add_stringf(errors, "DUPLICATE: %s=%s", ippGetName(attr), value);
5777 else
5778 cupsArrayAdd(values, (void *)value);
5779 }
5780
5781 // Cleanup...
5782 i = cupsArrayCount(values) == count;
5783 cupsArrayDelete(values);
5784
5785 return (i);
5786 }
5787
5788
5789 /*
5790 * 'with_flags_string()' - Return the "WITH-xxx" predicate that corresponds to
5791 * the flags.
5792 */
5793
5794 static const char * /* O - WITH-xxx string */
with_flags_string(int flags)5795 with_flags_string(int flags) /* I - WITH flags */
5796 {
5797 if (flags & IPPTOOL_WITH_ALL)
5798 {
5799 if (flags & IPPTOOL_WITH_HOSTNAME)
5800 return ("WITH-ALL-HOSTNAMES");
5801 else if (flags & IPPTOOL_WITH_RESOURCE)
5802 return ("WITH-ALL-RESOURCES");
5803 else if (flags & IPPTOOL_WITH_SCHEME)
5804 return ("WITH-ALL-SCHEMES");
5805 else
5806 return ("WITH-ALL-VALUES");
5807 }
5808 else if (flags & IPPTOOL_WITH_HOSTNAME)
5809 return ("WITH-HOSTNAME");
5810 else if (flags & IPPTOOL_WITH_RESOURCE)
5811 return ("WITH-RESOURCE");
5812 else if (flags & IPPTOOL_WITH_SCHEME)
5813 return ("WITH-SCHEME");
5814 else
5815 return ("WITH-VALUE");
5816 }
5817
5818
5819 /*
5820 * 'with_value()' - Test a WITH-VALUE predicate.
5821 */
5822
5823 static int /* O - 1 on match, 0 on non-match */
with_value(ipptool_test_t * data,cups_array_t * errors,char * value,int flags,ipp_attribute_t * attr,char * matchbuf,size_t matchlen)5824 with_value(ipptool_test_t *data, /* I - Test data */
5825 cups_array_t *errors, /* I - Errors array */
5826 char *value, /* I - Value string */
5827 int flags, /* I - Flags for match */
5828 ipp_attribute_t *attr, /* I - Attribute to compare */
5829 char *matchbuf, /* I - Buffer to hold matching value */
5830 size_t matchlen) /* I - Length of match buffer */
5831 {
5832 int i, /* Looping var */
5833 count, /* Number of values */
5834 match; /* Match? */
5835 char temp[1024], /* Temporary value string */
5836 *valptr; /* Pointer into value */
5837 const char *name; /* Attribute name */
5838
5839
5840 *matchbuf = '\0';
5841 match = (flags & IPPTOOL_WITH_ALL) ? 1 : 0;
5842
5843 /*
5844 * NULL matches everything.
5845 */
5846
5847 if (!value || !*value)
5848 return (1);
5849
5850 /*
5851 * Compare the value string to the attribute value.
5852 */
5853
5854 name = ippGetName(attr);
5855 count = ippGetCount(attr);
5856
5857 switch (ippGetValueTag(attr))
5858 {
5859 case IPP_TAG_INTEGER :
5860 case IPP_TAG_ENUM :
5861 for (i = 0; i < count; i ++)
5862 {
5863 char op, /* Comparison operator */
5864 *nextptr; /* Next pointer */
5865 int intvalue, /* Integer value */
5866 attrvalue = ippGetInteger(attr, i),
5867 /* Attribute value */
5868 valmatch = 0; /* Does the current value match? */
5869
5870 valptr = value;
5871
5872 while (isspace(*valptr & 255) || isdigit(*valptr & 255) ||
5873 *valptr == '-' || *valptr == ',' || *valptr == '<' ||
5874 *valptr == '=' || *valptr == '>')
5875 {
5876 op = '=';
5877 while (*valptr && !isdigit(*valptr & 255) && *valptr != '-')
5878 {
5879 if (*valptr == '<' || *valptr == '>' || *valptr == '=')
5880 op = *valptr;
5881 valptr ++;
5882 }
5883
5884 if (!*valptr)
5885 break;
5886
5887 intvalue = (int)strtol(valptr, &nextptr, 0);
5888 if (nextptr == valptr)
5889 break;
5890 valptr = nextptr;
5891
5892 if ((op == '=' && attrvalue == intvalue) ||
5893 (op == '<' && attrvalue < intvalue) ||
5894 (op == '>' && attrvalue > intvalue))
5895 {
5896 if (!matchbuf[0])
5897 snprintf(matchbuf, matchlen, "%d", attrvalue);
5898
5899 valmatch = 1;
5900 break;
5901 }
5902 }
5903
5904 if (flags & IPPTOOL_WITH_ALL)
5905 {
5906 if (!valmatch)
5907 {
5908 match = 0;
5909 break;
5910 }
5911 }
5912 else if (valmatch)
5913 {
5914 match = 1;
5915 break;
5916 }
5917 }
5918
5919 if (!match && errors)
5920 {
5921 for (i = 0; i < count; i ++)
5922 add_stringf(data->errors, "GOT: %s=%d", name, ippGetInteger(attr, i));
5923 }
5924 break;
5925
5926 case IPP_TAG_RANGE :
5927 for (i = 0; i < count; i ++)
5928 {
5929 char op, /* Comparison operator */
5930 *nextptr; /* Next pointer */
5931 int intvalue, /* Integer value */
5932 lower, /* Lower range */
5933 upper, /* Upper range */
5934 valmatch = 0; /* Does the current value match? */
5935
5936 lower = ippGetRange(attr, i, &upper);
5937 valptr = value;
5938
5939 while (isspace(*valptr & 255) || isdigit(*valptr & 255) ||
5940 *valptr == '-' || *valptr == ',' || *valptr == '<' ||
5941 *valptr == '=' || *valptr == '>')
5942 {
5943 op = '=';
5944 while (*valptr && !isdigit(*valptr & 255) && *valptr != '-')
5945 {
5946 if (*valptr == '<' || *valptr == '>' || *valptr == '=')
5947 op = *valptr;
5948 valptr ++;
5949 }
5950
5951 if (!*valptr)
5952 break;
5953
5954 intvalue = (int)strtol(valptr, &nextptr, 0);
5955 if (nextptr == valptr)
5956 break;
5957 valptr = nextptr;
5958
5959 if ((op == '=' && (lower == intvalue || upper == intvalue)) ||
5960 (op == '<' && upper < intvalue) ||
5961 (op == '>' && upper > intvalue))
5962 {
5963 if (!matchbuf[0])
5964 snprintf(matchbuf, matchlen, "%d-%d", lower, upper);
5965
5966 valmatch = 1;
5967 break;
5968 }
5969 }
5970
5971 if (flags & IPPTOOL_WITH_ALL)
5972 {
5973 if (!valmatch)
5974 {
5975 match = 0;
5976 break;
5977 }
5978 }
5979 else if (valmatch)
5980 {
5981 match = 1;
5982 break;
5983 }
5984 }
5985
5986 if (!match && errors)
5987 {
5988 for (i = 0; i < count; i ++)
5989 {
5990 int lower, upper; /* Range values */
5991
5992 lower = ippGetRange(attr, i, &upper);
5993 add_stringf(data->errors, "GOT: %s=%d-%d", name, lower, upper);
5994 }
5995 }
5996 break;
5997
5998 case IPP_TAG_BOOLEAN :
5999 for (i = 0; i < count; i ++)
6000 {
6001 if ((!strcmp(value, "true") || !strcmp(value, "1")) == ippGetBoolean(attr, i))
6002 {
6003 if (!matchbuf[0])
6004 strlcpy(matchbuf, value, matchlen);
6005
6006 if (!(flags & IPPTOOL_WITH_ALL))
6007 {
6008 match = 1;
6009 break;
6010 }
6011 }
6012 else if (flags & IPPTOOL_WITH_ALL)
6013 {
6014 match = 0;
6015 break;
6016 }
6017 }
6018
6019 if (!match && errors)
6020 {
6021 for (i = 0; i < count; i ++)
6022 add_stringf(data->errors, "GOT: %s=%s", name, ippGetBoolean(attr, i) ? "true" : "false");
6023 }
6024 break;
6025
6026 case IPP_TAG_RESOLUTION :
6027 for (i = 0; i < count; i ++)
6028 {
6029 int xres, yres; /* Resolution values */
6030 ipp_res_t units; /* Resolution units */
6031
6032 xres = ippGetResolution(attr, i, &yres, &units);
6033 if (xres == yres)
6034 snprintf(temp, sizeof(temp), "%d%s", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
6035 else
6036 snprintf(temp, sizeof(temp), "%dx%d%s", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
6037
6038 if (!strcmp(value, temp))
6039 {
6040 if (!matchbuf[0])
6041 strlcpy(matchbuf, value, matchlen);
6042
6043 if (!(flags & IPPTOOL_WITH_ALL))
6044 {
6045 match = 1;
6046 break;
6047 }
6048 }
6049 else if (flags & IPPTOOL_WITH_ALL)
6050 {
6051 match = 0;
6052 break;
6053 }
6054 }
6055
6056 if (!match && errors)
6057 {
6058 for (i = 0; i < count; i ++)
6059 {
6060 int xres, yres; /* Resolution values */
6061 ipp_res_t units; /* Resolution units */
6062
6063 xres = ippGetResolution(attr, i, &yres, &units);
6064 if (xres == yres)
6065 snprintf(temp, sizeof(temp), "%d%s", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
6066 else
6067 snprintf(temp, sizeof(temp), "%dx%d%s", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
6068
6069 if (strcmp(value, temp))
6070 add_stringf(data->errors, "GOT: %s=%s", name, temp);
6071 }
6072 }
6073 break;
6074
6075 case IPP_TAG_NOVALUE :
6076 case IPP_TAG_UNKNOWN :
6077 return (1);
6078
6079 case IPP_TAG_CHARSET :
6080 case IPP_TAG_KEYWORD :
6081 case IPP_TAG_LANGUAGE :
6082 case IPP_TAG_MIMETYPE :
6083 case IPP_TAG_NAME :
6084 case IPP_TAG_NAMELANG :
6085 case IPP_TAG_TEXT :
6086 case IPP_TAG_TEXTLANG :
6087 case IPP_TAG_URI :
6088 case IPP_TAG_URISCHEME :
6089 if (flags & IPPTOOL_WITH_REGEX)
6090 {
6091 /*
6092 * Value is an extended, case-sensitive POSIX regular expression...
6093 */
6094
6095 regex_t re; /* Regular expression */
6096
6097 if ((i = regcomp(&re, value, REG_EXTENDED | REG_NOSUB)) != 0)
6098 {
6099 regerror(i, &re, temp, sizeof(temp));
6100
6101 print_fatal_error(data, "Unable to compile WITH-VALUE regular expression \"%s\" - %s", value, temp);
6102 return (0);
6103 }
6104
6105 /*
6106 * See if ALL of the values match the given regular expression.
6107 */
6108
6109 for (i = 0; i < count; i ++)
6110 {
6111 if (!regexec(&re, get_string(attr, i, flags, temp, sizeof(temp)),
6112 0, NULL, 0))
6113 {
6114 if (!matchbuf[0])
6115 strlcpy(matchbuf, get_string(attr, i, flags, temp, sizeof(temp)), matchlen);
6116
6117 if (!(flags & IPPTOOL_WITH_ALL))
6118 {
6119 match = 1;
6120 break;
6121 }
6122 }
6123 else if (flags & IPPTOOL_WITH_ALL)
6124 {
6125 match = 0;
6126 break;
6127 }
6128 }
6129
6130 regfree(&re);
6131 }
6132 else if (ippGetValueTag(attr) == IPP_TAG_URI && !(flags & (IPPTOOL_WITH_SCHEME | IPPTOOL_WITH_HOSTNAME | IPPTOOL_WITH_RESOURCE)))
6133 {
6134 /*
6135 * Value is a literal URI string, see if the value(s) match...
6136 */
6137
6138 for (i = 0; i < count; i ++)
6139 {
6140 if (!compare_uris(value, get_string(attr, i, flags, temp, sizeof(temp))))
6141 {
6142 if (!matchbuf[0])
6143 strlcpy(matchbuf, get_string(attr, i, flags, temp, sizeof(temp)), matchlen);
6144
6145 if (!(flags & IPPTOOL_WITH_ALL))
6146 {
6147 match = 1;
6148 break;
6149 }
6150 }
6151 else if (flags & IPPTOOL_WITH_ALL)
6152 {
6153 match = 0;
6154 break;
6155 }
6156 }
6157 }
6158 else
6159 {
6160 /*
6161 * Value is a literal string, see if the value(s) match...
6162 */
6163
6164 for (i = 0; i < count; i ++)
6165 {
6166 int result;
6167
6168 switch (ippGetValueTag(attr))
6169 {
6170 case IPP_TAG_URI :
6171 /*
6172 * Some URI components are case-sensitive, some not...
6173 */
6174
6175 if (flags & (IPPTOOL_WITH_SCHEME | IPPTOOL_WITH_HOSTNAME))
6176 result = _cups_strcasecmp(value, get_string(attr, i, flags, temp, sizeof(temp)));
6177 else
6178 result = strcmp(value, get_string(attr, i, flags, temp, sizeof(temp)));
6179 break;
6180
6181 case IPP_TAG_MIMETYPE :
6182 case IPP_TAG_NAME :
6183 case IPP_TAG_NAMELANG :
6184 case IPP_TAG_TEXT :
6185 case IPP_TAG_TEXTLANG :
6186 /*
6187 * mimeMediaType, nameWithoutLanguage, nameWithLanguage,
6188 * textWithoutLanguage, and textWithLanguage are defined to
6189 * be case-insensitive strings...
6190 */
6191
6192 result = _cups_strcasecmp(value, get_string(attr, i, flags, temp, sizeof(temp)));
6193 break;
6194
6195 default :
6196 /*
6197 * Other string syntaxes are defined as lowercased so we use
6198 * case-sensitive comparisons to catch problems...
6199 */
6200
6201 result = strcmp(value, get_string(attr, i, flags, temp, sizeof(temp)));
6202 break;
6203 }
6204
6205 if (!result)
6206 {
6207 if (!matchbuf[0])
6208 strlcpy(matchbuf, get_string(attr, i, flags, temp, sizeof(temp)), matchlen);
6209
6210 if (!(flags & IPPTOOL_WITH_ALL))
6211 {
6212 match = 1;
6213 break;
6214 }
6215 }
6216 else if (flags & IPPTOOL_WITH_ALL)
6217 {
6218 match = 0;
6219 break;
6220 }
6221 }
6222 }
6223
6224 if (!match && errors)
6225 {
6226 for (i = 0; i < count; i ++)
6227 add_stringf(data->errors, "GOT: %s=\"%s\"", name, ippGetString(attr, i, NULL));
6228 }
6229 break;
6230
6231 case IPP_TAG_STRING :
6232 if (flags & IPPTOOL_WITH_REGEX)
6233 {
6234 /*
6235 * Value is an extended, case-sensitive POSIX regular expression...
6236 */
6237
6238 void *adata; /* Pointer to octetString data */
6239 int adatalen; /* Length of octetString */
6240 regex_t re; /* Regular expression */
6241
6242 if ((i = regcomp(&re, value, REG_EXTENDED | REG_NOSUB)) != 0)
6243 {
6244 regerror(i, &re, temp, sizeof(temp));
6245
6246 print_fatal_error(data, "Unable to compile WITH-VALUE regular expression \"%s\" - %s", value, temp);
6247 return (0);
6248 }
6249
6250 /*
6251 * See if ALL of the values match the given regular expression.
6252 */
6253
6254 for (i = 0; i < count; i ++)
6255 {
6256 if ((adata = ippGetOctetString(attr, i, &adatalen)) == NULL || adatalen >= (int)sizeof(temp))
6257 {
6258 match = 0;
6259 break;
6260 }
6261 memcpy(temp, adata, (size_t)adatalen);
6262 temp[adatalen] = '\0';
6263
6264 if (!regexec(&re, temp, 0, NULL, 0))
6265 {
6266 if (!matchbuf[0])
6267 strlcpy(matchbuf, temp, matchlen);
6268
6269 if (!(flags & IPPTOOL_WITH_ALL))
6270 {
6271 match = 1;
6272 break;
6273 }
6274 }
6275 else if (flags & IPPTOOL_WITH_ALL)
6276 {
6277 match = 0;
6278 break;
6279 }
6280 }
6281
6282 regfree(&re);
6283
6284 if (!match && errors)
6285 {
6286 for (i = 0; i < count; i ++)
6287 {
6288 adata = ippGetOctetString(attr, i, &adatalen);
6289 copy_hex_string(temp, adata, adatalen, sizeof(temp));
6290 add_stringf(data->errors, "GOT: %s=\"%s\"", name, temp);
6291 }
6292 }
6293 }
6294 else
6295 {
6296 /*
6297 * Value is a literal or hex-encoded string...
6298 */
6299
6300 unsigned char withdata[1023], /* WITH-VALUE data */
6301 *adata; /* Pointer to octetString data */
6302 int withlen, /* Length of WITH-VALUE data */
6303 adatalen; /* Length of octetString */
6304
6305 if (*value == '<')
6306 {
6307 /*
6308 * Grab hex-encoded value...
6309 */
6310
6311 if ((withlen = (int)strlen(value)) & 1 || withlen > (int)(2 * (sizeof(withdata) + 1)))
6312 {
6313 print_fatal_error(data, "Bad WITH-VALUE hex value.");
6314 return (0);
6315 }
6316
6317 withlen = withlen / 2 - 1;
6318
6319 for (valptr = value + 1, adata = withdata; *valptr; valptr += 2)
6320 {
6321 int ch; /* Current character/byte */
6322
6323 if (isdigit(valptr[0]))
6324 ch = (valptr[0] - '0') << 4;
6325 else if (isalpha(valptr[0]))
6326 ch = (tolower(valptr[0]) - 'a' + 10) << 4;
6327 else
6328 break;
6329
6330 if (isdigit(valptr[1]))
6331 ch |= valptr[1] - '0';
6332 else if (isalpha(valptr[1]))
6333 ch |= tolower(valptr[1]) - 'a' + 10;
6334 else
6335 break;
6336
6337 *adata++ = (unsigned char)ch;
6338 }
6339
6340 if (*valptr)
6341 {
6342 print_fatal_error(data, "Bad WITH-VALUE hex value.");
6343 return (0);
6344 }
6345 }
6346 else
6347 {
6348 /*
6349 * Copy literal string value...
6350 */
6351
6352 withlen = (int)strlen(value);
6353
6354 memcpy(withdata, value, (size_t)withlen);
6355 }
6356
6357 for (i = 0; i < count; i ++)
6358 {
6359 adata = ippGetOctetString(attr, i, &adatalen);
6360
6361 if (withlen == adatalen && !memcmp(withdata, adata, (size_t)withlen))
6362 {
6363 if (!matchbuf[0])
6364 copy_hex_string(matchbuf, adata, adatalen, matchlen);
6365
6366 if (!(flags & IPPTOOL_WITH_ALL))
6367 {
6368 match = 1;
6369 break;
6370 }
6371 }
6372 else if (flags & IPPTOOL_WITH_ALL)
6373 {
6374 match = 0;
6375 break;
6376 }
6377 }
6378
6379 if (!match && errors)
6380 {
6381 for (i = 0; i < count; i ++)
6382 {
6383 adata = ippGetOctetString(attr, i, &adatalen);
6384 copy_hex_string(temp, adata, adatalen, sizeof(temp));
6385 add_stringf(data->errors, "GOT: %s=\"%s\"", name, temp);
6386 }
6387 }
6388 }
6389 break;
6390
6391 default :
6392 break;
6393 }
6394
6395 return (match);
6396 }
6397
6398
6399 /*
6400 * 'with_value_from()' - Test a WITH-VALUE-FROM predicate.
6401 */
6402
6403 static int /* O - 1 on match, 0 on non-match */
with_value_from(cups_array_t * errors,ipp_attribute_t * fromattr,ipp_attribute_t * attr,char * matchbuf,size_t matchlen)6404 with_value_from(
6405 cups_array_t *errors, /* I - Errors array */
6406 ipp_attribute_t *fromattr, /* I - "From" attribute */
6407 ipp_attribute_t *attr, /* I - Attribute to compare */
6408 char *matchbuf, /* I - Buffer to hold matching value */
6409 size_t matchlen) /* I - Length of match buffer */
6410 {
6411 int i, j, /* Looping vars */
6412 count = ippGetCount(attr), /* Number of attribute values */
6413 match = 1; /* Match? */
6414
6415
6416 *matchbuf = '\0';
6417
6418 /*
6419 * Compare the from value(s) to the attribute value(s)...
6420 */
6421
6422 switch (ippGetValueTag(attr))
6423 {
6424 case IPP_TAG_INTEGER :
6425 if (ippGetValueTag(fromattr) != IPP_TAG_INTEGER && ippGetValueTag(fromattr) != IPP_TAG_RANGE)
6426 goto wrong_value_tag;
6427
6428 for (i = 0; i < count; i ++)
6429 {
6430 int value = ippGetInteger(attr, i);
6431 /* Current integer value */
6432
6433 if (ippContainsInteger(fromattr, value))
6434 {
6435 if (!matchbuf[0])
6436 snprintf(matchbuf, matchlen, "%d", value);
6437 }
6438 else
6439 {
6440 add_stringf(errors, "GOT: %s=%d", ippGetName(attr), value);
6441 match = 0;
6442 }
6443 }
6444 break;
6445
6446 case IPP_TAG_ENUM :
6447 if (ippGetValueTag(fromattr) != IPP_TAG_ENUM)
6448 goto wrong_value_tag;
6449
6450 for (i = 0; i < count; i ++)
6451 {
6452 int value = ippGetInteger(attr, i);
6453 /* Current integer value */
6454
6455 if (ippContainsInteger(fromattr, value))
6456 {
6457 if (!matchbuf[0])
6458 snprintf(matchbuf, matchlen, "%d", value);
6459 }
6460 else
6461 {
6462 add_stringf(errors, "GOT: %s=%d", ippGetName(attr), value);
6463 match = 0;
6464 }
6465 }
6466 break;
6467
6468 case IPP_TAG_RESOLUTION :
6469 if (ippGetValueTag(fromattr) != IPP_TAG_RESOLUTION)
6470 goto wrong_value_tag;
6471
6472 for (i = 0; i < count; i ++)
6473 {
6474 int xres, yres;
6475 ipp_res_t units;
6476 int fromcount = ippGetCount(fromattr);
6477 int fromxres, fromyres;
6478 ipp_res_t fromunits;
6479
6480 xres = ippGetResolution(attr, i, &yres, &units);
6481
6482 for (j = 0; j < fromcount; j ++)
6483 {
6484 fromxres = ippGetResolution(fromattr, j, &fromyres, &fromunits);
6485 if (fromxres == xres && fromyres == yres && fromunits == units)
6486 break;
6487 }
6488
6489 if (j < fromcount)
6490 {
6491 if (!matchbuf[0])
6492 {
6493 if (xres == yres)
6494 snprintf(matchbuf, matchlen, "%d%s", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
6495 else
6496 snprintf(matchbuf, matchlen, "%dx%d%s", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
6497 }
6498 }
6499 else
6500 {
6501 if (xres == yres)
6502 add_stringf(errors, "GOT: %s=%d%s", ippGetName(attr), xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
6503 else
6504 add_stringf(errors, "GOT: %s=%dx%d%s", ippGetName(attr), xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
6505
6506 match = 0;
6507 }
6508 }
6509 break;
6510
6511 case IPP_TAG_NOVALUE :
6512 case IPP_TAG_UNKNOWN :
6513 return (1);
6514
6515 case IPP_TAG_CHARSET :
6516 case IPP_TAG_KEYWORD :
6517 case IPP_TAG_LANGUAGE :
6518 case IPP_TAG_MIMETYPE :
6519 case IPP_TAG_NAME :
6520 case IPP_TAG_NAMELANG :
6521 case IPP_TAG_TEXT :
6522 case IPP_TAG_TEXTLANG :
6523 case IPP_TAG_URISCHEME :
6524 for (i = 0; i < count; i ++)
6525 {
6526 const char *value = ippGetString(attr, i, NULL);
6527 /* Current string value */
6528
6529 if (ippContainsString(fromattr, value))
6530 {
6531 if (!matchbuf[0])
6532 strlcpy(matchbuf, value, matchlen);
6533 }
6534 else
6535 {
6536 add_stringf(errors, "GOT: %s='%s'", ippGetName(attr), value);
6537 match = 0;
6538 }
6539 }
6540 break;
6541
6542 case IPP_TAG_URI :
6543 for (i = 0; i < count; i ++)
6544 {
6545 const char *value = ippGetString(attr, i, NULL);
6546 /* Current string value */
6547 int fromcount = ippGetCount(fromattr);
6548
6549 for (j = 0; j < fromcount; j ++)
6550 {
6551 if (!compare_uris(value, ippGetString(fromattr, j, NULL)))
6552 {
6553 if (!matchbuf[0])
6554 strlcpy(matchbuf, value, matchlen);
6555 break;
6556 }
6557 }
6558
6559 if (j >= fromcount)
6560 {
6561 add_stringf(errors, "GOT: %s='%s'", ippGetName(attr), value);
6562 match = 0;
6563 }
6564 }
6565 break;
6566
6567 default :
6568 match = 0;
6569 break;
6570 }
6571
6572 return (match);
6573
6574 /* value tag mismatch between fromattr and attr */
6575 wrong_value_tag :
6576
6577 add_stringf(errors, "GOT: %s OF-TYPE %s", ippGetName(attr), ippTagString(ippGetValueTag(attr)));
6578
6579 return (0);
6580 }
6581