]> git.ipfire.org Git - thirdparty/cups.git/blob - test/ipptool.c
Finish re-tooling of ipptool to use new parser.
[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 #include <cups/cups-private.h>
16 #include <cups/file-private.h>
17 #include <regex.h>
18 #include <sys/stat.h>
19 #ifdef WIN32
20 # include <windows.h>
21 # ifndef R_OK
22 # define R_OK 0
23 # endif /* !R_OK */
24 #else
25 # include <signal.h>
26 # include <termios.h>
27 #endif /* WIN32 */
28 #ifndef O_BINARY
29 # define O_BINARY 0
30 #endif /* !O_BINARY */
31
32
33 /*
34 * Types...
35 */
36
37 typedef enum _cups_transfer_e /**** How to send request data ****/
38 {
39 _CUPS_TRANSFER_AUTO, /* Chunk for files, length for static */
40 _CUPS_TRANSFER_CHUNKED, /* Chunk always */
41 _CUPS_TRANSFER_LENGTH /* Length always */
42 } _cups_transfer_t;
43
44 typedef enum _cups_output_e /**** Output mode ****/
45 {
46 _CUPS_OUTPUT_QUIET, /* No output */
47 _CUPS_OUTPUT_TEST, /* Traditional CUPS test output */
48 _CUPS_OUTPUT_PLIST, /* XML plist test output */
49 _CUPS_OUTPUT_IPPSERVER, /* ippserver attribute file output */
50 _CUPS_OUTPUT_LIST, /* Tabular list output */
51 _CUPS_OUTPUT_CSV /* Comma-separated values output */
52 } _cups_output_t;
53
54 typedef enum _cups_with_e /**** WITH flags ****/
55 {
56 _CUPS_WITH_LITERAL = 0, /* Match string is a literal value */
57 _CUPS_WITH_ALL = 1, /* Must match all values */
58 _CUPS_WITH_REGEX = 2, /* Match string is a regular expression */
59 _CUPS_WITH_HOSTNAME = 4, /* Match string is a URI hostname */
60 _CUPS_WITH_RESOURCE = 8, /* Match string is a URI resource */
61 _CUPS_WITH_SCHEME = 16 /* Match string is a URI scheme */
62 } _cups_with_t;
63
64 typedef struct _cups_expect_s /**** Expected attribute info ****/
65 {
66 int optional, /* Optional attribute? */
67 not_expect, /* Don't expect attribute? */
68 expect_all; /* Expect all attributes to match/not match */
69 char *name, /* Attribute name */
70 *of_type, /* Type name */
71 *same_count_as, /* Parallel attribute name */
72 *if_defined, /* Only required if variable defined */
73 *if_not_defined, /* Only required if variable is not defined */
74 *with_value, /* Attribute must include this value */
75 *with_value_from, /* Attribute must have one of the values in this attribute */
76 *define_match, /* Variable to define on match */
77 *define_no_match, /* Variable to define on no-match */
78 *define_value; /* Variable to define with value */
79 int repeat_limit, /* Maximum number of times to repeat */
80 repeat_match, /* Repeat test on match */
81 repeat_no_match, /* Repeat test on no match */
82 with_flags, /* WITH flags */
83 count; /* Expected count if > 0 */
84 ipp_tag_t in_group; /* IN-GROUP value */
85 } _cups_expect_t;
86
87 typedef struct _cups_status_s /**** Status info ****/
88 {
89 ipp_status_t status; /* Expected status code */
90 char *if_defined, /* Only if variable is defined */
91 *if_not_defined, /* Only if variable is not defined */
92 *define_match, /* Variable to define on match */
93 *define_no_match, /* Variable to define on no-match */
94 *define_value; /* Variable to define with value */
95 int repeat_limit, /* Maximum number of times to repeat */
96 repeat_match, /* Repeat the test when it does not match */
97 repeat_no_match; /* Repeat the test when it matches */
98 } _cups_status_t;
99
100 typedef struct _cups_testdata_s /**** Test Data ****/
101 {
102 /* Global Options */
103 http_encryption_t encryption; /* Encryption for connection */
104 int family; /* Address family */
105 _cups_output_t output; /* Output mode */
106 int stop_after_include_error;
107 /* Stop after include errors? */
108 double timeout; /* Timeout for connection */
109 int validate_headers, /* Validate HTTP headers in response? */
110 verbosity; /* Show all attributes? */
111
112 /* Test Defaults */
113 int def_ignore_errors; /* Default IGNORE-ERRORS value */
114 _cups_transfer_t def_transfer; /* Default TRANSFER value */
115 int def_version; /* Default IPP version */
116
117 /* Global State */
118 http_t *http; /* HTTP connection to printer/server */
119 cups_file_t *outfile; /* Output file */
120 int show_header, /* Show the test header? */
121 xml_header; /* 1 if XML plist header was written */
122 int pass, /* Have we passed all tests? */
123 test_count, /* Number of tests (total) */
124 pass_count, /* Number of tests that passed */
125 fail_count, /* Number of tests that failed */
126 skip_count; /* Number of tests that were skipped */
127
128 /* Per-Test State */
129 cups_array_t *errors; /* Errors array */
130 int prev_pass, /* Result of previous test */
131 skip_previous; /* Skip on previous test failure? */
132 char compression[16]; /* COMPRESSION value */
133 useconds_t delay; /* Initial delay */
134 int num_displayed; /* Number of displayed attributes */
135 char *displayed[200]; /* Displayed attributes */
136 int num_expects; /* Number of expected attributes */
137 _cups_expect_t expects[200], /* Expected attributes */
138 *expect, /* Current expected attribute */
139 *last_expect; /* Last EXPECT (for predicates) */
140 char file[1024], /* Data filename */
141 file_id[1024]; /* File identifier */
142 int ignore_errors; /* Ignore test failures? */
143 char name[1024]; /* Test name */
144 useconds_t repeat_interval; /* Repeat interval (delay) */
145 int request_id; /* Current request ID */
146 char resource[512]; /* Resource for request */
147 int skip_test, /* Skip this test? */
148 num_statuses; /* Number of valid status codes */
149 _cups_status_t statuses[100], /* Valid status codes */
150 *last_status; /* Last STATUS (for predicates) */
151 char test_id[1024]; /* Test identifier */
152 _cups_transfer_t transfer; /* To chunk or not to chunk */
153 int version; /* IPP version number to use */
154 } _cups_testdata_t;
155
156
157 /*
158 * Globals...
159 */
160
161 static int Cancel = 0; /* Cancel test? */
162
163
164 /*
165 * Local functions...
166 */
167
168 static void add_stringf(cups_array_t *a, const char *s, ...) __attribute__ ((__format__ (__printf__, 2, 3)));
169 static int compare_uris(const char *a, const char *b);
170 static int do_test(_ipp_file_t *f, _ipp_vars_t *vars, _cups_testdata_t *data);
171 static int do_tests(const char *testfile, _ipp_vars_t *vars, _cups_testdata_t *data);
172 static int error_cb(_ipp_file_t *f, _cups_testdata_t *data, const char *error);
173 static int expect_matches(_cups_expect_t *expect, ipp_tag_t value_tag);
174 static char *get_filename(const char *testfile, char *dst, const char *src, size_t dstsize);
175 static const char *get_string(ipp_attribute_t *attr, int element, int flags, char *buffer, size_t bufsize);
176 static void init_data(_cups_testdata_t *data);
177 static char *iso_date(const ipp_uchar_t *date);
178 static void pause_message(const char *message);
179 static void print_attr(cups_file_t *outfile, int output, ipp_attribute_t *attr, ipp_tag_t *group);
180 static void print_csv(_cups_testdata_t *data, ipp_attribute_t *attr, int num_displayed, char **displayed, size_t *widths);
181 static void print_fatal_error(_cups_testdata_t *data, const char *s, ...) __attribute__ ((__format__ (__printf__, 2, 3)));
182 static void print_ippserver_attr(_cups_testdata_t *data, ipp_attribute_t *attr, int indent);
183 static void print_ippserver_string(_cups_testdata_t *data, const char *s, size_t len);
184 static void print_line(_cups_testdata_t *data, ipp_attribute_t *attr, int num_displayed, char **displayed, size_t *widths);
185 static void print_xml_header(_cups_testdata_t *data);
186 static void print_xml_string(cups_file_t *outfile, const char *element, const char *s);
187 static void print_xml_trailer(_cups_testdata_t *data, int success, const char *message);
188 #ifndef WIN32
189 static void sigterm_handler(int sig);
190 #endif /* WIN32 */
191 static int timeout_cb(http_t *http, void *user_data);
192 static int token_cb(_ipp_file_t *f, _ipp_vars_t *vars, _cups_testdata_t *data, const char *token);
193 static void usage(void) __attribute__((noreturn));
194 static int validate_attr(_cups_testdata_t *data, cups_array_t *errors, ipp_attribute_t *attr);
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 (request->attrs)
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", attrptr->values[0].integer);
1124 _ippVarsSet(vars, "job-id", temp);
1125 }
1126
1127 if ((attrptr = ippFindAttribute(response, "job-uri",
1128 IPP_TAG_URI)) != NULL)
1129 _ippVarsSet(vars, "job-uri", attrptr->values[0].string.text);
1130
1131 if ((attrptr = ippFindAttribute(response, "notify-subscription-id",
1132 IPP_TAG_INTEGER)) != NULL)
1133 {
1134 snprintf(temp, sizeof(temp), "%d", attrptr->values[0].integer);
1135 _ippVarsSet(vars, "notify-subscription-id", temp);
1136 }
1137
1138 /*
1139 * Check response, validating groups and attributes and logging errors
1140 * as needed...
1141 */
1142
1143 if (ippGetState(response) != IPP_DATA)
1144 add_stringf(data->errors, "Missing end-of-attributes-tag in response (RFC 2910 section 3.5.1)");
1145
1146 if (data->version)
1147 {
1148 int major, minor; /* IPP version */
1149
1150 major = ippGetVersion(response, &minor);
1151
1152 if (major != (data->version / 10) || minor != (data->version % 10))
1153 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);
1154 }
1155
1156 if (ippGetRequestId(response) != data->request_id)
1157 add_stringf(data->errors, "Bad request ID %d in response - expected %d (RFC 2911 section 3.1.1)", ippGetRequestId(response), data->request_id);
1158
1159 attrptr = ippFirstAttribute(response);
1160 if (!attrptr)
1161 {
1162 add_stringf(data->errors, "Missing first attribute \"attributes-charset (charset)\" in group operation-attributes-tag (RFC 2911 section 3.1.4).");
1163 }
1164 else
1165 {
1166 if (!ippGetName(attrptr) || ippGetValueTag(attrptr) != IPP_TAG_CHARSET || ippGetGroupTag(attrptr) != IPP_TAG_OPERATION || ippGetCount(attrptr) != 1 ||strcmp(ippGetName(attrptr), "attributes-charset"))
1167 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)));
1168
1169 attrptr = ippNextAttribute(response);
1170 if (!attrptr)
1171 add_stringf(data->errors, "Missing second attribute \"attributes-natural-language (naturalLanguage)\" in group operation-attributes-tag (RFC 2911 section 3.1.4).");
1172 else if (!ippGetName(attrptr) || ippGetValueTag(attrptr) != IPP_TAG_LANGUAGE || ippGetGroupTag(attrptr) != IPP_TAG_OPERATION || ippGetCount(attrptr) != 1 || strcmp(ippGetName(attrptr), "attributes-natural-language"))
1173 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)));
1174 }
1175
1176 if ((attrptr = ippFindAttribute(response, "status-message", IPP_TAG_ZERO)) != NULL)
1177 {
1178 if (attrptr->value_tag != IPP_TAG_TEXT)
1179 add_stringf(data->errors, "status-message (text(255)) has wrong value tag %s (RFC 2911 section 3.1.6.2).", ippTagString(attrptr->value_tag));
1180 if (attrptr->group_tag != IPP_TAG_OPERATION)
1181 add_stringf(data->errors, "status-message (text(255)) has wrong group tag %s (RFC 2911 section 3.1.6.2).", ippTagString(attrptr->group_tag));
1182 if (attrptr->num_values != 1)
1183 add_stringf(data->errors, "status-message (text(255)) has %d values (RFC 2911 section 3.1.6.2).", attrptr->num_values);
1184 if (attrptr->value_tag == IPP_TAG_TEXT && strlen(attrptr->values[0].string.text) > 255)
1185 add_stringf(data->errors, "status-message (text(255)) has bad length %d (RFC 2911 section 3.1.6.2).", (int)strlen(attrptr->values[0].string.text));
1186 }
1187
1188 if ((attrptr = ippFindAttribute(response, "detailed-status-message",
1189 IPP_TAG_ZERO)) != NULL)
1190 {
1191 if (attrptr->value_tag != IPP_TAG_TEXT)
1192 add_stringf(data->errors,
1193 "detailed-status-message (text(MAX)) has wrong "
1194 "value tag %s (RFC 2911 section 3.1.6.3).",
1195 ippTagString(attrptr->value_tag));
1196 if (attrptr->group_tag != IPP_TAG_OPERATION)
1197 add_stringf(data->errors,
1198 "detailed-status-message (text(MAX)) has wrong "
1199 "group tag %s (RFC 2911 section 3.1.6.3).",
1200 ippTagString(attrptr->group_tag));
1201 if (attrptr->num_values != 1)
1202 add_stringf(data->errors,
1203 "detailed-status-message (text(MAX)) has %d values"
1204 " (RFC 2911 section 3.1.6.3).",
1205 attrptr->num_values);
1206 if (attrptr->value_tag == IPP_TAG_TEXT &&
1207 strlen(attrptr->values[0].string.text) > 1023)
1208 add_stringf(data->errors,
1209 "detailed-status-message (text(MAX)) has bad "
1210 "length %d (RFC 2911 section 3.1.6.3).",
1211 (int)strlen(attrptr->values[0].string.text));
1212 }
1213
1214 a = cupsArrayNew((cups_array_func_t)strcmp, NULL);
1215
1216 for (attrptr = response->attrs,
1217 group = attrptr ? attrptr->group_tag : IPP_TAG_ZERO;
1218 attrptr;
1219 attrptr = attrptr->next)
1220 {
1221 if (attrptr->group_tag != group)
1222 {
1223 int out_of_order = 0; /* Are attribute groups out-of-order? */
1224 cupsArrayClear(a);
1225
1226 switch (attrptr->group_tag)
1227 {
1228 case IPP_TAG_ZERO :
1229 break;
1230
1231 case IPP_TAG_OPERATION :
1232 out_of_order = 1;
1233 break;
1234
1235 case IPP_TAG_UNSUPPORTED_GROUP :
1236 if (group != IPP_TAG_OPERATION)
1237 out_of_order = 1;
1238 break;
1239
1240 case IPP_TAG_JOB :
1241 case IPP_TAG_PRINTER :
1242 if (group != IPP_TAG_OPERATION &&
1243 group != IPP_TAG_UNSUPPORTED_GROUP)
1244 out_of_order = 1;
1245 break;
1246
1247 case IPP_TAG_SUBSCRIPTION :
1248 if (group > attrptr->group_tag &&
1249 group != IPP_TAG_DOCUMENT)
1250 out_of_order = 1;
1251 break;
1252
1253 default :
1254 if (group > attrptr->group_tag)
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(attrptr->group_tag),
1262 ippTagString(group));
1263
1264 if (attrptr->group_tag != IPP_TAG_ZERO)
1265 group = attrptr->group_tag;
1266 }
1267
1268 validate_attr(data, data->errors, attrptr);
1269
1270 if (attrptr->name)
1271 {
1272 if (cupsArrayFind(a, attrptr->name))
1273 add_stringf(data->errors, "Duplicate \"%s\" attribute in %s group",
1274 attrptr->name, ippTagString(group));
1275
1276 cupsArrayAdd(a, attrptr->name);
1277 }
1278 }
1279
1280 cupsArrayDelete(a);
1281
1282 /*
1283 * Now check the test-defined expected status-code and attribute
1284 * values...
1285 */
1286
1287 for (i = 0, status_ok = 0; i < data->num_statuses; i ++)
1288 {
1289 if (data->statuses[i].if_defined &&
1290 !_ippVarsGet(vars, data->statuses[i].if_defined))
1291 continue;
1292
1293 if (data->statuses[i].if_not_defined &&
1294 _ippVarsGet(vars, data->statuses[i].if_not_defined))
1295 continue;
1296
1297 if (ippGetStatusCode(response) == data->statuses[i].status)
1298 {
1299 status_ok = 1;
1300
1301 if (data->statuses[i].repeat_match && repeat_count < data->statuses[i].repeat_limit)
1302 repeat_test = 1;
1303
1304 if (data->statuses[i].define_match)
1305 _ippVarsSet(vars, data->statuses[i].define_match, "1");
1306 }
1307 else
1308 {
1309 if (data->statuses[i].repeat_no_match && repeat_count < data->statuses[i].repeat_limit)
1310 repeat_test = 1;
1311
1312 if (data->statuses[i].define_no_match)
1313 {
1314 _ippVarsSet(vars, data->statuses[i].define_no_match, "1");
1315 status_ok = 1;
1316 }
1317 }
1318 }
1319
1320 if (!status_ok && data->num_statuses > 0)
1321 {
1322 for (i = 0; i < data->num_statuses; i ++)
1323 {
1324 if (data->statuses[i].if_defined &&
1325 !_ippVarsGet(vars, data->statuses[i].if_defined))
1326 continue;
1327
1328 if (data->statuses[i].if_not_defined &&
1329 _ippVarsGet(vars, data->statuses[i].if_not_defined))
1330 continue;
1331
1332 if (!data->statuses[i].repeat_match || repeat_count >= data->statuses[i].repeat_limit)
1333 add_stringf(data->errors, "EXPECTED: STATUS %s (got %s)",
1334 ippErrorString(data->statuses[i].status),
1335 ippErrorString(cupsLastError()));
1336 }
1337
1338 if ((attrptr = ippFindAttribute(response, "status-message",
1339 IPP_TAG_TEXT)) != NULL)
1340 add_stringf(data->errors, "status-message=\"%s\"",
1341 attrptr->values[0].string.text);
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, found->value_tag)) ||
1360 (found && expect->in_group &&
1361 found->group_tag != 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, found->value_tag))
1374 add_stringf(data->errors, "EXPECTED: %s OF-TYPE %s (got %s)",
1375 expect->name, expect->of_type,
1376 ippTagString(found->value_tag));
1377
1378 if (expect->in_group && found->group_tag != expect->in_group)
1379 add_stringf(data->errors, "EXPECTED: %s IN-GROUP %s (got %s).",
1380 expect->name, ippTagString(expect->in_group),
1381 ippTagString(found->group_tag));
1382 }
1383 }
1384
1385 if (expect->repeat_no_match && repeat_count < expect->repeat_limit)
1386 repeat_test = 1;
1387
1388 break;
1389 }
1390
1391 if (found)
1392 ippAttributeString(found, buffer, sizeof(buffer));
1393
1394 if (found && expect->with_value_from && !with_value_from(NULL, ippFindAttribute(response, expect->with_value_from, IPP_TAG_ZERO), found, buffer, sizeof(buffer)))
1395 {
1396 if (expect->define_no_match)
1397 _ippVarsSet(vars, expect->define_no_match, "1");
1398 else if (!expect->define_match && !expect->define_value && ((!expect->repeat_match && !expect->repeat_no_match) || repeat_count >= expect->repeat_limit))
1399 {
1400 add_stringf(data->errors, "EXPECTED: %s WITH-VALUES-FROM %s", expect->name, expect->with_value_from);
1401
1402 with_value_from(data->errors, ippFindAttribute(response, expect->with_value_from, IPP_TAG_ZERO), found, buffer, sizeof(buffer));
1403 }
1404
1405 if (expect->repeat_no_match && repeat_count < expect->repeat_limit)
1406 repeat_test = 1;
1407
1408 break;
1409 }
1410 else if (found && !with_value(data, NULL, expect->with_value, expect->with_flags, found, buffer, sizeof(buffer)))
1411 {
1412 if (expect->define_no_match)
1413 _ippVarsSet(vars, expect->define_no_match, "1");
1414 else if (!expect->define_match && !expect->define_value &&
1415 !expect->repeat_match && (!expect->repeat_no_match || repeat_count >= expect->repeat_limit))
1416 {
1417 if (expect->with_flags & _CUPS_WITH_REGEX)
1418 add_stringf(data->errors, "EXPECTED: %s %s /%s/", expect->name, with_flags_string(expect->with_flags), expect->with_value);
1419 else
1420 add_stringf(data->errors, "EXPECTED: %s %s \"%s\"", expect->name, with_flags_string(expect->with_flags), expect->with_value);
1421
1422 with_value(data, data->errors, expect->with_value, expect->with_flags, found, buffer, sizeof(buffer));
1423 }
1424
1425 if (expect->repeat_no_match &&
1426 repeat_count < expect->repeat_limit)
1427 repeat_test = 1;
1428
1429 break;
1430 }
1431
1432 if (found && expect->count > 0 &&
1433 found->num_values != expect->count)
1434 {
1435 if (expect->define_no_match)
1436 _ippVarsSet(vars, expect->define_no_match, "1");
1437 else if (!expect->define_match && !expect->define_value)
1438 {
1439 add_stringf(data->errors, "EXPECTED: %s COUNT %d (got %d)", expect->name,
1440 expect->count, found->num_values);
1441 }
1442
1443 if (expect->repeat_no_match &&
1444 repeat_count < expect->repeat_limit)
1445 repeat_test = 1;
1446
1447 break;
1448 }
1449
1450 if (found && expect->same_count_as)
1451 {
1452 attrptr = ippFindAttribute(response, expect->same_count_as,
1453 IPP_TAG_ZERO);
1454
1455 if (!attrptr || attrptr->num_values != found->num_values)
1456 {
1457 if (expect->define_no_match)
1458 _ippVarsSet(vars, expect->define_no_match, "1");
1459 else if (!expect->define_match && !expect->define_value)
1460 {
1461 if (!attrptr)
1462 add_stringf(data->errors,
1463 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
1464 "(not returned)", expect->name,
1465 found->num_values, expect->same_count_as);
1466 else if (attrptr->num_values != found->num_values)
1467 add_stringf(data->errors,
1468 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
1469 "(%d values)", expect->name, found->num_values,
1470 expect->same_count_as, attrptr->num_values);
1471 }
1472
1473 if (expect->repeat_no_match &&
1474 repeat_count < expect->repeat_limit)
1475 repeat_test = 1;
1476
1477 break;
1478 }
1479 }
1480
1481 if (found && expect->define_match)
1482 _ippVarsSet(vars, expect->define_match, "1");
1483
1484 if (found && expect->define_value)
1485 {
1486 if (!expect->with_value)
1487 {
1488 int last = ippGetCount(found) - 1;
1489 /* Last element in attribute */
1490
1491 switch (ippGetValueTag(found))
1492 {
1493 case IPP_TAG_ENUM :
1494 case IPP_TAG_INTEGER :
1495 snprintf(buffer, sizeof(buffer), "%d", ippGetInteger(found, last));
1496 break;
1497
1498 case IPP_TAG_BOOLEAN :
1499 if (ippGetBoolean(found, last))
1500 strlcpy(buffer, "true", sizeof(buffer));
1501 else
1502 strlcpy(buffer, "false", sizeof(buffer));
1503 break;
1504
1505 case IPP_TAG_RESOLUTION :
1506 {
1507 int xres, /* Horizontal resolution */
1508 yres; /* Vertical resolution */
1509 ipp_res_t units; /* Resolution units */
1510
1511 xres = ippGetResolution(found, last, &yres, &units);
1512
1513 if (xres == yres)
1514 snprintf(buffer, sizeof(buffer), "%d%s", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
1515 else
1516 snprintf(buffer, sizeof(buffer), "%dx%d%s", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
1517 }
1518 break;
1519
1520 case IPP_TAG_CHARSET :
1521 case IPP_TAG_KEYWORD :
1522 case IPP_TAG_LANGUAGE :
1523 case IPP_TAG_MIMETYPE :
1524 case IPP_TAG_NAME :
1525 case IPP_TAG_NAMELANG :
1526 case IPP_TAG_TEXT :
1527 case IPP_TAG_TEXTLANG :
1528 case IPP_TAG_URI :
1529 case IPP_TAG_URISCHEME :
1530 strlcpy(buffer, ippGetString(found, last, NULL), sizeof(buffer));
1531 break;
1532
1533 default :
1534 ippAttributeString(found, buffer, sizeof(buffer));
1535 break;
1536 }
1537 }
1538
1539 _ippVarsSet(vars, expect->define_value, buffer);
1540 }
1541
1542 if (found && expect->repeat_match &&
1543 repeat_count < expect->repeat_limit)
1544 repeat_test = 1;
1545 }
1546 while (expect->expect_all && (found = ippFindNextAttribute(response, expect->name, IPP_TAG_ZERO)) != NULL);
1547 }
1548 }
1549
1550 /*
1551 * If we are going to repeat this test, display intermediate results...
1552 */
1553
1554 if (repeat_test)
1555 {
1556 if (data->output == _CUPS_OUTPUT_TEST || (data->output == _CUPS_OUTPUT_PLIST && data->outfile != cupsFileStdout()))
1557 {
1558 cupsFilePrintf(cupsFileStdout(), "%04d]\n", repeat_count);
1559 \
1560 if (data->num_displayed > 0)
1561 {
1562 for (attrptr = ippFirstAttribute(response); attrptr; attrptr = ippNextAttribute(response))
1563 {
1564 const char *attrname = ippGetName(attrptr);
1565 if (attrname)
1566 {
1567 for (i = 0; i < data->num_displayed; i ++)
1568 {
1569 if (!strcmp(data->displayed[i], attrname))
1570 {
1571 print_attr(cupsFileStdout(), _CUPS_OUTPUT_TEST, attrptr, NULL);
1572 break;
1573 }
1574 }
1575 }
1576 }
1577 }
1578 }
1579
1580 if (data->output == _CUPS_OUTPUT_TEST || (data->output == _CUPS_OUTPUT_PLIST && data->outfile != cupsFileStdout()))
1581 {
1582 cupsFilePrintf(cupsFileStdout(), " %-68.68s [", data->name);
1583 }
1584
1585 ippDelete(response);
1586 response = NULL;
1587 }
1588 }
1589 while (repeat_test);
1590
1591 ippDelete(request);
1592
1593 request = NULL;
1594
1595 if (cupsArrayCount(data->errors) > 0)
1596 data->prev_pass = data->pass = 0;
1597
1598 if (data->prev_pass)
1599 data->pass_count ++;
1600 else
1601 data->fail_count ++;
1602
1603 if (data->output == _CUPS_OUTPUT_PLIST)
1604 {
1605 cupsFilePuts(data->outfile, "<key>Successful</key>\n");
1606 cupsFilePuts(data->outfile, data->prev_pass ? "<true />\n" : "<false />\n");
1607 cupsFilePuts(data->outfile, "<key>StatusCode</key>\n");
1608 print_xml_string(data->outfile, "string", ippErrorString(cupsLastError()));
1609 cupsFilePuts(data->outfile, "<key>ResponseAttributes</key>\n");
1610 cupsFilePuts(data->outfile, "<array>\n");
1611 cupsFilePuts(data->outfile, "<dict>\n");
1612 for (attrptr = response ? response->attrs : NULL,
1613 group = attrptr ? attrptr->group_tag : IPP_TAG_ZERO;
1614 attrptr;
1615 attrptr = attrptr->next)
1616 print_attr(data->outfile, data->output, attrptr, &group);
1617 cupsFilePuts(data->outfile, "</dict>\n");
1618 cupsFilePuts(data->outfile, "</array>\n");
1619 }
1620 else if (data->output == _CUPS_OUTPUT_IPPSERVER && response)
1621 {
1622 for (attrptr = ippFirstAttribute(response), group = IPP_TAG_ZERO; attrptr; attrptr = ippNextAttribute(response))
1623 {
1624 if (!ippGetName(attrptr) || ippGetGroupTag(attrptr) != IPP_TAG_PRINTER)
1625 continue;
1626
1627 print_ippserver_attr(data, attrptr, 0);
1628 }
1629 }
1630
1631 if (data->output == _CUPS_OUTPUT_TEST || (data->output == _CUPS_OUTPUT_PLIST && data->outfile != cupsFileStdout()))
1632 {
1633 cupsFilePuts(cupsFileStdout(), data->prev_pass ? "PASS]\n" : "FAIL]\n");
1634
1635 if (!data->prev_pass || (data->verbosity && response))
1636 {
1637 cupsFilePrintf(cupsFileStdout(), " RECEIVED: %lu bytes in response\n", (unsigned long)ippLength(response));
1638 cupsFilePrintf(cupsFileStdout(), " status-code = %s (%s)\n", ippErrorString(cupsLastError()), cupsLastErrorString());
1639
1640 if (data->verbosity && response)
1641 {
1642 for (attrptr = response->attrs;
1643 attrptr != NULL;
1644 attrptr = attrptr->next)
1645 print_attr(cupsFileStdout(), _CUPS_OUTPUT_TEST, attrptr, NULL);
1646 }
1647 }
1648 }
1649 else if (!data->prev_pass && data->output != _CUPS_OUTPUT_QUIET)
1650 fprintf(stderr, "%s\n", cupsLastErrorString());
1651
1652 if (data->prev_pass && data->output >= _CUPS_OUTPUT_LIST && !data->verbosity && data->num_displayed > 0)
1653 {
1654 size_t width; /* Length of value */
1655
1656 for (i = 0; i < data->num_displayed; i ++)
1657 {
1658 widths[i] = strlen(data->displayed[i]);
1659
1660 for (attrptr = ippFindAttribute(response, data->displayed[i], IPP_TAG_ZERO);
1661 attrptr;
1662 attrptr = ippFindNextAttribute(response, data->displayed[i], IPP_TAG_ZERO))
1663 {
1664 width = ippAttributeString(attrptr, NULL, 0);
1665 if (width > widths[i])
1666 widths[i] = width;
1667 }
1668 }
1669
1670 if (data->output == _CUPS_OUTPUT_CSV)
1671 print_csv(data, NULL, data->num_displayed, data->displayed, widths);
1672 else
1673 print_line(data, NULL, data->num_displayed, data->displayed, widths);
1674
1675 attrptr = response->attrs;
1676
1677 while (attrptr)
1678 {
1679 while (attrptr && attrptr->group_tag <= IPP_TAG_OPERATION)
1680 attrptr = attrptr->next;
1681
1682 if (attrptr)
1683 {
1684 if (data->output == _CUPS_OUTPUT_CSV)
1685 print_csv(data, attrptr, data->num_displayed, data->displayed, widths);
1686 else
1687 print_line(data, attrptr, data->num_displayed, data->displayed, widths);
1688
1689 while (attrptr && attrptr->group_tag > IPP_TAG_OPERATION)
1690 attrptr = attrptr->next;
1691 }
1692 }
1693 }
1694 else if (!data->prev_pass)
1695 {
1696 if (data->output == _CUPS_OUTPUT_PLIST)
1697 {
1698 cupsFilePuts(data->outfile, "<key>Errors</key>\n");
1699 cupsFilePuts(data->outfile, "<array>\n");
1700
1701 for (error = (char *)cupsArrayFirst(data->errors);
1702 error;
1703 error = (char *)cupsArrayNext(data->errors))
1704 print_xml_string(data->outfile, "string", error);
1705
1706 cupsFilePuts(data->outfile, "</array>\n");
1707 }
1708
1709 if (data->output == _CUPS_OUTPUT_TEST || (data->output == _CUPS_OUTPUT_PLIST && data->outfile != cupsFileStdout()))
1710 {
1711 for (error = (char *)cupsArrayFirst(data->errors);
1712 error;
1713 error = (char *)cupsArrayNext(data->errors))
1714 cupsFilePrintf(cupsFileStdout(), " %s\n", error);
1715 }
1716 }
1717
1718 if (data->num_displayed > 0 && !data->verbosity && response && (data->output == _CUPS_OUTPUT_TEST || (data->output == _CUPS_OUTPUT_PLIST && data->outfile != cupsFileStdout())))
1719 {
1720 for (attrptr = response->attrs;
1721 attrptr != NULL;
1722 attrptr = attrptr->next)
1723 {
1724 if (attrptr->name)
1725 {
1726 for (i = 0; i < data->num_displayed; i ++)
1727 {
1728 if (!strcmp(data->displayed[i], attrptr->name))
1729 {
1730 print_attr(data->outfile, data->output, attrptr, NULL);
1731 break;
1732 }
1733 }
1734 }
1735 }
1736 }
1737
1738 skip_error:
1739
1740 if (data->output == _CUPS_OUTPUT_PLIST)
1741 cupsFilePuts(data->outfile, "</dict>\n");
1742
1743 ippDelete(response);
1744 response = NULL;
1745
1746 for (i = 0; i < data->num_statuses; i ++)
1747 {
1748 if (data->statuses[i].if_defined)
1749 free(data->statuses[i].if_defined);
1750 if (data->statuses[i].if_not_defined)
1751 free(data->statuses[i].if_not_defined);
1752 if (data->statuses[i].define_match)
1753 free(data->statuses[i].define_match);
1754 if (data->statuses[i].define_no_match)
1755 free(data->statuses[i].define_no_match);
1756 }
1757 data->num_statuses = 0;
1758
1759 for (i = data->num_expects, expect = data->expects; i > 0; i --, expect ++)
1760 {
1761 free(expect->name);
1762 if (expect->of_type)
1763 free(expect->of_type);
1764 if (expect->same_count_as)
1765 free(expect->same_count_as);
1766 if (expect->if_defined)
1767 free(expect->if_defined);
1768 if (expect->if_not_defined)
1769 free(expect->if_not_defined);
1770 if (expect->with_value)
1771 free(expect->with_value);
1772 if (expect->define_match)
1773 free(expect->define_match);
1774 if (expect->define_no_match)
1775 free(expect->define_no_match);
1776 if (expect->define_value)
1777 free(expect->define_value);
1778 }
1779 data->num_expects = 0;
1780
1781 for (i = 0; i < data->num_displayed; i ++)
1782 free(data->displayed[i]);
1783 data->num_displayed = 0;
1784
1785 return (data->ignore_errors || data->prev_pass);
1786 }
1787
1788
1789 /*
1790 * 'do_tests()' - Do tests as specified in the test file.
1791 */
1792
1793 static int /* O - 1 on success, 0 on failure */
1794 do_tests(const char *testfile, /* I - Test file to use */
1795 _ipp_vars_t *vars, /* I - Variables */
1796 _cups_testdata_t *data) /* I - Test data */
1797 {
1798 http_encryption_t encryption; /* Encryption mode */
1799
1800
1801 /*
1802 * Connect to the printer/server...
1803 */
1804
1805 if (!_cups_strcasecmp(vars->scheme, "https") || !_cups_strcasecmp(vars->scheme, "ipps"))
1806 encryption = HTTP_ENCRYPTION_ALWAYS;
1807 else
1808 encryption = data->encryption;
1809
1810 if ((data->http = httpConnect2(vars->host, vars->port, NULL, data->family, encryption, 1, 30000, NULL)) == NULL)
1811 {
1812 print_fatal_error(data, "Unable to connect to \"%s\" on port %d - %s", vars->host, vars->port, cupsLastErrorString());
1813 return (0);
1814 }
1815
1816 #ifdef HAVE_LIBZ
1817 httpSetDefaultField(data->http, HTTP_FIELD_ACCEPT_ENCODING, "deflate, gzip, identity");
1818 #else
1819 httpSetDefaultField(data->http, HTTP_FIELD_ACCEPT_ENCODING, "identity");
1820 #endif /* HAVE_LIBZ */
1821
1822 if (data->timeout > 0.0)
1823 httpSetTimeout(data->http, data->timeout, timeout_cb, NULL);
1824
1825 /*
1826 * Run tests...
1827 */
1828
1829 _ippFileParse(vars, testfile, (_ipp_ftoken_cb_t)token_cb, (_ipp_ferror_cb_t)error_cb, (void *)data);
1830
1831 /*
1832 * Close connection and return...
1833 */
1834
1835 httpClose(data->http);
1836 data->http = NULL;
1837
1838 return (data->pass);
1839 }
1840
1841
1842 /*
1843 * 'error_cb()' - Print/add an error message.
1844 */
1845
1846 static int /* O - 1 to continue, 0 to stop */
1847 error_cb(_ipp_file_t *f, /* I - IPP file data */
1848 _cups_testdata_t *data, /* I - Test data */
1849 const char *error) /* I - Error message */
1850 {
1851 (void)f;
1852
1853 print_fatal_error(data, "%s", error);
1854
1855 return (1);
1856 }
1857
1858
1859 /*
1860 * 'expect_matches()' - Return true if the tag matches the specification.
1861 */
1862
1863 static int /* O - 1 if matches, 0 otherwise */
1864 expect_matches(
1865 _cups_expect_t *expect, /* I - Expected attribute */
1866 ipp_tag_t value_tag) /* I - Value tag for attribute */
1867 {
1868 int match; /* Match? */
1869 char *of_type, /* Type name to match */
1870 *next, /* Next name to match */
1871 sep; /* Separator character */
1872
1873
1874 /*
1875 * If we don't expect a particular type, return immediately...
1876 */
1877
1878 if (!expect->of_type)
1879 return (1);
1880
1881 /*
1882 * Parse the "of_type" value since the string can contain multiple attribute
1883 * types separated by "," or "|"...
1884 */
1885
1886 for (of_type = expect->of_type, match = 0; !match && *of_type; of_type = next)
1887 {
1888 /*
1889 * Find the next separator, and set it (temporarily) to nul if present.
1890 */
1891
1892 for (next = of_type; *next && *next != '|' && *next != ','; next ++);
1893
1894 if ((sep = *next) != '\0')
1895 *next = '\0';
1896
1897 /*
1898 * Support some meta-types to make it easier to write the test file.
1899 */
1900
1901 if (!strcmp(of_type, "text"))
1902 match = value_tag == IPP_TAG_TEXTLANG || value_tag == IPP_TAG_TEXT;
1903 else if (!strcmp(of_type, "name"))
1904 match = value_tag == IPP_TAG_NAMELANG || value_tag == IPP_TAG_NAME;
1905 else if (!strcmp(of_type, "collection"))
1906 match = value_tag == IPP_TAG_BEGIN_COLLECTION;
1907 else
1908 match = value_tag == ippTagValue(of_type);
1909
1910 /*
1911 * Restore the separator if we have one...
1912 */
1913
1914 if (sep)
1915 *next++ = sep;
1916 }
1917
1918 return (match);
1919 }
1920
1921
1922 /*
1923 * 'get_filename()' - Get a filename based on the current test file.
1924 */
1925
1926 static char * /* O - Filename */
1927 get_filename(const char *testfile, /* I - Current test file */
1928 char *dst, /* I - Destination filename */
1929 const char *src, /* I - Source filename */
1930 size_t dstsize) /* I - Size of destination buffer */
1931 {
1932 char *dstptr; /* Pointer into destination */
1933 _cups_globals_t *cg = _cupsGlobals();
1934 /* Global data */
1935
1936
1937 if (*src == '<' && src[strlen(src) - 1] == '>')
1938 {
1939 /*
1940 * Map <filename> to CUPS_DATADIR/ipptool/filename...
1941 */
1942
1943 snprintf(dst, dstsize, "%s/ipptool/%s", cg->cups_datadir, src + 1);
1944 dstptr = dst + strlen(dst) - 1;
1945 if (*dstptr == '>')
1946 *dstptr = '\0';
1947 }
1948 else if (!access(src, R_OK) || *src == '/'
1949 #ifdef WIN32
1950 || (isalpha(*src & 255) && src[1] == ':')
1951 #endif /* WIN32 */
1952 )
1953 {
1954 /*
1955 * Use the path as-is...
1956 */
1957
1958 strlcpy(dst, src, dstsize);
1959 }
1960 else
1961 {
1962 /*
1963 * Make path relative to testfile...
1964 */
1965
1966 strlcpy(dst, testfile, dstsize);
1967 if ((dstptr = strrchr(dst, '/')) != NULL)
1968 dstptr ++;
1969 else
1970 dstptr = dst; /* Should never happen */
1971
1972 strlcpy(dstptr, src, dstsize - (size_t)(dstptr - dst));
1973 }
1974
1975 return (dst);
1976 }
1977
1978
1979 /*
1980 * 'get_string()' - Get a pointer to a string value or the portion of interest.
1981 */
1982
1983 static const char * /* O - Pointer to string */
1984 get_string(ipp_attribute_t *attr, /* I - IPP attribute */
1985 int element, /* I - Element to fetch */
1986 int flags, /* I - Value ("with") flags */
1987 char *buffer, /* I - Temporary buffer */
1988 size_t bufsize) /* I - Size of temporary buffer */
1989 {
1990 const char *value; /* Value */
1991 char *ptr, /* Pointer into value */
1992 scheme[256], /* URI scheme */
1993 userpass[256], /* Username/password */
1994 hostname[256], /* Hostname */
1995 resource[1024]; /* Resource */
1996 int port; /* Port number */
1997
1998
1999 value = ippGetString(attr, element, NULL);
2000
2001 if (flags & _CUPS_WITH_HOSTNAME)
2002 {
2003 if (httpSeparateURI(HTTP_URI_CODING_ALL, value, scheme, sizeof(scheme), userpass, sizeof(userpass), buffer, (int)bufsize, &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
2004 buffer[0] = '\0';
2005
2006 ptr = buffer + strlen(buffer) - 1;
2007 if (ptr >= buffer && *ptr == '.')
2008 *ptr = '\0'; /* Drop trailing "." */
2009
2010 return (buffer);
2011 }
2012 else if (flags & _CUPS_WITH_RESOURCE)
2013 {
2014 if (httpSeparateURI(HTTP_URI_CODING_ALL, value, scheme, sizeof(scheme), userpass, sizeof(userpass), hostname, sizeof(hostname), &port, buffer, (int)bufsize) < HTTP_URI_STATUS_OK)
2015 buffer[0] = '\0';
2016
2017 return (buffer);
2018 }
2019 else if (flags & _CUPS_WITH_SCHEME)
2020 {
2021 if (httpSeparateURI(HTTP_URI_CODING_ALL, value, buffer, (int)bufsize, userpass, sizeof(userpass), hostname, sizeof(hostname), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
2022 buffer[0] = '\0';
2023
2024 return (buffer);
2025 }
2026 else if (ippGetValueTag(attr) == IPP_TAG_URI && (!strncmp(value, "ipp://", 6) || !strncmp(value, "http://", 7) || !strncmp(value, "ipps://", 7) || !strncmp(value, "https://", 8)))
2027 {
2028 http_uri_status_t status = httpSeparateURI(HTTP_URI_CODING_ALL, value, scheme, sizeof(scheme), userpass, sizeof(userpass), hostname, sizeof(hostname), &port, resource, sizeof(resource));
2029
2030 if (status < HTTP_URI_STATUS_OK)
2031 {
2032 /*
2033 * Bad URI...
2034 */
2035
2036 buffer[0] = '\0';
2037 }
2038 else
2039 {
2040 /*
2041 * Normalize URI with no trailing dot...
2042 */
2043
2044 if ((ptr = hostname + strlen(hostname) - 1) >= hostname && *ptr == '.')
2045 *ptr = '\0';
2046
2047 httpAssembleURI(HTTP_URI_CODING_ALL, buffer, (int)bufsize, scheme, userpass, hostname, port, resource);
2048 }
2049
2050 return (buffer);
2051 }
2052 else
2053 return (value);
2054 }
2055
2056
2057 /*
2058 * 'init_data()' - Initialize test data.
2059 */
2060
2061 static void
2062 init_data(_cups_testdata_t *data) /* I - Data */
2063 {
2064 memset(data, 0, sizeof(_cups_testdata_t));
2065
2066 data->output = _CUPS_OUTPUT_LIST;
2067 data->outfile = cupsFileStdout();
2068 data->family = AF_UNSPEC;
2069 data->def_transfer = _CUPS_TRANSFER_AUTO;
2070 data->def_version = 11;
2071 data->errors = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free);
2072 data->pass = 1;
2073 data->prev_pass = 1;
2074 data->request_id = (CUPS_RAND() % 1000) * 137 + 1;
2075 data->show_header = 1;
2076 }
2077
2078
2079 /*
2080 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
2081 * value.
2082 */
2083
2084 static char * /* O - ISO 8601 date/time string */
2085 iso_date(const ipp_uchar_t *date) /* I - IPP (RFC 1903) date/time value */
2086 {
2087 time_t utctime; /* UTC time since 1970 */
2088 struct tm *utcdate; /* UTC date/time */
2089 static char buffer[255]; /* String buffer */
2090
2091
2092 utctime = ippDateToTime(date);
2093 utcdate = gmtime(&utctime);
2094
2095 snprintf(buffer, sizeof(buffer), "%04d-%02d-%02dT%02d:%02d:%02dZ",
2096 utcdate->tm_year + 1900, utcdate->tm_mon + 1, utcdate->tm_mday,
2097 utcdate->tm_hour, utcdate->tm_min, utcdate->tm_sec);
2098
2099 return (buffer);
2100 }
2101
2102
2103 /*
2104 * 'pause_message()' - Display the message and pause until the user presses a key.
2105 */
2106
2107 static void
2108 pause_message(const char *message) /* I - Message */
2109 {
2110 #ifdef WIN32
2111 HANDLE tty; /* Console handle */
2112 DWORD mode; /* Console mode */
2113 char key; /* Key press */
2114 DWORD bytes; /* Bytes read for key press */
2115
2116
2117 /*
2118 * Disable input echo and set raw input...
2119 */
2120
2121 if ((tty = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE)
2122 return;
2123
2124 if (!GetConsoleMode(tty, &mode))
2125 return;
2126
2127 if (!SetConsoleMode(tty, 0))
2128 return;
2129
2130 #else
2131 int tty; /* /dev/tty - never read from stdin */
2132 struct termios original, /* Original input mode */
2133 noecho; /* No echo input mode */
2134 char key; /* Current key press */
2135
2136
2137 /*
2138 * Disable input echo and set raw input...
2139 */
2140
2141 if ((tty = open("/dev/tty", O_RDONLY)) < 0)
2142 return;
2143
2144 if (tcgetattr(tty, &original))
2145 {
2146 close(tty);
2147 return;
2148 }
2149
2150 noecho = original;
2151 noecho.c_lflag &= (tcflag_t)~(ICANON | ECHO | ECHOE | ISIG);
2152
2153 if (tcsetattr(tty, TCSAFLUSH, &noecho))
2154 {
2155 close(tty);
2156 return;
2157 }
2158 #endif /* WIN32 */
2159
2160 /*
2161 * Display the prompt...
2162 */
2163
2164 cupsFilePrintf(cupsFileStdout(), "%s\n---- PRESS ANY KEY ----", message);
2165
2166 #ifdef WIN32
2167 /*
2168 * Read a key...
2169 */
2170
2171 ReadFile(tty, &key, 1, &bytes, NULL);
2172
2173 /*
2174 * Cleanup...
2175 */
2176
2177 SetConsoleMode(tty, mode);
2178
2179 #else
2180 /*
2181 * Read a key...
2182 */
2183
2184 read(tty, &key, 1);
2185
2186 /*
2187 * Cleanup...
2188 */
2189
2190 tcsetattr(tty, TCSAFLUSH, &original);
2191 close(tty);
2192 #endif /* WIN32 */
2193
2194 /*
2195 * Erase the "press any key" prompt...
2196 */
2197
2198 cupsFilePuts(cupsFileStdout(), "\r \r");
2199 }
2200
2201
2202 /*
2203 * 'print_attr()' - Print an attribute on the screen.
2204 */
2205
2206 static void
2207 print_attr(cups_file_t *outfile, /* I - Output file */
2208 int output, /* I - Output format */
2209 ipp_attribute_t *attr, /* I - Attribute to print */
2210 ipp_tag_t *group) /* IO - Current group */
2211 {
2212 int i; /* Looping var */
2213 ipp_attribute_t *colattr; /* Collection attribute */
2214
2215
2216 if (output == _CUPS_OUTPUT_PLIST)
2217 {
2218 if (!attr->name || (group && *group != attr->group_tag))
2219 {
2220 if (attr->group_tag != IPP_TAG_ZERO)
2221 {
2222 cupsFilePuts(outfile, "</dict>\n");
2223 cupsFilePuts(outfile, "<dict>\n");
2224 }
2225
2226 if (group)
2227 *group = attr->group_tag;
2228 }
2229
2230 if (!attr->name)
2231 return;
2232
2233 print_xml_string(outfile, "key", attr->name);
2234 if (attr->num_values > 1)
2235 cupsFilePuts(outfile, "<array>\n");
2236
2237 switch (attr->value_tag)
2238 {
2239 case IPP_TAG_INTEGER :
2240 case IPP_TAG_ENUM :
2241 for (i = 0; i < attr->num_values; i ++)
2242 cupsFilePrintf(outfile, "<integer>%d</integer>\n", attr->values[i].integer);
2243 break;
2244
2245 case IPP_TAG_BOOLEAN :
2246 for (i = 0; i < attr->num_values; i ++)
2247 cupsFilePuts(outfile, attr->values[i].boolean ? "<true />\n" : "<false />\n");
2248 break;
2249
2250 case IPP_TAG_RANGE :
2251 for (i = 0; i < attr->num_values; i ++)
2252 cupsFilePrintf(outfile, "<dict><key>lower</key><integer>%d</integer>"
2253 "<key>upper</key><integer>%d</integer></dict>\n",
2254 attr->values[i].range.lower, attr->values[i].range.upper);
2255 break;
2256
2257 case IPP_TAG_RESOLUTION :
2258 for (i = 0; i < attr->num_values; i ++)
2259 cupsFilePrintf(outfile, "<dict><key>xres</key><integer>%d</integer>"
2260 "<key>yres</key><integer>%d</integer>"
2261 "<key>units</key><string>%s</string></dict>\n",
2262 attr->values[i].resolution.xres,
2263 attr->values[i].resolution.yres,
2264 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
2265 "dpi" : "dpcm");
2266 break;
2267
2268 case IPP_TAG_DATE :
2269 for (i = 0; i < attr->num_values; i ++)
2270 cupsFilePrintf(outfile, "<date>%s</date>\n", iso_date(attr->values[i].date));
2271 break;
2272
2273 case IPP_TAG_STRING :
2274 for (i = 0; i < attr->num_values; i ++)
2275 {
2276 char buffer[IPP_MAX_LENGTH * 5 / 4 + 1];
2277 /* Output buffer */
2278
2279 cupsFilePrintf(outfile, "<data>%s</data>\n",
2280 httpEncode64_2(buffer, sizeof(buffer),
2281 attr->values[i].unknown.data,
2282 attr->values[i].unknown.length));
2283 }
2284 break;
2285
2286 case IPP_TAG_TEXT :
2287 case IPP_TAG_NAME :
2288 case IPP_TAG_KEYWORD :
2289 case IPP_TAG_URI :
2290 case IPP_TAG_URISCHEME :
2291 case IPP_TAG_CHARSET :
2292 case IPP_TAG_LANGUAGE :
2293 case IPP_TAG_MIMETYPE :
2294 for (i = 0; i < attr->num_values; i ++)
2295 print_xml_string(outfile, "string", attr->values[i].string.text);
2296 break;
2297
2298 case IPP_TAG_TEXTLANG :
2299 case IPP_TAG_NAMELANG :
2300 for (i = 0; i < attr->num_values; i ++)
2301 {
2302 cupsFilePuts(outfile, "<dict><key>language</key><string>");
2303 print_xml_string(outfile, NULL, attr->values[i].string.language);
2304 cupsFilePuts(outfile, "</string><key>string</key><string>");
2305 print_xml_string(outfile, NULL, attr->values[i].string.text);
2306 cupsFilePuts(outfile, "</string></dict>\n");
2307 }
2308 break;
2309
2310 case IPP_TAG_BEGIN_COLLECTION :
2311 for (i = 0; i < attr->num_values; i ++)
2312 {
2313 cupsFilePuts(outfile, "<dict>\n");
2314 for (colattr = attr->values[i].collection->attrs;
2315 colattr;
2316 colattr = colattr->next)
2317 print_attr(outfile, output, colattr, NULL);
2318 cupsFilePuts(outfile, "</dict>\n");
2319 }
2320 break;
2321
2322 default :
2323 cupsFilePrintf(outfile, "<string>&lt;&lt;%s&gt;&gt;</string>\n", ippTagString(attr->value_tag));
2324 break;
2325 }
2326
2327 if (attr->num_values > 1)
2328 cupsFilePuts(outfile, "</array>\n");
2329 }
2330 else
2331 {
2332 char buffer[131072]; /* Value buffer */
2333
2334 if (output == _CUPS_OUTPUT_TEST)
2335 {
2336 if (!attr->name)
2337 {
2338 cupsFilePuts(outfile, " -- separator --\n");
2339 return;
2340 }
2341
2342 cupsFilePrintf(outfile, " %s (%s%s) = ", attr->name, attr->num_values > 1 ? "1setOf " : "", ippTagString(attr->value_tag));
2343 }
2344
2345 ippAttributeString(attr, buffer, sizeof(buffer));
2346 cupsFilePrintf(outfile, "%s\n", buffer);
2347 }
2348 }
2349
2350
2351 /*
2352 * 'print_csv()' - Print a line of CSV text.
2353 */
2354
2355 static void
2356 print_csv(
2357 _cups_testdata_t *data, /* I - Test data */
2358 ipp_attribute_t *attr, /* I - First attribute for line */
2359 int num_displayed, /* I - Number of attributes to display */
2360 char **displayed, /* I - Attributes to display */
2361 size_t *widths) /* I - Column widths */
2362 {
2363 int i; /* Looping var */
2364 size_t maxlength; /* Max length of all columns */
2365 char *buffer, /* String buffer */
2366 *bufptr; /* Pointer into buffer */
2367 ipp_attribute_t *current; /* Current attribute */
2368
2369
2370 /*
2371 * Get the maximum string length we have to show and allocate...
2372 */
2373
2374 for (i = 1, maxlength = widths[0]; i < num_displayed; i ++)
2375 if (widths[i] > maxlength)
2376 maxlength = widths[i];
2377
2378 maxlength += 2;
2379
2380 if ((buffer = malloc(maxlength)) == NULL)
2381 return;
2382
2383 /*
2384 * Loop through the attributes to display...
2385 */
2386
2387 if (attr)
2388 {
2389 for (i = 0; i < num_displayed; i ++)
2390 {
2391 if (i)
2392 cupsFilePutChar(data->outfile, ',');
2393
2394 buffer[0] = '\0';
2395
2396 for (current = attr; current; current = current->next)
2397 {
2398 if (!current->name)
2399 break;
2400 else if (!strcmp(current->name, displayed[i]))
2401 {
2402 ippAttributeString(current, buffer, maxlength);
2403 break;
2404 }
2405 }
2406
2407 if (strchr(buffer, ',') != NULL || strchr(buffer, '\"') != NULL ||
2408 strchr(buffer, '\\') != NULL)
2409 {
2410 cupsFilePutChar(cupsFileStdout(), '\"');
2411 for (bufptr = buffer; *bufptr; bufptr ++)
2412 {
2413 if (*bufptr == '\\' || *bufptr == '\"')
2414 cupsFilePutChar(cupsFileStdout(), '\\');
2415 cupsFilePutChar(cupsFileStdout(), *bufptr);
2416 }
2417 cupsFilePutChar(cupsFileStdout(), '\"');
2418 }
2419 else
2420 cupsFilePuts(data->outfile, buffer);
2421 }
2422 cupsFilePutChar(cupsFileStdout(), '\n');
2423 }
2424 else
2425 {
2426 for (i = 0; i < num_displayed; i ++)
2427 {
2428 if (i)
2429 cupsFilePutChar(cupsFileStdout(), ',');
2430
2431 cupsFilePuts(data->outfile, displayed[i]);
2432 }
2433 cupsFilePutChar(cupsFileStdout(), '\n');
2434 }
2435
2436 free(buffer);
2437 }
2438
2439
2440 /*
2441 * 'print_fatal_error()' - Print a fatal error message.
2442 */
2443
2444 static void
2445 print_fatal_error(
2446 _cups_testdata_t *data, /* I - Test data */
2447 const char *s, /* I - Printf-style format string */
2448 ...) /* I - Additional arguments as needed */
2449 {
2450 char buffer[10240]; /* Format buffer */
2451 va_list ap; /* Pointer to arguments */
2452
2453
2454 /*
2455 * Format the error message...
2456 */
2457
2458 va_start(ap, s);
2459 vsnprintf(buffer, sizeof(buffer), s, ap);
2460 va_end(ap);
2461
2462 /*
2463 * Then output it...
2464 */
2465
2466 if (data->output == _CUPS_OUTPUT_PLIST)
2467 {
2468 print_xml_header(data);
2469 print_xml_trailer(data, 0, buffer);
2470 }
2471
2472 _cupsLangPrintf(stderr, "ipptool: %s", buffer);
2473 }
2474
2475
2476 /*
2477 * 'print_ippserver_attr()' - Print a attribute suitable for use by ippserver.
2478 */
2479
2480 static void
2481 print_ippserver_attr(
2482 _cups_testdata_t *data, /* I - Test data */
2483 ipp_attribute_t *attr, /* I - Attribute to print */
2484 int indent) /* I - Indentation level */
2485 {
2486 int i, /* Looping var */
2487 count = ippGetCount(attr);
2488 /* Number of values */
2489 ipp_attribute_t *colattr; /* Collection attribute */
2490
2491
2492 if (indent == 0)
2493 cupsFilePrintf(data->outfile, "ATTR %s %s", ippTagString(ippGetValueTag(attr)), ippGetName(attr));
2494 else
2495 cupsFilePrintf(data->outfile, "%*sMEMBER %s %s", indent, "", ippTagString(ippGetValueTag(attr)), ippGetName(attr));
2496
2497 switch (ippGetValueTag(attr))
2498 {
2499 case IPP_TAG_INTEGER :
2500 case IPP_TAG_ENUM :
2501 for (i = 0; i < count; i ++)
2502 cupsFilePrintf(data->outfile, "%s%d", i ? "," : " ", ippGetInteger(attr, i));
2503 break;
2504
2505 case IPP_TAG_BOOLEAN :
2506 cupsFilePuts(data->outfile, ippGetBoolean(attr, 0) ? " true" : " false");
2507
2508 for (i = 1; i < count; i ++)
2509 cupsFilePuts(data->outfile, ippGetBoolean(attr, 1) ? ",true" : ",false");
2510 break;
2511
2512 case IPP_TAG_RANGE :
2513 for (i = 0; i < count; i ++)
2514 {
2515 int upper, lower = ippGetRange(attr, i, &upper);
2516
2517 cupsFilePrintf(data->outfile, "%s%d-%d", i ? "," : " ", lower, upper);
2518 }
2519 break;
2520
2521 case IPP_TAG_RESOLUTION :
2522 for (i = 0; i < count; i ++)
2523 {
2524 ipp_res_t units;
2525 int yres, xres = ippGetResolution(attr, i, &yres, &units);
2526
2527 cupsFilePrintf(data->outfile, "%s%dx%d%s", i ? "," : " ", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
2528 }
2529 break;
2530
2531 case IPP_TAG_DATE :
2532 for (i = 0; i < count; i ++)
2533 cupsFilePrintf(data->outfile, "%s%s", i ? "," : " ", iso_date(ippGetDate(attr, i)));
2534 break;
2535
2536 case IPP_TAG_STRING :
2537 for (i = 0; i < count; i ++)
2538 {
2539 int len;
2540 const char *s = (const char *)ippGetOctetString(attr, i, &len);
2541
2542 cupsFilePuts(data->outfile, i ? "," : " ");
2543 print_ippserver_string(data, s, (size_t)len);
2544 }
2545 break;
2546
2547 case IPP_TAG_TEXT :
2548 case IPP_TAG_TEXTLANG :
2549 case IPP_TAG_NAME :
2550 case IPP_TAG_NAMELANG :
2551 case IPP_TAG_KEYWORD :
2552 case IPP_TAG_URI :
2553 case IPP_TAG_URISCHEME :
2554 case IPP_TAG_CHARSET :
2555 case IPP_TAG_LANGUAGE :
2556 case IPP_TAG_MIMETYPE :
2557 for (i = 0; i < count; i ++)
2558 {
2559 const char *s = ippGetString(attr, i, NULL);
2560
2561 cupsFilePuts(data->outfile, i ? "," : " ");
2562 print_ippserver_string(data, s, strlen(s));
2563 }
2564 break;
2565
2566 case IPP_TAG_BEGIN_COLLECTION :
2567 for (i = 0; i < attr->num_values; i ++)
2568 {
2569 ipp_t *col = ippGetCollection(attr, i);
2570
2571 cupsFilePuts(data->outfile, i ? ",{\n" : " {\n");
2572 for (colattr = ippFirstAttribute(col); colattr; colattr = ippNextAttribute(col))
2573 print_ippserver_attr(data, colattr, indent + 4);
2574 cupsFilePrintf(data->outfile, "%*s}", indent, "");
2575 }
2576 break;
2577
2578 default :
2579 cupsFilePuts(data->outfile, " \"\"");
2580 break;
2581 }
2582
2583 cupsFilePuts(data->outfile, "\n");
2584 }
2585
2586
2587 /*
2588 * 'print_ippserver_string()' - Print a string suitable for use by ippserver.
2589 */
2590
2591 static void
2592 print_ippserver_string(
2593 _cups_testdata_t *data, /* I - Test data */
2594 const char *s, /* I - String to print */
2595 size_t len) /* I - Length of string */
2596 {
2597 cupsFilePutChar(data->outfile, '\"');
2598 while (len > 0)
2599 {
2600 if (*s == '\"')
2601 cupsFilePutChar(data->outfile, '\\');
2602 cupsFilePutChar(data->outfile, *s);
2603
2604 s ++;
2605 len --;
2606 }
2607 cupsFilePutChar(data->outfile, '\"');
2608 }
2609
2610
2611 /*
2612 * 'print_line()' - Print a line of formatted or CSV text.
2613 */
2614
2615 static void
2616 print_line(
2617 _cups_testdata_t *data, /* I - Test data */
2618 ipp_attribute_t *attr, /* I - First attribute for line */
2619 int num_displayed, /* I - Number of attributes to display */
2620 char **displayed, /* I - Attributes to display */
2621 size_t *widths) /* I - Column widths */
2622 {
2623 int i; /* Looping var */
2624 size_t maxlength; /* Max length of all columns */
2625 char *buffer; /* String buffer */
2626 ipp_attribute_t *current; /* Current attribute */
2627
2628
2629 /*
2630 * Get the maximum string length we have to show and allocate...
2631 */
2632
2633 for (i = 1, maxlength = widths[0]; i < num_displayed; i ++)
2634 if (widths[i] > maxlength)
2635 maxlength = widths[i];
2636
2637 maxlength += 2;
2638
2639 if ((buffer = malloc(maxlength)) == NULL)
2640 return;
2641
2642 /*
2643 * Loop through the attributes to display...
2644 */
2645
2646 if (attr)
2647 {
2648 for (i = 0; i < num_displayed; i ++)
2649 {
2650 if (i)
2651 cupsFilePutChar(cupsFileStdout(), ' ');
2652
2653 buffer[0] = '\0';
2654
2655 for (current = attr; current; current = current->next)
2656 {
2657 if (!current->name)
2658 break;
2659 else if (!strcmp(current->name, displayed[i]))
2660 {
2661 ippAttributeString(current, buffer, maxlength);
2662 break;
2663 }
2664 }
2665
2666 cupsFilePrintf(data->outfile, "%*s", (int)-widths[i], buffer);
2667 }
2668 cupsFilePutChar(cupsFileStdout(), '\n');
2669 }
2670 else
2671 {
2672 for (i = 0; i < num_displayed; i ++)
2673 {
2674 if (i)
2675 cupsFilePutChar(cupsFileStdout(), ' ');
2676
2677 cupsFilePrintf(data->outfile, "%*s", (int)-widths[i], displayed[i]);
2678 }
2679 cupsFilePutChar(cupsFileStdout(), '\n');
2680
2681 for (i = 0; i < num_displayed; i ++)
2682 {
2683 if (i)
2684 cupsFilePutChar(cupsFileStdout(), ' ');
2685
2686 memset(buffer, '-', widths[i]);
2687 buffer[widths[i]] = '\0';
2688 cupsFilePuts(data->outfile, buffer);
2689 }
2690 cupsFilePutChar(cupsFileStdout(), '\n');
2691 }
2692
2693 free(buffer);
2694 }
2695
2696
2697 /*
2698 * 'print_xml_header()' - Print a standard XML plist header.
2699 */
2700
2701 static void
2702 print_xml_header(_cups_testdata_t *data)/* I - Test data */
2703 {
2704 if (!data->xml_header)
2705 {
2706 cupsFilePuts(data->outfile, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
2707 cupsFilePuts(data->outfile, "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n");
2708 cupsFilePuts(data->outfile, "<plist version=\"1.0\">\n");
2709 cupsFilePuts(data->outfile, "<dict>\n");
2710 cupsFilePuts(data->outfile, "<key>ipptoolVersion</key>\n");
2711 cupsFilePuts(data->outfile, "<string>" CUPS_SVERSION "</string>\n");
2712 cupsFilePuts(data->outfile, "<key>Transfer</key>\n");
2713 cupsFilePrintf(data->outfile, "<string>%s</string>\n", data->transfer == _CUPS_TRANSFER_AUTO ? "auto" : data->transfer == _CUPS_TRANSFER_CHUNKED ? "chunked" : "length");
2714 cupsFilePuts(data->outfile, "<key>Tests</key>\n");
2715 cupsFilePuts(data->outfile, "<array>\n");
2716
2717 data->xml_header = 1;
2718 }
2719 }
2720
2721
2722 /*
2723 * 'print_xml_string()' - Print an XML string with escaping.
2724 */
2725
2726 static void
2727 print_xml_string(cups_file_t *outfile, /* I - Test data */
2728 const char *element, /* I - Element name or NULL */
2729 const char *s) /* I - String to print */
2730 {
2731 if (element)
2732 cupsFilePrintf(outfile, "<%s>", element);
2733
2734 while (*s)
2735 {
2736 if (*s == '&')
2737 cupsFilePuts(outfile, "&amp;");
2738 else if (*s == '<')
2739 cupsFilePuts(outfile, "&lt;");
2740 else if (*s == '>')
2741 cupsFilePuts(outfile, "&gt;");
2742 else if ((*s & 0xe0) == 0xc0)
2743 {
2744 /*
2745 * Validate UTF-8 two-byte sequence...
2746 */
2747
2748 if ((s[1] & 0xc0) != 0x80)
2749 {
2750 cupsFilePutChar(outfile, '?');
2751 s ++;
2752 }
2753 else
2754 {
2755 cupsFilePutChar(outfile, *s++);
2756 cupsFilePutChar(outfile, *s);
2757 }
2758 }
2759 else if ((*s & 0xf0) == 0xe0)
2760 {
2761 /*
2762 * Validate UTF-8 three-byte sequence...
2763 */
2764
2765 if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80)
2766 {
2767 cupsFilePutChar(outfile, '?');
2768 s += 2;
2769 }
2770 else
2771 {
2772 cupsFilePutChar(outfile, *s++);
2773 cupsFilePutChar(outfile, *s++);
2774 cupsFilePutChar(outfile, *s);
2775 }
2776 }
2777 else if ((*s & 0xf8) == 0xf0)
2778 {
2779 /*
2780 * Validate UTF-8 four-byte sequence...
2781 */
2782
2783 if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 ||
2784 (s[3] & 0xc0) != 0x80)
2785 {
2786 cupsFilePutChar(outfile, '?');
2787 s += 3;
2788 }
2789 else
2790 {
2791 cupsFilePutChar(outfile, *s++);
2792 cupsFilePutChar(outfile, *s++);
2793 cupsFilePutChar(outfile, *s++);
2794 cupsFilePutChar(outfile, *s);
2795 }
2796 }
2797 else if ((*s & 0x80) || (*s < ' ' && !isspace(*s & 255)))
2798 {
2799 /*
2800 * Invalid control character...
2801 */
2802
2803 cupsFilePutChar(outfile, '?');
2804 }
2805 else
2806 cupsFilePutChar(outfile, *s);
2807
2808 s ++;
2809 }
2810
2811 if (element)
2812 cupsFilePrintf(outfile, "</%s>\n", element);
2813 }
2814
2815
2816 /*
2817 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
2818 */
2819
2820 static void
2821 print_xml_trailer(
2822 _cups_testdata_t *data, /* I - Test data */
2823 int success, /* I - 1 on success, 0 on failure */
2824 const char *message) /* I - Error message or NULL */
2825 {
2826 if (data->xml_header)
2827 {
2828 cupsFilePuts(data->outfile, "</array>\n");
2829 cupsFilePuts(data->outfile, "<key>Successful</key>\n");
2830 cupsFilePuts(data->outfile, success ? "<true />\n" : "<false />\n");
2831 if (message)
2832 {
2833 cupsFilePuts(data->outfile, "<key>ErrorMessage</key>\n");
2834 print_xml_string(data->outfile, "string", message);
2835 }
2836 cupsFilePuts(data->outfile, "</dict>\n");
2837 cupsFilePuts(data->outfile, "</plist>\n");
2838
2839 data->xml_header = 0;
2840 }
2841 }
2842
2843
2844 #ifndef WIN32
2845 /*
2846 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
2847 */
2848
2849 static void
2850 sigterm_handler(int sig) /* I - Signal number (unused) */
2851 {
2852 (void)sig;
2853
2854 Cancel = 1;
2855
2856 signal(SIGINT, SIG_DFL);
2857 signal(SIGTERM, SIG_DFL);
2858 }
2859 #endif /* !WIN32 */
2860
2861
2862 /*
2863 * 'timeout_cb()' - Handle HTTP timeouts.
2864 */
2865
2866 static int /* O - 1 to continue, 0 to cancel */
2867 timeout_cb(http_t *http, /* I - Connection to server */
2868 void *user_data) /* I - User data (unused) */
2869 {
2870 int buffered = 0; /* Bytes buffered but not yet sent */
2871
2872
2873 (void)user_data;
2874
2875 /*
2876 * If the socket still have data waiting to be sent to the printer (as can
2877 * happen if the printer runs out of paper), continue to wait until the output
2878 * buffer is empty...
2879 */
2880
2881 #ifdef SO_NWRITE /* macOS and some versions of Linux */
2882 socklen_t len = sizeof(buffered); /* Size of return value */
2883
2884 if (getsockopt(httpGetFd(http), SOL_SOCKET, SO_NWRITE, &buffered, &len))
2885 buffered = 0;
2886
2887 #elif defined(SIOCOUTQ) /* Others except Windows */
2888 if (ioctl(httpGetFd(http), SIOCOUTQ, &buffered))
2889 buffered = 0;
2890
2891 #else /* Windows (not possible) */
2892 (void)http;
2893 #endif /* SO_NWRITE */
2894
2895 return (buffered > 0);
2896 }
2897
2898
2899 /*
2900 * 'token_cb()' - Parse test file-specific tokens and run tests.
2901 */
2902
2903 static int /* O - 1 to continue, 0 to stop */
2904 token_cb(_ipp_file_t *f, /* I - IPP file data */
2905 _ipp_vars_t *vars, /* I - IPP variables */
2906 _cups_testdata_t *data, /* I - Test data */
2907 const char *token) /* I - Current token */
2908 {
2909 char name[1024], /* Name string */
2910 temp[1024], /* Temporary string */
2911 value[1024], /* Value string */
2912 *ptr; /* Pointer into value */
2913
2914
2915 if (!token)
2916 {
2917 /*
2918 * Initialize state as needed (nothing for now...)
2919 */
2920
2921 return (1);
2922 }
2923 else if (f->attrs)
2924 {
2925 /*
2926 * Parse until we see a close brace...
2927 */
2928
2929 if (_cups_strcasecmp(token, "COUNT") &&
2930 _cups_strcasecmp(token, "DEFINE-MATCH") &&
2931 _cups_strcasecmp(token, "DEFINE-NO-MATCH") &&
2932 _cups_strcasecmp(token, "DEFINE-VALUE") &&
2933 _cups_strcasecmp(token, "IF-DEFINED") &&
2934 _cups_strcasecmp(token, "IF-NOT-DEFINED") &&
2935 _cups_strcasecmp(token, "IN-GROUP") &&
2936 _cups_strcasecmp(token, "OF-TYPE") &&
2937 _cups_strcasecmp(token, "REPEAT-LIMIT") &&
2938 _cups_strcasecmp(token, "REPEAT-MATCH") &&
2939 _cups_strcasecmp(token, "REPEAT-NO-MATCH") &&
2940 _cups_strcasecmp(token, "SAME-COUNT-AS") &&
2941 _cups_strcasecmp(token, "WITH-ALL-VALUES") &&
2942 _cups_strcasecmp(token, "WITH-ALL-HOSTNAMES") &&
2943 _cups_strcasecmp(token, "WITH-ALL-RESOURCES") &&
2944 _cups_strcasecmp(token, "WITH-ALL-SCHEMES") &&
2945 _cups_strcasecmp(token, "WITH-HOSTNAME") &&
2946 _cups_strcasecmp(token, "WITH-RESOURCE") &&
2947 _cups_strcasecmp(token, "WITH-SCHEME") &&
2948 _cups_strcasecmp(token, "WITH-VALUE") &&
2949 _cups_strcasecmp(token, "WITH-VALUE-FROM"))
2950 data->last_expect = NULL;
2951
2952 if (_cups_strcasecmp(token, "DEFINE-MATCH") &&
2953 _cups_strcasecmp(token, "DEFINE-NO-MATCH") &&
2954 _cups_strcasecmp(token, "IF-DEFINED") &&
2955 _cups_strcasecmp(token, "IF-NOT-DEFINED") &&
2956 _cups_strcasecmp(token, "REPEAT-LIMIT") &&
2957 _cups_strcasecmp(token, "REPEAT-MATCH") &&
2958 _cups_strcasecmp(token, "REPEAT-NO-MATCH"))
2959 data->last_status = NULL;
2960
2961 if (!strcmp(token, "}"))
2962 {
2963 return (do_test(f, vars, data));
2964 }
2965 else if (!strcmp(token, "COMPRESSION"))
2966 {
2967 /*
2968 * COMPRESSION none
2969 * COMPRESSION deflate
2970 * COMPRESSION gzip
2971 */
2972
2973 if (_ippFileReadToken(f, temp, sizeof(temp)))
2974 {
2975 _ippVarsExpand(vars, data->compression, temp, sizeof(data->compression));
2976 #ifdef HAVE_LIBZ
2977 if (strcmp(data->compression, "none") && strcmp(data->compression, "deflate") &&
2978 strcmp(data->compression, "gzip"))
2979 #else
2980 if (strcmp(data->compression, "none"))
2981 #endif /* HAVE_LIBZ */
2982 {
2983 print_fatal_error(data, "Unsupported COMPRESSION value \"%s\" on line %d of \"%s\".", data->compression, f->linenum, f->filename);
2984 return (0);
2985 }
2986
2987 if (!strcmp(data->compression, "none"))
2988 data->compression[0] = '\0';
2989 }
2990 else
2991 {
2992 print_fatal_error(data, "Missing COMPRESSION value on line %d of \"%s\".", f->linenum, f->filename);
2993 return (0);
2994 }
2995 }
2996 else if (!strcmp(token, "DEFINE"))
2997 {
2998 /*
2999 * DEFINE name value
3000 */
3001
3002 if (_ippFileReadToken(f, name, sizeof(name)) && _ippFileReadToken(f, temp, sizeof(temp)))
3003 {
3004 _ippVarsExpand(vars, value, temp, sizeof(value));
3005 _ippVarsSet(vars, name, value);
3006 }
3007 else
3008 {
3009 print_fatal_error(data, "Missing DEFINE name and/or value on line %d of \"%s\".", f->linenum, f->filename);
3010 return (0);
3011 }
3012 }
3013 else if (!strcmp(token, "IGNORE-ERRORS"))
3014 {
3015 /*
3016 * IGNORE-ERRORS yes
3017 * IGNORE-ERRORS no
3018 */
3019
3020 if (_ippFileReadToken(f, temp, sizeof(temp)) && (!_cups_strcasecmp(temp, "yes") || !_cups_strcasecmp(temp, "no")))
3021 {
3022 data->ignore_errors = !_cups_strcasecmp(temp, "yes");
3023 }
3024 else
3025 {
3026 print_fatal_error(data, "Missing IGNORE-ERRORS value on line %d of \"%s\".", f->linenum, f->filename);
3027 return (0);
3028 }
3029 }
3030 else if (!_cups_strcasecmp(token, "NAME"))
3031 {
3032 /*
3033 * Name of test...
3034 */
3035
3036 _ippFileReadToken(f, temp, sizeof(temp));
3037 _ippVarsExpand(vars, data->name, temp, sizeof(data->name));
3038 }
3039 else if (!_cups_strcasecmp(token, "PAUSE"))
3040 {
3041 /*
3042 * Pause with a message...
3043 */
3044
3045 if (_ippFileReadToken(f, temp, sizeof(temp)))
3046 {
3047 pause_message(temp);
3048 }
3049 else
3050 {
3051 print_fatal_error(data, "Missing PAUSE message on line %d of \"%s\".", f->linenum, f->filename);
3052 return (0);
3053 }
3054 }
3055 else if (!strcmp(token, "REQUEST-ID"))
3056 {
3057 /*
3058 * REQUEST-ID #
3059 * REQUEST-ID random
3060 */
3061
3062 if (_ippFileReadToken(f, temp, sizeof(temp)))
3063 {
3064 if (isdigit(temp[0] & 255))
3065 {
3066 data->request_id = atoi(temp);
3067 }
3068 else if (!_cups_strcasecmp(temp, "random"))
3069 {
3070 data->request_id = (CUPS_RAND() % 1000) * 137 + 1;
3071 }
3072 else
3073 {
3074 print_fatal_error(data, "Bad REQUEST-ID value \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
3075 return (0);
3076 }
3077 }
3078 else
3079 {
3080 print_fatal_error(data, "Missing REQUEST-ID value on line %d of \"%s\".", f->linenum, f->filename);
3081 return (0);
3082 }
3083 }
3084 else if (!strcmp(token, "SKIP-IF-DEFINED"))
3085 {
3086 /*
3087 * SKIP-IF-DEFINED variable
3088 */
3089
3090 if (_ippFileReadToken(f, name, sizeof(name)))
3091 {
3092 if (_ippVarsGet(vars, name))
3093 data->skip_test = 1;
3094 }
3095 else
3096 {
3097 print_fatal_error(data, "Missing SKIP-IF-DEFINED value on line %d of \"%s\".", f->linenum, f->filename);
3098 return (0);
3099 }
3100 }
3101 else if (!strcmp(token, "SKIP-IF-MISSING"))
3102 {
3103 /*
3104 * SKIP-IF-MISSING filename
3105 */
3106
3107 if (_ippFileReadToken(f, temp, sizeof(temp)))
3108 {
3109 char filename[1024]; /* Filename */
3110
3111 _ippVarsExpand(vars, value, temp, sizeof(value));
3112 get_filename(f->filename, filename, temp, sizeof(filename));
3113
3114 if (access(filename, R_OK))
3115 data->skip_test = 1;
3116 }
3117 else
3118 {
3119 print_fatal_error(data, "Missing SKIP-IF-MISSING filename on line %d of \"%s\".", f->linenum, f->filename);
3120 return (0);
3121 }
3122 }
3123 else if (!strcmp(token, "SKIP-IF-NOT-DEFINED"))
3124 {
3125 /*
3126 * SKIP-IF-NOT-DEFINED variable
3127 */
3128
3129 if (_ippFileReadToken(f, name, sizeof(name)))
3130 {
3131 if (!_ippVarsGet(vars, name))
3132 data->skip_test = 1;
3133 }
3134 else
3135 {
3136 print_fatal_error(data, "Missing SKIP-IF-NOT-DEFINED value on line %d of \"%s\".", f->linenum, f->filename);
3137 return (0);
3138 }
3139 }
3140 else if (!strcmp(token, "SKIP-PREVIOUS-ERROR"))
3141 {
3142 /*
3143 * SKIP-PREVIOUS-ERROR yes
3144 * SKIP-PREVIOUS-ERROR no
3145 */
3146
3147 if (_ippFileReadToken(f, temp, sizeof(temp)) && (!_cups_strcasecmp(temp, "yes") || !_cups_strcasecmp(temp, "no")))
3148 {
3149 data->skip_previous = !_cups_strcasecmp(temp, "yes");
3150 }
3151 else
3152 {
3153 print_fatal_error(data, "Missing SKIP-PREVIOUS-ERROR value on line %d of \"%s\".", f->linenum, f->filename);
3154 return (0);
3155 }
3156 }
3157 else if (!strcmp(token, "TEST-ID"))
3158 {
3159 /*
3160 * TEST-ID "string"
3161 */
3162
3163 if (_ippFileReadToken(f, temp, sizeof(temp)))
3164 {
3165 _ippVarsExpand(vars, data->test_id, temp, sizeof(data->test_id));
3166 }
3167 else
3168 {
3169 print_fatal_error(data, "Missing TEST-ID value on line %d of \"%s\".", f->linenum, f->filename);
3170 return (0);
3171 }
3172 }
3173 else if (!strcmp(token, "TRANSFER"))
3174 {
3175 /*
3176 * TRANSFER auto
3177 * TRANSFER chunked
3178 * TRANSFER length
3179 */
3180
3181 if (_ippFileReadToken(f, temp, sizeof(temp)))
3182 {
3183 if (!strcmp(temp, "auto"))
3184 {
3185 data->transfer = _CUPS_TRANSFER_AUTO;
3186 }
3187 else if (!strcmp(temp, "chunked"))
3188 {
3189 data->transfer = _CUPS_TRANSFER_CHUNKED;
3190 }
3191 else if (!strcmp(temp, "length"))
3192 {
3193 data->transfer = _CUPS_TRANSFER_LENGTH;
3194 }
3195 else
3196 {
3197 print_fatal_error(data, "Bad TRANSFER value \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
3198 return (0);
3199 }
3200 }
3201 else
3202 {
3203 print_fatal_error(data, "Missing TRANSFER value on line %d of \"%s\".", f->linenum, f->filename);
3204 return (0);
3205 }
3206 }
3207 else if (!_cups_strcasecmp(token, "VERSION"))
3208 {
3209 if (_ippFileReadToken(f, temp, sizeof(temp)))
3210 {
3211 if (!strcmp(temp, "0.0"))
3212 {
3213 data->version = 0;
3214 }
3215 else if (!strcmp(temp, "1.0"))
3216 {
3217 data->version = 10;
3218 }
3219 else if (!strcmp(temp, "1.1"))
3220 {
3221 data->version = 11;
3222 }
3223 else if (!strcmp(temp, "2.0"))
3224 {
3225 data->version = 20;
3226 }
3227 else if (!strcmp(temp, "2.1"))
3228 {
3229 data->version = 21;
3230 }
3231 else if (!strcmp(temp, "2.2"))
3232 {
3233 data->version = 22;
3234 }
3235 else
3236 {
3237 print_fatal_error(data, "Bad VERSION \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
3238 return (0);
3239 }
3240 }
3241 else
3242 {
3243 print_fatal_error(data, "Missing VERSION number on line %d of \"%s\".", f->linenum, f->filename);
3244 return (0);
3245 }
3246 }
3247 else if (!_cups_strcasecmp(token, "RESOURCE"))
3248 {
3249 /*
3250 * Resource name...
3251 */
3252
3253 if (!_ippFileReadToken(f, data->resource, sizeof(data->resource)))
3254 {
3255 print_fatal_error(data, "Missing RESOURCE path on line %d of \"%s\".", f->linenum, f->filename);
3256 return (0);
3257 }
3258 }
3259 else if (!_cups_strcasecmp(token, "OPERATION"))
3260 {
3261 /*
3262 * Operation...
3263 */
3264
3265 ipp_op_t op; /* Operation code */
3266
3267 if (!_ippFileReadToken(f, temp, sizeof(temp)))
3268 {
3269 print_fatal_error(data, "Missing OPERATION code on line %d of \"%s\".", f->linenum, f->filename);
3270 return (0);
3271 }
3272
3273 _ippVarsExpand(vars, value, temp, sizeof(value));
3274
3275 if ((op = ippOpValue(value)) == (ipp_op_t)-1 && (op = (ipp_op_t)strtol(value, NULL, 0)) == 0)
3276 {
3277 print_fatal_error(data, "Bad OPERATION code \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
3278 return (0);
3279 }
3280
3281 ippSetOperation(f->attrs, op);
3282 }
3283 else if (!_cups_strcasecmp(token, "GROUP"))
3284 {
3285 /*
3286 * Attribute group...
3287 */
3288
3289 ipp_tag_t group_tag; /* Group tag */
3290
3291 if (!_ippFileReadToken(f, temp, sizeof(temp)))
3292 {
3293 print_fatal_error(data, "Missing GROUP tag on line %d of \"%s\".", f->linenum, f->filename);
3294 return (0);
3295 }
3296
3297 if ((group_tag = ippTagValue(temp)) == IPP_TAG_ZERO || group_tag >= IPP_TAG_UNSUPPORTED_VALUE)
3298 {
3299 print_fatal_error(data, "Bad GROUP tag \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
3300 return (0);
3301 }
3302
3303 if (group_tag == f->group_tag)
3304 ippAddSeparator(f->attrs);
3305
3306 f->group_tag = group_tag;
3307 }
3308 else if (!_cups_strcasecmp(token, "DELAY"))
3309 {
3310 /*
3311 * Delay before operation...
3312 */
3313
3314 double dval; /* Delay value */
3315
3316 if (!_ippFileReadToken(f, temp, sizeof(temp)))
3317 {
3318 print_fatal_error(data, "Missing DELAY value on line %d of \"%s\".", f->linenum, f->filename);
3319 return (0);
3320 }
3321
3322 _ippVarsExpand(vars, value, temp, sizeof(value));
3323
3324 if ((dval = _cupsStrScand(value, &ptr, localeconv())) < 0.0 || (*ptr && *ptr != ','))
3325 {
3326 print_fatal_error(data, "Bad DELAY value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
3327 return (0);
3328 }
3329
3330 data->delay = (useconds_t)(1000000.0 * dval);
3331
3332 if (*ptr == ',')
3333 {
3334 if ((dval = _cupsStrScand(ptr + 1, &ptr, localeconv())) <= 0.0 || *ptr)
3335 {
3336 print_fatal_error(data, "Bad DELAY value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
3337 return (0);
3338 }
3339
3340 data->repeat_interval = (useconds_t)(1000000.0 * dval);
3341 }
3342 else
3343 data->repeat_interval = data->delay;
3344 }
3345 else if (!_cups_strcasecmp(token, "FILE"))
3346 {
3347 /*
3348 * File...
3349 */
3350
3351 if (!_ippFileReadToken(f, temp, sizeof(temp)))
3352 {
3353 print_fatal_error(data, "Missing FILE filename on line %d of \"%s\".", f->linenum, f->filename);
3354 return (0);
3355 }
3356
3357 _ippVarsExpand(vars, value, temp, sizeof(value));
3358 get_filename(f->filename, data->file, value, sizeof(data->file));
3359
3360 if (access(data->file, R_OK))
3361 {
3362 print_fatal_error(data, "Filename \"%s\" (mapped to \"%s\") on line %d of \"%s\" cannot be read.", value, data->file, f->linenum, f->filename);
3363 return (0);
3364 }
3365 }
3366 else if (!_cups_strcasecmp(token, "STATUS"))
3367 {
3368 /*
3369 * Status...
3370 */
3371
3372 if (data->num_statuses >= (int)(sizeof(data->statuses) / sizeof(data->statuses[0])))
3373 {
3374 print_fatal_error(data, "Too many STATUS's on line %d of \"%s\".", f->linenum, f->filename);
3375 return (0);
3376 }
3377
3378 if (!_ippFileReadToken(f, temp, sizeof(temp)))
3379 {
3380 print_fatal_error(data, "Missing STATUS code on line %d of \"%s\".", f->linenum, f->filename);
3381 return (0);
3382 }
3383
3384 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)
3385 {
3386 print_fatal_error(data, "Bad STATUS code \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
3387 return (0);
3388 }
3389
3390 data->last_status = data->statuses + data->num_statuses;
3391 data->num_statuses ++;
3392
3393 data->last_status->define_match = NULL;
3394 data->last_status->define_no_match = NULL;
3395 data->last_status->if_defined = NULL;
3396 data->last_status->if_not_defined = NULL;
3397 data->last_status->repeat_limit = 1000;
3398 data->last_status->repeat_match = 0;
3399 data->last_status->repeat_no_match = 0;
3400 }
3401 else if (!_cups_strcasecmp(token, "EXPECT") || !_cups_strcasecmp(token, "EXPECT-ALL"))
3402 {
3403 /*
3404 * Expected attributes...
3405 */
3406
3407 int expect_all = !_cups_strcasecmp(token, "EXPECT-ALL");
3408
3409 if (data->num_expects >= (int)(sizeof(data->expects) / sizeof(data->expects[0])))
3410 {
3411 print_fatal_error(data, "Too many EXPECT's on line %d of \"%s\".", f->linenum, f->filename);
3412 return (0);
3413 }
3414
3415 if (!_ippFileReadToken(f, name, sizeof(name)))
3416 {
3417 print_fatal_error(data, "Missing EXPECT name on line %d of \"%s\".", f->linenum, f->filename);
3418 return (0);
3419 }
3420
3421 data->last_expect = data->expects + data->num_expects;
3422 data->num_expects ++;
3423
3424 memset(data->last_expect, 0, sizeof(_cups_expect_t));
3425 data->last_expect->repeat_limit = 1000;
3426 data->last_expect->expect_all = expect_all;
3427
3428 if (name[0] == '!')
3429 {
3430 data->last_expect->not_expect = 1;
3431 data->last_expect->name = strdup(name + 1);
3432 }
3433 else if (name[0] == '?')
3434 {
3435 data->last_expect->optional = 1;
3436 data->last_expect->name = strdup(name + 1);
3437 }
3438 else
3439 data->last_expect->name = strdup(name);
3440 }
3441 else if (!_cups_strcasecmp(token, "COUNT"))
3442 {
3443 int count; /* Count value */
3444
3445 if (!_ippFileReadToken(f, temp, sizeof(temp)))
3446 {
3447 print_fatal_error(data, "Missing COUNT number on line %d of \"%s\".", f->linenum, f->filename);
3448 return (0);
3449 }
3450
3451 if ((count = atoi(temp)) <= 0)
3452 {
3453 print_fatal_error(data, "Bad COUNT \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
3454 return (0);
3455 }
3456
3457 if (data->last_expect)
3458 {
3459 data->last_expect->count = count;
3460 }
3461 else
3462 {
3463 print_fatal_error(data, "COUNT without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
3464 return (0);
3465 }
3466 }
3467 else if (!_cups_strcasecmp(token, "DEFINE-MATCH"))
3468 {
3469 if (!_ippFileReadToken(f, temp, sizeof(temp)))
3470 {
3471 print_fatal_error(data, "Missing DEFINE-MATCH variable on line %d of \"%s\".", f->linenum, f->filename);
3472 return (0);
3473 }
3474
3475 if (data->last_expect)
3476 {
3477 data->last_expect->define_match = strdup(temp);
3478 }
3479 else if (data->last_status)
3480 {
3481 data->last_status->define_match = strdup(temp);
3482 }
3483 else
3484 {
3485 print_fatal_error(data, "DEFINE-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f->linenum, f->filename);
3486 return (0);
3487 }
3488 }
3489 else if (!_cups_strcasecmp(token, "DEFINE-NO-MATCH"))
3490 {
3491 if (!_ippFileReadToken(f, temp, sizeof(temp)))
3492 {
3493 print_fatal_error(data, "Missing DEFINE-NO-MATCH variable on line %d of \"%s\".", f->linenum, f->filename);
3494 return (0);
3495 }
3496
3497 if (data->last_expect)
3498 {
3499 data->last_expect->define_no_match = strdup(temp);
3500 }
3501 else if (data->last_status)
3502 {
3503 data->last_status->define_no_match = strdup(temp);
3504 }
3505 else
3506 {
3507 print_fatal_error(data, "DEFINE-NO-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f->linenum, f->filename);
3508 return (0);
3509 }
3510 }
3511 else if (!_cups_strcasecmp(token, "DEFINE-VALUE"))
3512 {
3513 if (!_ippFileReadToken(f, temp, sizeof(temp)))
3514 {
3515 print_fatal_error(data, "Missing DEFINE-VALUE variable on line %d of \"%s\".", f->linenum, f->filename);
3516 return (0);
3517 }
3518
3519 if (data->last_expect)
3520 {
3521 data->last_expect->define_value = strdup(temp);
3522 }
3523 else
3524 {
3525 print_fatal_error(data, "DEFINE-VALUE without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
3526 return (0);
3527 }
3528 }
3529 else if (!_cups_strcasecmp(token, "OF-TYPE"))
3530 {
3531 if (!_ippFileReadToken(f, temp, sizeof(temp)))
3532 {
3533 print_fatal_error(data, "Missing OF-TYPE value tag(s) on line %d of \"%s\".", f->linenum, f->filename);
3534 return (0);
3535 }
3536
3537 if (data->last_expect)
3538 {
3539 data->last_expect->of_type = strdup(temp);
3540 }
3541 else
3542 {
3543 print_fatal_error(data, "OF-TYPE without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
3544 return (0);
3545 }
3546 }
3547 else if (!_cups_strcasecmp(token, "IN-GROUP"))
3548 {
3549 ipp_tag_t in_group; /* IN-GROUP value */
3550
3551 if (!_ippFileReadToken(f, temp, sizeof(temp)))
3552 {
3553 print_fatal_error(data, "Missing IN-GROUP group tag on line %d of \"%s\".", f->linenum, f->filename);
3554 return (0);
3555 }
3556
3557 if ((in_group = ippTagValue(temp)) == IPP_TAG_ZERO || in_group >= IPP_TAG_UNSUPPORTED_VALUE)
3558 {
3559 print_fatal_error(data, "Bad IN-GROUP group tag \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
3560 return (0);
3561 }
3562 else if (data->last_expect)
3563 {
3564 data->last_expect->in_group = in_group;
3565 }
3566 else
3567 {
3568 print_fatal_error(data, "IN-GROUP without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
3569 return (0);
3570 }
3571 }
3572 else if (!_cups_strcasecmp(token, "REPEAT-LIMIT"))
3573 {
3574 if (!_ippFileReadToken(f, temp, sizeof(temp)))
3575 {
3576 print_fatal_error(data, "Missing REPEAT-LIMIT value on line %d of \"%s\".", f->linenum, f->filename);
3577 return (0);
3578 }
3579 else if (atoi(temp) <= 0)
3580 {
3581 print_fatal_error(data, "Bad REPEAT-LIMIT value on line %d of \"%s\".", f->linenum, f->filename);
3582 return (0);
3583 }
3584
3585 if (data->last_status)
3586 {
3587 data->last_status->repeat_limit = atoi(temp);
3588 }
3589 else if (data->last_expect)
3590 {
3591 data->last_expect->repeat_limit = atoi(temp);
3592 }
3593 else
3594 {
3595 print_fatal_error(data, "REPEAT-LIMIT without a preceding EXPECT or STATUS on line %d of \"%s\".", f->linenum, f->filename);
3596 return (0);
3597 }
3598 }
3599 else if (!_cups_strcasecmp(token, "REPEAT-MATCH"))
3600 {
3601 if (data->last_status)
3602 {
3603 data->last_status->repeat_match = 1;
3604 }
3605 else if (data->last_expect)
3606 {
3607 data->last_expect->repeat_match = 1;
3608 }
3609 else
3610 {
3611 print_fatal_error(data, "REPEAT-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f->linenum, f->filename);
3612 return (0);
3613 }
3614 }
3615 else if (!_cups_strcasecmp(token, "REPEAT-NO-MATCH"))
3616 {
3617 if (data->last_status)
3618 {
3619 data->last_status->repeat_no_match = 1;
3620 }
3621 else if (data->last_expect)
3622 {
3623 data->last_expect->repeat_no_match = 1;
3624 }
3625 else
3626 {
3627 print_fatal_error(data, "REPEAT-NO-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f->linenum, f->filename);
3628 return (0);
3629 }
3630 }
3631 else if (!_cups_strcasecmp(token, "SAME-COUNT-AS"))
3632 {
3633 if (!_ippFileReadToken(f, temp, sizeof(temp)))
3634 {
3635 print_fatal_error(data, "Missing SAME-COUNT-AS name on line %d of \"%s\".", f->linenum, f->filename);
3636 return (0);
3637 }
3638
3639 if (data->last_expect)
3640 {
3641 data->last_expect->same_count_as = strdup(temp);
3642 }
3643 else
3644 {
3645 print_fatal_error(data, "SAME-COUNT-AS without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
3646 return (0);
3647 }
3648 }
3649 else if (!_cups_strcasecmp(token, "IF-DEFINED"))
3650 {
3651 if (!_ippFileReadToken(f, temp, sizeof(temp)))
3652 {
3653 print_fatal_error(data, "Missing IF-DEFINED name on line %d of \"%s\".", f->linenum, f->filename);
3654 return (0);
3655 }
3656
3657 if (data->last_expect)
3658 {
3659 data->last_expect->if_defined = strdup(temp);
3660 }
3661 else if (data->last_status)
3662 {
3663 data->last_status->if_defined = strdup(temp);
3664 }
3665 else
3666 {
3667 print_fatal_error(data, "IF-DEFINED without a preceding EXPECT or STATUS on line %d of \"%s\".", f->linenum, f->filename);
3668 return (0);
3669 }
3670 }
3671 else if (!_cups_strcasecmp(token, "IF-NOT-DEFINED"))
3672 {
3673 if (!_ippFileReadToken(f, temp, sizeof(temp)))
3674 {
3675 print_fatal_error(data, "Missing IF-NOT-DEFINED name on line %d of \"%s\".", f->linenum, f->filename);
3676 return (0);
3677 }
3678
3679 if (data->last_expect)
3680 {
3681 data->last_expect->if_not_defined = strdup(temp);
3682 }
3683 else if (data->last_status)
3684 {
3685 data->last_status->if_not_defined = strdup(temp);
3686 }
3687 else
3688 {
3689 print_fatal_error(data, "IF-NOT-DEFINED without a preceding EXPECT or STATUS on line %d of \"%s\".", f->linenum, f->filename);
3690 return (0);
3691 }
3692 }
3693 else if (!_cups_strcasecmp(token, "WITH-ALL-VALUES") ||
3694 !_cups_strcasecmp(token, "WITH-ALL-HOSTNAMES") ||
3695 !_cups_strcasecmp(token, "WITH-ALL-RESOURCES") ||
3696 !_cups_strcasecmp(token, "WITH-ALL-SCHEMES") ||
3697 !_cups_strcasecmp(token, "WITH-HOSTNAME") ||
3698 !_cups_strcasecmp(token, "WITH-RESOURCE") ||
3699 !_cups_strcasecmp(token, "WITH-SCHEME") ||
3700 !_cups_strcasecmp(token, "WITH-VALUE"))
3701 {
3702 off_t lastpos; /* Last file position */
3703
3704 if (data->last_expect)
3705 {
3706 if (!_cups_strcasecmp(token, "WITH-ALL-HOSTNAMES") || !_cups_strcasecmp(token, "WITH-HOSTNAME"))
3707 data->last_expect->with_flags = _CUPS_WITH_HOSTNAME;
3708 else if (!_cups_strcasecmp(token, "WITH-ALL-RESOURCES") || !_cups_strcasecmp(token, "WITH-RESOURCE"))
3709 data->last_expect->with_flags = _CUPS_WITH_RESOURCE;
3710 else if (!_cups_strcasecmp(token, "WITH-ALL-SCHEMES") || !_cups_strcasecmp(token, "WITH-SCHEME"))
3711 data->last_expect->with_flags = _CUPS_WITH_SCHEME;
3712
3713 if (!_cups_strncasecmp(token, "WITH-ALL-", 9))
3714 data->last_expect->with_flags |= _CUPS_WITH_ALL;
3715 }
3716
3717 if (!_ippFileReadToken(f, temp, sizeof(temp)))
3718 {
3719 print_fatal_error(data, "Missing %s value on line %d of \"%s\".", token, f->linenum, f->filename);
3720 return (0);
3721 }
3722
3723 /*
3724 * Read additional comma-delimited values - needed since legacy test files
3725 * will have unquoted WITH-VALUE values with commas...
3726 */
3727
3728 ptr = temp + strlen(temp);
3729
3730 for (;;)
3731 {
3732 lastpos = cupsFileTell(f->fp);
3733 ptr += strlen(ptr);
3734
3735 if (!_ippFileReadToken(f, ptr, (sizeof(temp) - (size_t)(ptr - temp))))
3736 break;
3737
3738 if (!strcmp(ptr, ","))
3739 {
3740 /*
3741 * Append a value...
3742 */
3743
3744 ptr += strlen(ptr);
3745
3746 if (!_ippFileReadToken(f, ptr, (sizeof(temp) - (size_t)(ptr - temp))))
3747 break;
3748 }
3749 else
3750 {
3751 /*
3752 * Not another value, stop here...
3753 */
3754
3755 cupsFileSeek(f->fp, lastpos);
3756 *ptr = '\0';
3757 break;
3758 }
3759 }
3760
3761 if (data->last_expect)
3762 {
3763 /*
3764 * Expand any variables in the value and then save it.
3765 */
3766
3767 _ippVarsExpand(vars, value, temp, sizeof(value));
3768
3769 ptr = value + strlen(value) - 1;
3770
3771 if (value[0] == '/' && ptr > value && *ptr == '/')
3772 {
3773 /*
3774 * WITH-VALUE is a POSIX extended regular expression.
3775 */
3776
3777 data->last_expect->with_value = calloc(1, (size_t)(ptr - value));
3778 data->last_expect->with_flags |= _CUPS_WITH_REGEX;
3779
3780 if (data->last_expect->with_value)
3781 memcpy(data->last_expect->with_value, value + 1, (size_t)(ptr - value - 1));
3782 }
3783 else
3784 {
3785 /*
3786 * WITH-VALUE is a literal value...
3787 */
3788
3789 for (ptr = value; *ptr; ptr ++)
3790 {
3791 if (*ptr == '\\' && ptr[1])
3792 {
3793 /*
3794 * Remove \ from \foo...
3795 */
3796
3797 _cups_strcpy(ptr, ptr + 1);
3798 }
3799 }
3800
3801 data->last_expect->with_value = strdup(value);
3802 data->last_expect->with_flags |= _CUPS_WITH_LITERAL;
3803 }
3804 }
3805 else
3806 {
3807 print_fatal_error(data, "%s without a preceding EXPECT on line %d of \"%s\".", token, f->linenum, f->filename);
3808 return (0);
3809 }
3810 }
3811 else if (!_cups_strcasecmp(token, "WITH-VALUE-FROM"))
3812 {
3813 if (!_ippFileReadToken(f, temp, sizeof(temp)))
3814 {
3815 print_fatal_error(data, "Missing %s value on line %d of \"%s\".", token, f->linenum, f->filename);
3816 return (0);
3817 }
3818
3819 if (data->last_expect)
3820 {
3821 /*
3822 * Expand any variables in the value and then save it.
3823 */
3824
3825 _ippVarsExpand(vars, value, temp, sizeof(value));
3826
3827 data->last_expect->with_value_from = strdup(value);
3828 data->last_expect->with_flags = _CUPS_WITH_LITERAL;
3829 }
3830 else
3831 {
3832 print_fatal_error(data, "%s without a preceding EXPECT on line %d of \"%s\".", token, f->linenum, f->filename);
3833 return (0);
3834 }
3835 }
3836 else if (!_cups_strcasecmp(token, "DISPLAY"))
3837 {
3838 /*
3839 * Display attributes...
3840 */
3841
3842 if (data->num_displayed >= (int)(sizeof(data->displayed) / sizeof(data->displayed[0])))
3843 {
3844 print_fatal_error(data, "Too many DISPLAY's on line %d of \"%s\".", f->linenum, f->filename);
3845 return (0);
3846 }
3847
3848 if (!_ippFileReadToken(f, temp, sizeof(temp)))
3849 {
3850 print_fatal_error(data, "Missing DISPLAY name on line %d of \"%s\".", f->linenum, f->filename);
3851 return (0);
3852 }
3853
3854 data->displayed[data->num_displayed] = strdup(temp);
3855 data->num_displayed ++;
3856 }
3857 else
3858 {
3859 print_fatal_error(data, "Unexpected token %s seen on line %d of \"%s\".", token, f->linenum, f->filename);
3860 return (0);
3861 }
3862 }
3863 else
3864 {
3865 /*
3866 * Scan for the start of a test (open brace)...
3867 */
3868
3869 if (!strcmp(token, "{"))
3870 {
3871 /*
3872 * Start new test...
3873 */
3874
3875 if (data->show_header)
3876 {
3877 if (data->output == _CUPS_OUTPUT_PLIST)
3878 print_xml_header(data);
3879
3880 if (data->output == _CUPS_OUTPUT_TEST || (data->output == _CUPS_OUTPUT_PLIST && data->outfile != cupsFileStdout()))
3881 cupsFilePrintf(cupsFileStdout(), "\"%s\":\n", f->filename);
3882
3883 data->show_header = 0;
3884 }
3885
3886 data->compression[0] = '\0';
3887 data->delay = 0;
3888 data->num_expects = 0;
3889 data->last_expect = NULL;
3890 data->file[0] = '\0';
3891 data->ignore_errors = data->def_ignore_errors;
3892 strlcpy(data->name, f->filename, sizeof(data->name));
3893 if ((ptr = strrchr(data->name, '.')) != NULL)
3894 *ptr = '\0';
3895 data->repeat_interval = 5000000;
3896 data->request_id ++;
3897 strlcpy(data->resource, vars->resource, sizeof(data->resource));
3898 data->skip_previous = 0;
3899 data->skip_test = 0;
3900 data->num_statuses = 0;
3901 data->last_status = NULL;
3902 data->test_id[0] = '\0';
3903 data->transfer = data->def_transfer;
3904 data->version = data->def_version;
3905
3906 f->attrs = ippNew();
3907 f->group_tag = IPP_TAG_ZERO;
3908 }
3909 else if (!strcmp(token, "DEFINE"))
3910 {
3911 /*
3912 * DEFINE name value
3913 */
3914
3915 if (_ippFileReadToken(f, name, sizeof(name)) && _ippFileReadToken(f, temp, sizeof(temp)))
3916 {
3917 _ippVarsExpand(vars, value, temp, sizeof(value));
3918 _ippVarsSet(vars, name, value);
3919 }
3920 else
3921 {
3922 print_fatal_error(data, "Missing DEFINE name and/or value on line %d of \"%s\".", f->linenum, f->filename);
3923 return (0);
3924 }
3925 }
3926 else if (!strcmp(token, "DEFINE-DEFAULT"))
3927 {
3928 /*
3929 * DEFINE-DEFAULT name value
3930 */
3931
3932 if (_ippFileReadToken(f, name, sizeof(name)) && _ippFileReadToken(f, temp, sizeof(temp)))
3933 {
3934 if (!_ippVarsGet(vars, name))
3935 {
3936 _ippVarsExpand(vars, value, temp, sizeof(value));
3937 _ippVarsSet(vars, name, value);
3938 }
3939 }
3940 else
3941 {
3942 print_fatal_error(data, "Missing DEFINE-DEFAULT name and/or value on line %d of \"%s\".", f->linenum, f->filename);
3943 return (0);
3944 }
3945 }
3946 else if (!strcmp(token, "FILE-ID"))
3947 {
3948 /*
3949 * FILE-ID "string"
3950 */
3951
3952 if (_ippFileReadToken(f, temp, sizeof(temp)))
3953 {
3954 _ippVarsExpand(vars, data->file_id, temp, sizeof(data->file_id));
3955 }
3956 else
3957 {
3958 print_fatal_error(data, "Missing FILE-ID value on line %d of \"%s\".", f->linenum, f->filename);
3959 return (0);
3960 }
3961 }
3962 else if (!strcmp(token, "IGNORE-ERRORS"))
3963 {
3964 /*
3965 * IGNORE-ERRORS yes
3966 * IGNORE-ERRORS no
3967 */
3968
3969 if (_ippFileReadToken(f, temp, sizeof(temp)) && (!_cups_strcasecmp(temp, "yes") || !_cups_strcasecmp(temp, "no")))
3970 {
3971 data->def_ignore_errors = !_cups_strcasecmp(temp, "yes");
3972 }
3973 else
3974 {
3975 print_fatal_error(data, "Missing IGNORE-ERRORS value on line %d of \"%s\".", f->linenum, f->filename);
3976 return (0);
3977 }
3978 }
3979 else if (!strcmp(token, "INCLUDE"))
3980 {
3981 /*
3982 * INCLUDE "filename"
3983 * INCLUDE <filename>
3984 */
3985
3986 if (_ippFileReadToken(f, temp, sizeof(temp)))
3987 {
3988 /*
3989 * Map the filename to and then run the tests...
3990 */
3991
3992 _cups_testdata_t inc_data; /* Data for included file */
3993 char filename[1024]; /* Mapped filename */
3994
3995 memcpy(&inc_data, data, sizeof(inc_data));
3996 inc_data.http = NULL;
3997 inc_data.pass = 1;
3998 inc_data.prev_pass = 1;
3999 inc_data.show_header = 1;
4000
4001 if (!do_tests(get_filename(f->filename, filename, temp, sizeof(filename)), vars, &inc_data) && data->stop_after_include_error)
4002 {
4003 data->pass = data->prev_pass = 0;
4004 return (0);
4005 }
4006 }
4007 else
4008 {
4009 print_fatal_error(data, "Missing INCLUDE filename on line %d of \"%s\".", f->linenum, f->filename);
4010 return (0);
4011 }
4012
4013 data->show_header = 1;
4014 }
4015 else if (!strcmp(token, "INCLUDE-IF-DEFINED"))
4016 {
4017 /*
4018 * INCLUDE-IF-DEFINED name "filename"
4019 * INCLUDE-IF-DEFINED name <filename>
4020 */
4021
4022 if (_ippFileReadToken(f, name, sizeof(name)) && _ippFileReadToken(f, temp, sizeof(temp)))
4023 {
4024 /*
4025 * Map the filename to and then run the tests...
4026 */
4027
4028 _cups_testdata_t inc_data; /* Data for included file */
4029 char filename[1024]; /* Mapped filename */
4030
4031 memcpy(&inc_data, data, sizeof(inc_data));
4032 inc_data.http = NULL;
4033 inc_data.pass = 1;
4034 inc_data.prev_pass = 1;
4035 inc_data.show_header = 1;
4036
4037 if (!do_tests(get_filename(f->filename, filename, temp, sizeof(filename)), vars, &inc_data) && data->stop_after_include_error)
4038 {
4039 data->pass = data->prev_pass = 0;
4040 return (0);
4041 }
4042 }
4043 else
4044 {
4045 print_fatal_error(data, "Missing INCLUDE-IF-DEFINED name or filename on line %d of \"%s\".", f->linenum, f->filename);
4046 return (0);
4047 }
4048
4049 data->show_header = 1;
4050 }
4051 else if (!strcmp(token, "INCLUDE-IF-NOT-DEFINED"))
4052 {
4053 /*
4054 * INCLUDE-IF-NOT-DEFINED name "filename"
4055 * INCLUDE-IF-NOT-DEFINED name <filename>
4056 */
4057
4058 if (_ippFileReadToken(f, name, sizeof(name)) && _ippFileReadToken(f, temp, sizeof(temp)))
4059 {
4060 /*
4061 * Map the filename to and then run the tests...
4062 */
4063
4064 _cups_testdata_t inc_data; /* Data for included file */
4065 char filename[1024]; /* Mapped filename */
4066
4067 memcpy(&inc_data, data, sizeof(inc_data));
4068 inc_data.http = NULL;
4069 inc_data.pass = 1;
4070 inc_data.prev_pass = 1;
4071 inc_data.show_header = 1;
4072
4073 if (!do_tests(get_filename(f->filename, filename, temp, sizeof(filename)), vars, &inc_data) && data->stop_after_include_error)
4074 {
4075 data->pass = data->prev_pass = 0;
4076 return (0);
4077 }
4078 }
4079 else
4080 {
4081 print_fatal_error(data, "Missing INCLUDE-IF-NOT-DEFINED name or filename on line %d of \"%s\".", f->linenum, f->filename);
4082 return (0);
4083 }
4084
4085 data->show_header = 1;
4086 }
4087 else if (!strcmp(token, "SKIP-IF-DEFINED"))
4088 {
4089 /*
4090 * SKIP-IF-DEFINED variable
4091 */
4092
4093 if (_ippFileReadToken(f, name, sizeof(name)))
4094 {
4095 if (_ippVarsGet(vars, name))
4096 data->skip_test = 1;
4097 }
4098 else
4099 {
4100 print_fatal_error(data, "Missing SKIP-IF-DEFINED variable on line %d of \"%s\".", f->linenum, f->filename);
4101 return (0);
4102 }
4103 }
4104 else if (!strcmp(token, "SKIP-IF-NOT-DEFINED"))
4105 {
4106 /*
4107 * SKIP-IF-NOT-DEFINED variable
4108 */
4109
4110 if (_ippFileReadToken(f, name, sizeof(name)))
4111 {
4112 if (!_ippVarsGet(vars, name))
4113 data->skip_test = 1;
4114 }
4115 else
4116 {
4117 print_fatal_error(data, "Missing SKIP-IF-NOT-DEFINED variable on line %d of \"%s\".", f->linenum, f->filename);
4118 return (0);
4119 }
4120 }
4121 else if (!strcmp(token, "STOP-AFTER-INCLUDE-ERROR"))
4122 {
4123 /*
4124 * STOP-AFTER-INCLUDE-ERROR yes
4125 * STOP-AFTER-INCLUDE-ERROR no
4126 */
4127
4128 if (_ippFileReadToken(f, temp, sizeof(temp)) && (!_cups_strcasecmp(temp, "yes") || !_cups_strcasecmp(temp, "no")))
4129 {
4130 data->stop_after_include_error = !_cups_strcasecmp(temp, "yes");
4131 }
4132 else
4133 {
4134 print_fatal_error(data, "Missing STOP-AFTER-INCLUDE-ERROR value on line %d of \"%s\".", f->linenum, f->filename);
4135 return (0);
4136 }
4137 }
4138 else if (!strcmp(token, "TRANSFER"))
4139 {
4140 /*
4141 * TRANSFER auto
4142 * TRANSFER chunked
4143 * TRANSFER length
4144 */
4145
4146 if (_ippFileReadToken(f, temp, sizeof(temp)))
4147 {
4148 if (!strcmp(temp, "auto"))
4149 data->def_transfer = _CUPS_TRANSFER_AUTO;
4150 else if (!strcmp(temp, "chunked"))
4151 data->def_transfer = _CUPS_TRANSFER_CHUNKED;
4152 else if (!strcmp(temp, "length"))
4153 data->def_transfer = _CUPS_TRANSFER_LENGTH;
4154 else
4155 {
4156 print_fatal_error(data, "Bad TRANSFER value \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
4157 return (0);
4158 }
4159 }
4160 else
4161 {
4162 print_fatal_error(data, "Missing TRANSFER value on line %d of \"%s\".", f->linenum, f->filename);
4163 return (0);
4164 }
4165 }
4166 else if (!strcmp(token, "VERSION"))
4167 {
4168 if (_ippFileReadToken(f, temp, sizeof(temp)))
4169 {
4170 if (!strcmp(temp, "1.0"))
4171 data->def_version = 10;
4172 else if (!strcmp(temp, "1.1"))
4173 data->def_version = 11;
4174 else if (!strcmp(temp, "2.0"))
4175 data->def_version = 20;
4176 else if (!strcmp(temp, "2.1"))
4177 data->def_version = 21;
4178 else if (!strcmp(temp, "2.2"))
4179 data->def_version = 22;
4180 else
4181 {
4182 print_fatal_error(data, "Bad VERSION \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
4183 return (0);
4184 }
4185 }
4186 else
4187 {
4188 print_fatal_error(data, "Missing VERSION number on line %d of \"%s\".", f->linenum, f->filename);
4189 return (0);
4190 }
4191 }
4192 else
4193 {
4194 print_fatal_error(data, "Unexpected token %s seen on line %d of \"%s\".", token, f->linenum, f->filename);
4195 return (0);
4196 }
4197 }
4198
4199 return (1);
4200 }
4201
4202
4203 /*
4204 * 'usage()' - Show program usage.
4205 */
4206
4207 static void
4208 usage(void)
4209 {
4210 _cupsLangPuts(stderr, _("Usage: ipptool [options] URI filename [ ... filenameN ]"));
4211 _cupsLangPuts(stderr, _("Options:"));
4212 _cupsLangPuts(stderr, _(" --help Show help."));
4213 _cupsLangPuts(stderr, _(" --ippserver filename Produce ippserver attribute file."));
4214 _cupsLangPuts(stderr, _(" --stop-after-include-error\n"
4215 " Stop tests after a failed INCLUDE."));
4216 _cupsLangPuts(stderr, _(" --version Show version."));
4217 _cupsLangPuts(stderr, _(" -4 Connect using IPv4."));
4218 _cupsLangPuts(stderr, _(" -6 Connect using IPv6."));
4219 _cupsLangPuts(stderr, _(" -C Send requests using "
4220 "chunking (default)."));
4221 _cupsLangPuts(stderr, _(" -E Test with encryption using HTTP Upgrade to TLS."));
4222 _cupsLangPuts(stderr, _(" -I Ignore errors."));
4223 _cupsLangPuts(stderr, _(" -L Send requests using content-length."));
4224 _cupsLangPuts(stderr, _(" -P filename.plist Produce XML plist to a file and test report to standard output."));
4225 _cupsLangPuts(stderr, _(" -S Test with encryption using HTTPS."));
4226 _cupsLangPuts(stderr, _(" -T seconds Set the receive/send timeout in seconds."));
4227 _cupsLangPuts(stderr, _(" -V version Set default IPP version."));
4228 _cupsLangPuts(stderr, _(" -X Produce XML plist instead of plain text."));
4229 _cupsLangPuts(stderr, _(" -c Produce CSV output."));
4230 _cupsLangPuts(stderr, _(" -d name=value Set named variable to value."));
4231 _cupsLangPuts(stderr, _(" -f filename Set default request filename."));
4232 _cupsLangPuts(stderr, _(" -h Validate HTTP response headers."));
4233 _cupsLangPuts(stderr, _(" -i seconds Repeat the last file with the given time interval."));
4234 _cupsLangPuts(stderr, _(" -l Produce plain text output."));
4235 _cupsLangPuts(stderr, _(" -n count Repeat the last file the given number of times."));
4236 _cupsLangPuts(stderr, _(" -q Run silently."));
4237 _cupsLangPuts(stderr, _(" -t Produce a test report."));
4238 _cupsLangPuts(stderr, _(" -v Be verbose."));
4239
4240 exit(1);
4241 }
4242
4243
4244 /*
4245 * 'validate_attr()' - Determine whether an attribute is valid.
4246 */
4247
4248 static int /* O - 1 if valid, 0 otherwise */
4249 validate_attr(_cups_testdata_t *data, /* I - Test data */
4250 cups_array_t *errors, /* I - Errors array */
4251 ipp_attribute_t *attr) /* I - Attribute to validate */
4252 {
4253 int i; /* Looping var */
4254 char scheme[64], /* Scheme from URI */
4255 userpass[256], /* Username/password from URI */
4256 hostname[256], /* Hostname from URI */
4257 resource[1024]; /* Resource from URI */
4258 int port, /* Port number from URI */
4259 uri_status, /* URI separation status */
4260 valid = 1; /* Is the attribute valid? */
4261 const char *ptr; /* Pointer into string */
4262 ipp_attribute_t *colattr; /* Collection attribute */
4263 regex_t re; /* Regular expression */
4264 ipp_uchar_t *date; /* Current date value */
4265
4266
4267 /*
4268 * Skip separators.
4269 */
4270
4271 if (!attr->name)
4272 return (1);
4273
4274 /*
4275 * Validate the attribute name.
4276 */
4277
4278 for (ptr = attr->name; *ptr; ptr ++)
4279 if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' && *ptr != '_')
4280 break;
4281
4282 if (*ptr || ptr == attr->name)
4283 {
4284 valid = 0;
4285
4286 add_stringf(data->errors,
4287 "\"%s\": Bad attribute name - invalid character "
4288 "(RFC 2911 section 4.1.3).", attr->name);
4289 }
4290
4291 if ((ptr - attr->name) > 255)
4292 {
4293 valid = 0;
4294
4295 add_stringf(data->errors,
4296 "\"%s\": Bad attribute name - bad length "
4297 "(RFC 2911 section 4.1.3).", attr->name);
4298 }
4299
4300 switch (attr->value_tag)
4301 {
4302 case IPP_TAG_INTEGER :
4303 break;
4304
4305 case IPP_TAG_BOOLEAN :
4306 for (i = 0; i < attr->num_values; i ++)
4307 {
4308 if (attr->values[i].boolean != 0 &&
4309 attr->values[i].boolean != 1)
4310 {
4311 valid = 0;
4312
4313 add_stringf(data->errors,
4314 "\"%s\": Bad boolen value %d "
4315 "(RFC 2911 section 4.1.11).", attr->name,
4316 attr->values[i].boolean);
4317 }
4318 }
4319 break;
4320
4321 case IPP_TAG_ENUM :
4322 for (i = 0; i < attr->num_values; i ++)
4323 {
4324 if (attr->values[i].integer < 1)
4325 {
4326 valid = 0;
4327
4328 add_stringf(data->errors,
4329 "\"%s\": Bad enum value %d - out of range "
4330 "(RFC 2911 section 4.1.4).", attr->name,
4331 attr->values[i].integer);
4332 }
4333 }
4334 break;
4335
4336 case IPP_TAG_STRING :
4337 for (i = 0; i < attr->num_values; i ++)
4338 {
4339 if (attr->values[i].unknown.length > IPP_MAX_OCTETSTRING)
4340 {
4341 valid = 0;
4342
4343 add_stringf(data->errors,
4344 "\"%s\": Bad octetString value - bad length %d "
4345 "(RFC 2911 section 4.1.10).", attr->name,
4346 attr->values[i].unknown.length);
4347 }
4348 }
4349 break;
4350
4351 case IPP_TAG_DATE :
4352 for (i = 0; i < attr->num_values; i ++)
4353 {
4354 date = attr->values[i].date;
4355
4356 if (date[2] < 1 || date[2] > 12)
4357 {
4358 valid = 0;
4359
4360 add_stringf(data->errors,
4361 "\"%s\": Bad dateTime month %u "
4362 "(RFC 2911 section 4.1.14).", attr->name, date[2]);
4363 }
4364
4365 if (date[3] < 1 || date[3] > 31)
4366 {
4367 valid = 0;
4368
4369 add_stringf(data->errors,
4370 "\"%s\": Bad dateTime day %u "
4371 "(RFC 2911 section 4.1.14).", attr->name, date[3]);
4372 }
4373
4374 if (date[4] > 23)
4375 {
4376 valid = 0;
4377
4378 add_stringf(data->errors,
4379 "\"%s\": Bad dateTime hours %u "
4380 "(RFC 2911 section 4.1.14).", attr->name, date[4]);
4381 }
4382
4383 if (date[5] > 59)
4384 {
4385 valid = 0;
4386
4387 add_stringf(data->errors,
4388 "\"%s\": Bad dateTime minutes %u "
4389 "(RFC 2911 section 4.1.14).", attr->name, date[5]);
4390 }
4391
4392 if (date[6] > 60)
4393 {
4394 valid = 0;
4395
4396 add_stringf(data->errors,
4397 "\"%s\": Bad dateTime seconds %u "
4398 "(RFC 2911 section 4.1.14).", attr->name, date[6]);
4399 }
4400
4401 if (date[7] > 9)
4402 {
4403 valid = 0;
4404
4405 add_stringf(data->errors,
4406 "\"%s\": Bad dateTime deciseconds %u "
4407 "(RFC 2911 section 4.1.14).", attr->name, date[7]);
4408 }
4409
4410 if (date[8] != '-' && date[8] != '+')
4411 {
4412 valid = 0;
4413
4414 add_stringf(data->errors,
4415 "\"%s\": Bad dateTime UTC sign '%c' "
4416 "(RFC 2911 section 4.1.14).", attr->name, date[8]);
4417 }
4418
4419 if (date[9] > 11)
4420 {
4421 valid = 0;
4422
4423 add_stringf(data->errors,
4424 "\"%s\": Bad dateTime UTC hours %u "
4425 "(RFC 2911 section 4.1.14).", attr->name, date[9]);
4426 }
4427
4428 if (date[10] > 59)
4429 {
4430 valid = 0;
4431
4432 add_stringf(data->errors,
4433 "\"%s\": Bad dateTime UTC minutes %u "
4434 "(RFC 2911 section 4.1.14).", attr->name, date[10]);
4435 }
4436 }
4437 break;
4438
4439 case IPP_TAG_RESOLUTION :
4440 for (i = 0; i < attr->num_values; i ++)
4441 {
4442 if (attr->values[i].resolution.xres <= 0)
4443 {
4444 valid = 0;
4445
4446 add_stringf(data->errors,
4447 "\"%s\": Bad resolution value %dx%d%s - cross "
4448 "feed resolution must be positive "
4449 "(RFC 2911 section 4.1.15).", attr->name,
4450 attr->values[i].resolution.xres,
4451 attr->values[i].resolution.yres,
4452 attr->values[i].resolution.units ==
4453 IPP_RES_PER_INCH ? "dpi" :
4454 attr->values[i].resolution.units ==
4455 IPP_RES_PER_CM ? "dpcm" : "unknown");
4456 }
4457
4458 if (attr->values[i].resolution.yres <= 0)
4459 {
4460 valid = 0;
4461
4462 add_stringf(data->errors,
4463 "\"%s\": Bad resolution value %dx%d%s - feed "
4464 "resolution must be positive "
4465 "(RFC 2911 section 4.1.15).", attr->name,
4466 attr->values[i].resolution.xres,
4467 attr->values[i].resolution.yres,
4468 attr->values[i].resolution.units ==
4469 IPP_RES_PER_INCH ? "dpi" :
4470 attr->values[i].resolution.units ==
4471 IPP_RES_PER_CM ? "dpcm" : "unknown");
4472 }
4473
4474 if (attr->values[i].resolution.units != IPP_RES_PER_INCH &&
4475 attr->values[i].resolution.units != IPP_RES_PER_CM)
4476 {
4477 valid = 0;
4478
4479 add_stringf(data->errors,
4480 "\"%s\": Bad resolution value %dx%d%s - bad "
4481 "units value (RFC 2911 section 4.1.15).",
4482 attr->name, attr->values[i].resolution.xres,
4483 attr->values[i].resolution.yres,
4484 attr->values[i].resolution.units ==
4485 IPP_RES_PER_INCH ? "dpi" :
4486 attr->values[i].resolution.units ==
4487 IPP_RES_PER_CM ? "dpcm" : "unknown");
4488 }
4489 }
4490 break;
4491
4492 case IPP_TAG_RANGE :
4493 for (i = 0; i < attr->num_values; i ++)
4494 {
4495 if (attr->values[i].range.lower > attr->values[i].range.upper)
4496 {
4497 valid = 0;
4498
4499 add_stringf(data->errors,
4500 "\"%s\": Bad rangeOfInteger value %d-%d - lower "
4501 "greater than upper (RFC 2911 section 4.1.13).",
4502 attr->name, attr->values[i].range.lower,
4503 attr->values[i].range.upper);
4504 }
4505 }
4506 break;
4507
4508 case IPP_TAG_BEGIN_COLLECTION :
4509 for (i = 0; i < attr->num_values; i ++)
4510 {
4511 for (colattr = attr->values[i].collection->attrs;
4512 colattr;
4513 colattr = colattr->next)
4514 {
4515 if (!validate_attr(data, NULL, colattr))
4516 {
4517 valid = 0;
4518 break;
4519 }
4520 }
4521
4522 if (colattr && errors)
4523 {
4524 add_stringf(data->errors, "\"%s\": Bad collection value.", attr->name);
4525
4526 while (colattr)
4527 {
4528 validate_attr(data, errors, colattr);
4529 colattr = colattr->next;
4530 }
4531 }
4532 }
4533 break;
4534
4535 case IPP_TAG_TEXT :
4536 case IPP_TAG_TEXTLANG :
4537 for (i = 0; i < attr->num_values; i ++)
4538 {
4539 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
4540 {
4541 if ((*ptr & 0xe0) == 0xc0)
4542 {
4543 ptr ++;
4544 if ((*ptr & 0xc0) != 0x80)
4545 break;
4546 }
4547 else if ((*ptr & 0xf0) == 0xe0)
4548 {
4549 ptr ++;
4550 if ((*ptr & 0xc0) != 0x80)
4551 break;
4552 ptr ++;
4553 if ((*ptr & 0xc0) != 0x80)
4554 break;
4555 }
4556 else if ((*ptr & 0xf8) == 0xf0)
4557 {
4558 ptr ++;
4559 if ((*ptr & 0xc0) != 0x80)
4560 break;
4561 ptr ++;
4562 if ((*ptr & 0xc0) != 0x80)
4563 break;
4564 ptr ++;
4565 if ((*ptr & 0xc0) != 0x80)
4566 break;
4567 }
4568 else if (*ptr & 0x80)
4569 break;
4570 }
4571
4572 if (*ptr)
4573 {
4574 valid = 0;
4575
4576 add_stringf(data->errors,
4577 "\"%s\": Bad text value \"%s\" - bad UTF-8 "
4578 "sequence (RFC 2911 section 4.1.1).", attr->name,
4579 attr->values[i].string.text);
4580 }
4581
4582 if ((ptr - attr->values[i].string.text) > (IPP_MAX_TEXT - 1))
4583 {
4584 valid = 0;
4585
4586 add_stringf(data->errors,
4587 "\"%s\": Bad text value \"%s\" - bad length %d "
4588 "(RFC 2911 section 4.1.1).", attr->name,
4589 attr->values[i].string.text,
4590 (int)strlen(attr->values[i].string.text));
4591 }
4592 }
4593 break;
4594
4595 case IPP_TAG_NAME :
4596 case IPP_TAG_NAMELANG :
4597 for (i = 0; i < attr->num_values; i ++)
4598 {
4599 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
4600 {
4601 if ((*ptr & 0xe0) == 0xc0)
4602 {
4603 ptr ++;
4604 if ((*ptr & 0xc0) != 0x80)
4605 break;
4606 }
4607 else if ((*ptr & 0xf0) == 0xe0)
4608 {
4609 ptr ++;
4610 if ((*ptr & 0xc0) != 0x80)
4611 break;
4612 ptr ++;
4613 if ((*ptr & 0xc0) != 0x80)
4614 break;
4615 }
4616 else if ((*ptr & 0xf8) == 0xf0)
4617 {
4618 ptr ++;
4619 if ((*ptr & 0xc0) != 0x80)
4620 break;
4621 ptr ++;
4622 if ((*ptr & 0xc0) != 0x80)
4623 break;
4624 ptr ++;
4625 if ((*ptr & 0xc0) != 0x80)
4626 break;
4627 }
4628 else if (*ptr & 0x80)
4629 break;
4630 }
4631
4632 if (*ptr)
4633 {
4634 valid = 0;
4635
4636 add_stringf(data->errors,
4637 "\"%s\": Bad name value \"%s\" - bad UTF-8 "
4638 "sequence (RFC 2911 section 4.1.2).", attr->name,
4639 attr->values[i].string.text);
4640 }
4641
4642 if ((ptr - attr->values[i].string.text) > (IPP_MAX_NAME - 1))
4643 {
4644 valid = 0;
4645
4646 add_stringf(data->errors,
4647 "\"%s\": Bad name value \"%s\" - bad length %d "
4648 "(RFC 2911 section 4.1.2).", attr->name,
4649 attr->values[i].string.text,
4650 (int)strlen(attr->values[i].string.text));
4651 }
4652 }
4653 break;
4654
4655 case IPP_TAG_KEYWORD :
4656 for (i = 0; i < attr->num_values; i ++)
4657 {
4658 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
4659 if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' &&
4660 *ptr != '_')
4661 break;
4662
4663 if (*ptr || ptr == attr->values[i].string.text)
4664 {
4665 valid = 0;
4666
4667 add_stringf(data->errors,
4668 "\"%s\": Bad keyword value \"%s\" - invalid "
4669 "character (RFC 2911 section 4.1.3).",
4670 attr->name, attr->values[i].string.text);
4671 }
4672
4673 if ((ptr - attr->values[i].string.text) > (IPP_MAX_KEYWORD - 1))
4674 {
4675 valid = 0;
4676
4677 add_stringf(data->errors,
4678 "\"%s\": Bad keyword value \"%s\" - bad "
4679 "length %d (RFC 2911 section 4.1.3).",
4680 attr->name, attr->values[i].string.text,
4681 (int)strlen(attr->values[i].string.text));
4682 }
4683 }
4684 break;
4685
4686 case IPP_TAG_URI :
4687 for (i = 0; i < attr->num_values; i ++)
4688 {
4689 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL,
4690 attr->values[i].string.text,
4691 scheme, sizeof(scheme),
4692 userpass, sizeof(userpass),
4693 hostname, sizeof(hostname),
4694 &port, resource, sizeof(resource));
4695
4696 if (uri_status < HTTP_URI_OK)
4697 {
4698 valid = 0;
4699
4700 add_stringf(data->errors,
4701 "\"%s\": Bad URI value \"%s\" - %s "
4702 "(RFC 2911 section 4.1.5).", attr->name,
4703 attr->values[i].string.text,
4704 httpURIStatusString(uri_status));
4705 }
4706
4707 if (strlen(attr->values[i].string.text) > (IPP_MAX_URI - 1))
4708 {
4709 valid = 0;
4710
4711 add_stringf(data->errors,
4712 "\"%s\": Bad URI value \"%s\" - bad length %d "
4713 "(RFC 2911 section 4.1.5).", attr->name,
4714 attr->values[i].string.text,
4715 (int)strlen(attr->values[i].string.text));
4716 }
4717 }
4718 break;
4719
4720 case IPP_TAG_URISCHEME :
4721 for (i = 0; i < attr->num_values; i ++)
4722 {
4723 ptr = attr->values[i].string.text;
4724 if (islower(*ptr & 255))
4725 {
4726 for (ptr ++; *ptr; ptr ++)
4727 if (!islower(*ptr & 255) && !isdigit(*ptr & 255) &&
4728 *ptr != '+' && *ptr != '-' && *ptr != '.')
4729 break;
4730 }
4731
4732 if (*ptr || ptr == attr->values[i].string.text)
4733 {
4734 valid = 0;
4735
4736 add_stringf(data->errors,
4737 "\"%s\": Bad uriScheme value \"%s\" - bad "
4738 "characters (RFC 2911 section 4.1.6).",
4739 attr->name, attr->values[i].string.text);
4740 }
4741
4742 if ((ptr - attr->values[i].string.text) > (IPP_MAX_URISCHEME - 1))
4743 {
4744 valid = 0;
4745
4746 add_stringf(data->errors,
4747 "\"%s\": Bad uriScheme value \"%s\" - bad "
4748 "length %d (RFC 2911 section 4.1.6).",
4749 attr->name, attr->values[i].string.text,
4750 (int)strlen(attr->values[i].string.text));
4751 }
4752 }
4753 break;
4754
4755 case IPP_TAG_CHARSET :
4756 for (i = 0; i < attr->num_values; i ++)
4757 {
4758 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
4759 if (!isprint(*ptr & 255) || isupper(*ptr & 255) ||
4760 isspace(*ptr & 255))
4761 break;
4762
4763 if (*ptr || ptr == attr->values[i].string.text)
4764 {
4765 valid = 0;
4766
4767 add_stringf(data->errors,
4768 "\"%s\": Bad charset value \"%s\" - bad "
4769 "characters (RFC 2911 section 4.1.7).",
4770 attr->name, attr->values[i].string.text);
4771 }
4772
4773 if ((ptr - attr->values[i].string.text) > (IPP_MAX_CHARSET - 1))
4774 {
4775 valid = 0;
4776
4777 add_stringf(data->errors,
4778 "\"%s\": Bad charset value \"%s\" - bad "
4779 "length %d (RFC 2911 section 4.1.7).",
4780 attr->name, attr->values[i].string.text,
4781 (int)strlen(attr->values[i].string.text));
4782 }
4783 }
4784 break;
4785
4786 case IPP_TAG_LANGUAGE :
4787 /*
4788 * The following regular expression is derived from the ABNF for
4789 * language tags in RFC 4646. All I can say is that this is the
4790 * easiest way to check the values...
4791 */
4792
4793 if ((i = regcomp(&re,
4794 "^("
4795 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
4796 /* language */
4797 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
4798 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
4799 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
4800 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
4801 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
4802 "|"
4803 "x(-[a-z0-9]{1,8})+" /* privateuse */
4804 "|"
4805 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
4806 ")$",
4807 REG_NOSUB | REG_EXTENDED)) != 0)
4808 {
4809 char temp[256]; /* Temporary error string */
4810
4811 regerror(i, &re, temp, sizeof(temp));
4812 print_fatal_error(data, "Unable to compile naturalLanguage regular "
4813 "expression: %s.", temp);
4814 break;
4815 }
4816
4817 for (i = 0; i < attr->num_values; i ++)
4818 {
4819 if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
4820 {
4821 valid = 0;
4822
4823 add_stringf(data->errors,
4824 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
4825 "characters (RFC 2911 section 4.1.8).",
4826 attr->name, attr->values[i].string.text);
4827 }
4828
4829 if (strlen(attr->values[i].string.text) > (IPP_MAX_LANGUAGE - 1))
4830 {
4831 valid = 0;
4832
4833 add_stringf(data->errors,
4834 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
4835 "length %d (RFC 2911 section 4.1.8).",
4836 attr->name, attr->values[i].string.text,
4837 (int)strlen(attr->values[i].string.text));
4838 }
4839 }
4840
4841 regfree(&re);
4842 break;
4843
4844 case IPP_TAG_MIMETYPE :
4845 /*
4846 * The following regular expression is derived from the ABNF for
4847 * language tags in RFC 2045 and 4288. All I can say is that this is
4848 * the easiest way to check the values...
4849 */
4850
4851 if ((i = regcomp(&re,
4852 "^"
4853 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
4854 "/"
4855 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
4856 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
4857 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
4858 /* value */
4859 "$",
4860 REG_NOSUB | REG_EXTENDED)) != 0)
4861 {
4862 char temp[256]; /* Temporary error string */
4863
4864 regerror(i, &re, temp, sizeof(temp));
4865 print_fatal_error(data, "Unable to compile mimeMediaType regular "
4866 "expression: %s.", temp);
4867 break;
4868 }
4869
4870 for (i = 0; i < attr->num_values; i ++)
4871 {
4872 if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
4873 {
4874 valid = 0;
4875
4876 add_stringf(data->errors,
4877 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
4878 "characters (RFC 2911 section 4.1.9).",
4879 attr->name, attr->values[i].string.text);
4880 }
4881
4882 if (strlen(attr->values[i].string.text) > (IPP_MAX_MIMETYPE - 1))
4883 {
4884 valid = 0;
4885
4886 add_stringf(data->errors,
4887 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
4888 "length %d (RFC 2911 section 4.1.9).",
4889 attr->name, attr->values[i].string.text,
4890 (int)strlen(attr->values[i].string.text));
4891 }
4892 }
4893
4894 regfree(&re);
4895 break;
4896
4897 default :
4898 break;
4899 }
4900
4901 return (valid);
4902 }
4903
4904
4905 /*
4906 * 'with_flags_string()' - Return the "WITH-xxx" predicate that corresponds to
4907 the flags.
4908 */
4909
4910 static const char * /* O - WITH-xxx string */
4911 with_flags_string(int flags) /* I - WITH flags */
4912 {
4913 if (flags & _CUPS_WITH_ALL)
4914 {
4915 if (flags & _CUPS_WITH_HOSTNAME)
4916 return ("WITH-ALL-HOSTNAMES");
4917 else if (flags & _CUPS_WITH_RESOURCE)
4918 return ("WITH-ALL-RESOURCES");
4919 else if (flags & _CUPS_WITH_SCHEME)
4920 return ("WITH-ALL-SCHEMES");
4921 else
4922 return ("WITH-ALL-VALUES");
4923 }
4924 else if (flags & _CUPS_WITH_HOSTNAME)
4925 return ("WITH-HOSTNAME");
4926 else if (flags & _CUPS_WITH_RESOURCE)
4927 return ("WITH-RESOURCE");
4928 else if (flags & _CUPS_WITH_SCHEME)
4929 return ("WITH-SCHEME");
4930 else
4931 return ("WITH-VALUE");
4932 }
4933
4934
4935 /*
4936 * 'with_value()' - Test a WITH-VALUE predicate.
4937 */
4938
4939 static int /* O - 1 on match, 0 on non-match */
4940 with_value(_cups_testdata_t *data, /* I - Test data */
4941 cups_array_t *errors, /* I - Errors array */
4942 char *value, /* I - Value string */
4943 int flags, /* I - Flags for match */
4944 ipp_attribute_t *attr, /* I - Attribute to compare */
4945 char *matchbuf, /* I - Buffer to hold matching value */
4946 size_t matchlen) /* I - Length of match buffer */
4947 {
4948 int i, /* Looping var */
4949 match; /* Match? */
4950 char temp[1024], /* Temporary value string */
4951 *valptr; /* Pointer into value */
4952
4953
4954 *matchbuf = '\0';
4955 match = (flags & _CUPS_WITH_ALL) ? 1 : 0;
4956
4957 /*
4958 * NULL matches everything.
4959 */
4960
4961 if (!value || !*value)
4962 return (1);
4963
4964 /*
4965 * Compare the value string to the attribute value.
4966 */
4967
4968 switch (attr->value_tag)
4969 {
4970 case IPP_TAG_INTEGER :
4971 case IPP_TAG_ENUM :
4972 for (i = 0; i < attr->num_values; i ++)
4973 {
4974 char op, /* Comparison operator */
4975 *nextptr; /* Next pointer */
4976 int intvalue, /* Integer value */
4977 valmatch = 0; /* Does the current value match? */
4978
4979 valptr = value;
4980
4981 while (isspace(*valptr & 255) || isdigit(*valptr & 255) ||
4982 *valptr == '-' || *valptr == ',' || *valptr == '<' ||
4983 *valptr == '=' || *valptr == '>')
4984 {
4985 op = '=';
4986 while (*valptr && !isdigit(*valptr & 255) && *valptr != '-')
4987 {
4988 if (*valptr == '<' || *valptr == '>' || *valptr == '=')
4989 op = *valptr;
4990 valptr ++;
4991 }
4992
4993 if (!*valptr)
4994 break;
4995
4996 intvalue = (int)strtol(valptr, &nextptr, 0);
4997 if (nextptr == valptr)
4998 break;
4999 valptr = nextptr;
5000
5001 if ((op == '=' && attr->values[i].integer == intvalue) ||
5002 (op == '<' && attr->values[i].integer < intvalue) ||
5003 (op == '>' && attr->values[i].integer > intvalue))
5004 {
5005 if (!matchbuf[0])
5006 snprintf(matchbuf, matchlen, "%d", attr->values[i].integer);
5007
5008 valmatch = 1;
5009 break;
5010 }
5011 }
5012
5013 if (flags & _CUPS_WITH_ALL)
5014 {
5015 if (!valmatch)
5016 {
5017 match = 0;
5018 break;
5019 }
5020 }
5021 else if (valmatch)
5022 {
5023 match = 1;
5024 break;
5025 }
5026 }
5027
5028 if (!match && errors)
5029 {
5030 for (i = 0; i < attr->num_values; i ++)
5031 add_stringf(data->errors, "GOT: %s=%d", attr->name,
5032 attr->values[i].integer);
5033 }
5034 break;
5035
5036 case IPP_TAG_RANGE :
5037 for (i = 0; i < attr->num_values; i ++)
5038 {
5039 char op, /* Comparison operator */
5040 *nextptr; /* Next pointer */
5041 int intvalue, /* Integer value */
5042 valmatch = 0; /* Does the current value match? */
5043
5044 valptr = value;
5045
5046 while (isspace(*valptr & 255) || isdigit(*valptr & 255) ||
5047 *valptr == '-' || *valptr == ',' || *valptr == '<' ||
5048 *valptr == '=' || *valptr == '>')
5049 {
5050 op = '=';
5051 while (*valptr && !isdigit(*valptr & 255) && *valptr != '-')
5052 {
5053 if (*valptr == '<' || *valptr == '>' || *valptr == '=')
5054 op = *valptr;
5055 valptr ++;
5056 }
5057
5058 if (!*valptr)
5059 break;
5060
5061 intvalue = (int)strtol(valptr, &nextptr, 0);
5062 if (nextptr == valptr)
5063 break;
5064 valptr = nextptr;
5065
5066 if ((op == '=' && (attr->values[i].range.lower == intvalue ||
5067 attr->values[i].range.upper == intvalue)) ||
5068 (op == '<' && attr->values[i].range.upper < intvalue) ||
5069 (op == '>' && attr->values[i].range.upper > intvalue))
5070 {
5071 if (!matchbuf[0])
5072 snprintf(matchbuf, matchlen, "%d-%d",
5073 attr->values[0].range.lower,
5074 attr->values[0].range.upper);
5075
5076 valmatch = 1;
5077 break;
5078 }
5079 }
5080
5081 if (flags & _CUPS_WITH_ALL)
5082 {
5083 if (!valmatch)
5084 {
5085 match = 0;
5086 break;
5087 }
5088 }
5089 else if (valmatch)
5090 {
5091 match = 1;
5092 break;
5093 }
5094 }
5095
5096 if (!match && errors)
5097 {
5098 for (i = 0; i < attr->num_values; i ++)
5099 add_stringf(data->errors, "GOT: %s=%d-%d", attr->name,
5100 attr->values[i].range.lower,
5101 attr->values[i].range.upper);
5102 }
5103 break;
5104
5105 case IPP_TAG_BOOLEAN :
5106 for (i = 0; i < attr->num_values; i ++)
5107 {
5108 if ((!strcmp(value, "true")) == attr->values[i].boolean)
5109 {
5110 if (!matchbuf[0])
5111 strlcpy(matchbuf, value, matchlen);
5112
5113 if (!(flags & _CUPS_WITH_ALL))
5114 {
5115 match = 1;
5116 break;
5117 }
5118 }
5119 else if (flags & _CUPS_WITH_ALL)
5120 {
5121 match = 0;
5122 break;
5123 }
5124 }
5125
5126 if (!match && errors)
5127 {
5128 for (i = 0; i < attr->num_values; i ++)
5129 add_stringf(data->errors, "GOT: %s=%s", attr->name,
5130 attr->values[i].boolean ? "true" : "false");
5131 }
5132 break;
5133
5134 case IPP_TAG_RESOLUTION :
5135 for (i = 0; i < attr->num_values; i ++)
5136 {
5137 if (attr->values[i].resolution.xres ==
5138 attr->values[i].resolution.yres)
5139 snprintf(temp, sizeof(temp), "%d%s",
5140 attr->values[i].resolution.xres,
5141 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
5142 "dpi" : "dpcm");
5143 else
5144 snprintf(temp, sizeof(temp), "%dx%d%s",
5145 attr->values[i].resolution.xres,
5146 attr->values[i].resolution.yres,
5147 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
5148 "dpi" : "dpcm");
5149
5150 if (!strcmp(value, temp))
5151 {
5152 if (!matchbuf[0])
5153 strlcpy(matchbuf, value, matchlen);
5154
5155 if (!(flags & _CUPS_WITH_ALL))
5156 {
5157 match = 1;
5158 break;
5159 }
5160 }
5161 else if (flags & _CUPS_WITH_ALL)
5162 {
5163 match = 0;
5164 break;
5165 }
5166 }
5167
5168 if (!match && errors)
5169 {
5170 for (i = 0; i < attr->num_values; i ++)
5171 {
5172 if (attr->values[i].resolution.xres ==
5173 attr->values[i].resolution.yres)
5174 snprintf(temp, sizeof(temp), "%d%s",
5175 attr->values[i].resolution.xres,
5176 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
5177 "dpi" : "dpcm");
5178 else
5179 snprintf(temp, sizeof(temp), "%dx%d%s",
5180 attr->values[i].resolution.xres,
5181 attr->values[i].resolution.yres,
5182 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
5183 "dpi" : "dpcm");
5184
5185 if (strcmp(value, temp))
5186 add_stringf(data->errors, "GOT: %s=%s", attr->name, temp);
5187 }
5188 }
5189 break;
5190
5191 case IPP_TAG_NOVALUE :
5192 case IPP_TAG_UNKNOWN :
5193 return (1);
5194
5195 case IPP_TAG_CHARSET :
5196 case IPP_TAG_KEYWORD :
5197 case IPP_TAG_LANGUAGE :
5198 case IPP_TAG_MIMETYPE :
5199 case IPP_TAG_NAME :
5200 case IPP_TAG_NAMELANG :
5201 case IPP_TAG_TEXT :
5202 case IPP_TAG_TEXTLANG :
5203 case IPP_TAG_URI :
5204 case IPP_TAG_URISCHEME :
5205 if (flags & _CUPS_WITH_REGEX)
5206 {
5207 /*
5208 * Value is an extended, case-sensitive POSIX regular expression...
5209 */
5210
5211 regex_t re; /* Regular expression */
5212
5213 if ((i = regcomp(&re, value, REG_EXTENDED | REG_NOSUB)) != 0)
5214 {
5215 regerror(i, &re, temp, sizeof(temp));
5216
5217 print_fatal_error(data, "Unable to compile WITH-VALUE regular expression "
5218 "\"%s\" - %s", value, temp);
5219 return (0);
5220 }
5221
5222 /*
5223 * See if ALL of the values match the given regular expression.
5224 */
5225
5226 for (i = 0; i < attr->num_values; i ++)
5227 {
5228 if (!regexec(&re, get_string(attr, i, flags, temp, sizeof(temp)),
5229 0, NULL, 0))
5230 {
5231 if (!matchbuf[0])
5232 strlcpy(matchbuf,
5233 get_string(attr, i, flags, temp, sizeof(temp)),
5234 matchlen);
5235
5236 if (!(flags & _CUPS_WITH_ALL))
5237 {
5238 match = 1;
5239 break;
5240 }
5241 }
5242 else if (flags & _CUPS_WITH_ALL)
5243 {
5244 match = 0;
5245 break;
5246 }
5247 }
5248
5249 regfree(&re);
5250 }
5251 else if (ippGetValueTag(attr) == IPP_TAG_URI && !(flags & (_CUPS_WITH_SCHEME | _CUPS_WITH_HOSTNAME | _CUPS_WITH_RESOURCE)))
5252 {
5253 /*
5254 * Value is a literal URI string, see if the value(s) match...
5255 */
5256
5257 for (i = 0; i < attr->num_values; i ++)
5258 {
5259 if (!compare_uris(value, get_string(attr, i, flags, temp, sizeof(temp))))
5260 {
5261 if (!matchbuf[0])
5262 strlcpy(matchbuf,
5263 get_string(attr, i, flags, temp, sizeof(temp)),
5264 matchlen);
5265
5266 if (!(flags & _CUPS_WITH_ALL))
5267 {
5268 match = 1;
5269 break;
5270 }
5271 }
5272 else if (flags & _CUPS_WITH_ALL)
5273 {
5274 match = 0;
5275 break;
5276 }
5277 }
5278 }
5279 else
5280 {
5281 /*
5282 * Value is a literal string, see if the value(s) match...
5283 */
5284
5285 for (i = 0; i < attr->num_values; i ++)
5286 {
5287 int result;
5288
5289 switch (ippGetValueTag(attr))
5290 {
5291 case IPP_TAG_URI :
5292 /*
5293 * Some URI components are case-sensitive, some not...
5294 */
5295
5296 if (flags & (_CUPS_WITH_SCHEME | _CUPS_WITH_HOSTNAME))
5297 result = _cups_strcasecmp(value, get_string(attr, i, flags, temp, sizeof(temp)));
5298 else
5299 result = strcmp(value, get_string(attr, i, flags, temp, sizeof(temp)));
5300 break;
5301
5302 case IPP_TAG_MIMETYPE :
5303 case IPP_TAG_NAME :
5304 case IPP_TAG_NAMELANG :
5305 case IPP_TAG_TEXT :
5306 case IPP_TAG_TEXTLANG :
5307 /*
5308 * mimeMediaType, nameWithoutLanguage, nameWithLanguage,
5309 * textWithoutLanguage, and textWithLanguage are defined to
5310 * be case-insensitive strings...
5311 */
5312
5313 result = _cups_strcasecmp(value, get_string(attr, i, flags, temp, sizeof(temp)));
5314 break;
5315
5316 default :
5317 /*
5318 * Other string syntaxes are defined as lowercased so we use
5319 * case-sensitive comparisons to catch problems...
5320 */
5321
5322 result = strcmp(value, get_string(attr, i, flags, temp, sizeof(temp)));
5323 break;
5324 }
5325
5326 if (!result)
5327 {
5328 if (!matchbuf[0])
5329 strlcpy(matchbuf,
5330 get_string(attr, i, flags, temp, sizeof(temp)),
5331 matchlen);
5332
5333 if (!(flags & _CUPS_WITH_ALL))
5334 {
5335 match = 1;
5336 break;
5337 }
5338 }
5339 else if (flags & _CUPS_WITH_ALL)
5340 {
5341 match = 0;
5342 break;
5343 }
5344 }
5345 }
5346
5347 if (!match && errors)
5348 {
5349 for (i = 0; i < attr->num_values; i ++)
5350 add_stringf(data->errors, "GOT: %s=\"%s\"", attr->name,
5351 attr->values[i].string.text);
5352 }
5353 break;
5354
5355 default :
5356 break;
5357 }
5358
5359 return (match);
5360 }
5361
5362
5363 /*
5364 * 'with_value_from()' - Test a WITH-VALUE-FROM predicate.
5365 */
5366
5367 static int /* O - 1 on match, 0 on non-match */
5368 with_value_from(
5369 cups_array_t *errors, /* I - Errors array */
5370 ipp_attribute_t *fromattr, /* I - "From" attribute */
5371 ipp_attribute_t *attr, /* I - Attribute to compare */
5372 char *matchbuf, /* I - Buffer to hold matching value */
5373 size_t matchlen) /* I - Length of match buffer */
5374 {
5375 int i, j, /* Looping vars */
5376 count = ippGetCount(attr), /* Number of attribute values */
5377 match = 1; /* Match? */
5378
5379
5380 *matchbuf = '\0';
5381
5382 /*
5383 * Compare the from value(s) to the attribute value(s)...
5384 */
5385
5386 switch (ippGetValueTag(attr))
5387 {
5388 case IPP_TAG_INTEGER :
5389 if (ippGetValueTag(fromattr) != IPP_TAG_INTEGER && ippGetValueTag(fromattr) != IPP_TAG_RANGE)
5390 goto wrong_value_tag;
5391
5392 for (i = 0; i < count; i ++)
5393 {
5394 int value = ippGetInteger(attr, i);
5395 /* Current integer value */
5396
5397 if (ippContainsInteger(fromattr, value))
5398 {
5399 if (!matchbuf[0])
5400 snprintf(matchbuf, matchlen, "%d", value);
5401 }
5402 else
5403 {
5404 add_stringf(errors, "GOT: %s=%d", ippGetName(attr), value);
5405 match = 0;
5406 }
5407 }
5408 break;
5409
5410 case IPP_TAG_ENUM :
5411 if (ippGetValueTag(fromattr) != IPP_TAG_ENUM)
5412 goto wrong_value_tag;
5413
5414 for (i = 0; i < count; i ++)
5415 {
5416 int value = ippGetInteger(attr, i);
5417 /* Current integer value */
5418
5419 if (ippContainsInteger(fromattr, value))
5420 {
5421 if (!matchbuf[0])
5422 snprintf(matchbuf, matchlen, "%d", value);
5423 }
5424 else
5425 {
5426 add_stringf(errors, "GOT: %s=%d", ippGetName(attr), value);
5427 match = 0;
5428 }
5429 }
5430 break;
5431
5432 case IPP_TAG_RESOLUTION :
5433 if (ippGetValueTag(fromattr) != IPP_TAG_RESOLUTION)
5434 goto wrong_value_tag;
5435
5436 for (i = 0; i < count; i ++)
5437 {
5438 int xres, yres;
5439 ipp_res_t units;
5440 int fromcount = ippGetCount(fromattr);
5441 int fromxres, fromyres;
5442 ipp_res_t fromunits;
5443
5444 xres = ippGetResolution(attr, i, &yres, &units);
5445
5446 for (j = 0; j < fromcount; j ++)
5447 {
5448 fromxres = ippGetResolution(fromattr, j, &fromyres, &fromunits);
5449 if (fromxres == xres && fromyres == yres && fromunits == units)
5450 break;
5451 }
5452
5453 if (j < fromcount)
5454 {
5455 if (!matchbuf[0])
5456 {
5457 if (xres == yres)
5458 snprintf(matchbuf, matchlen, "%d%s", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
5459 else
5460 snprintf(matchbuf, matchlen, "%dx%d%s", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
5461 }
5462 }
5463 else
5464 {
5465 if (xres == yres)
5466 add_stringf(errors, "GOT: %s=%d%s", ippGetName(attr), xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
5467 else
5468 add_stringf(errors, "GOT: %s=%dx%d%s", ippGetName(attr), xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
5469
5470 match = 0;
5471 }
5472 }
5473 break;
5474
5475 case IPP_TAG_NOVALUE :
5476 case IPP_TAG_UNKNOWN :
5477 return (1);
5478
5479 case IPP_TAG_CHARSET :
5480 case IPP_TAG_KEYWORD :
5481 case IPP_TAG_LANGUAGE :
5482 case IPP_TAG_MIMETYPE :
5483 case IPP_TAG_NAME :
5484 case IPP_TAG_NAMELANG :
5485 case IPP_TAG_TEXT :
5486 case IPP_TAG_TEXTLANG :
5487 case IPP_TAG_URISCHEME :
5488 for (i = 0; i < count; i ++)
5489 {
5490 const char *value = ippGetString(attr, i, NULL);
5491 /* Current string value */
5492
5493 if (ippContainsString(fromattr, value))
5494 {
5495 if (!matchbuf[0])
5496 strlcpy(matchbuf, value, matchlen);
5497 }
5498 else
5499 {
5500 add_stringf(errors, "GOT: %s='%s'", ippGetName(attr), value);
5501 match = 0;
5502 }
5503 }
5504 break;
5505
5506 case IPP_TAG_URI :
5507 for (i = 0; i < count; i ++)
5508 {
5509 const char *value = ippGetString(attr, i, NULL);
5510 /* Current string value */
5511 int fromcount = ippGetCount(fromattr);
5512
5513 for (j = 0; j < fromcount; j ++)
5514 {
5515 if (!compare_uris(value, ippGetString(fromattr, j, NULL)))
5516 {
5517 if (!matchbuf[0])
5518 strlcpy(matchbuf, value, matchlen);
5519 break;
5520 }
5521 }
5522
5523 if (j >= fromcount)
5524 {
5525 add_stringf(errors, "GOT: %s='%s'", ippGetName(attr), value);
5526 match = 0;
5527 }
5528 }
5529 break;
5530
5531 default :
5532 match = 0;
5533 break;
5534 }
5535
5536 return (match);
5537
5538 /* value tag mismatch between fromattr and attr */
5539 wrong_value_tag :
5540
5541 add_stringf(errors, "GOT: %s OF-TYPE %s", ippGetName(attr), ippTagString(ippGetValueTag(attr)));
5542
5543 return (0);
5544 }