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