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