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