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