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