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