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