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