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