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