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