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