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