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