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