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