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