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