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