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