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