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