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