]> git.ipfire.org Git - thirdparty/cups.git/blob - test/ipptool.c
Merge changes from CUPS 1.6svn-r10056
[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
1503 switch (value)
1504 {
1505 case IPP_TAG_BOOLEAN :
1506 if (!_cups_strcasecmp(token, "true"))
1507 ippAddBoolean(request, group, attr, 1);
1508 else
1509 ippAddBoolean(request, group, attr, atoi(token));
1510 break;
1511
1512 case IPP_TAG_INTEGER :
1513 case IPP_TAG_ENUM :
1514 if (!strchr(token, ','))
1515 ippAddInteger(request, group, value, attr,
1516 strtol(token, &tokenptr, 0));
1517 else
1518 {
1519 int values[100], /* Values */
1520 num_values = 1; /* Number of values */
1521
1522 values[0] = strtol(token, &tokenptr, 10);
1523 while (tokenptr && *tokenptr &&
1524 num_values < (int)(sizeof(values) / sizeof(values[0])))
1525 {
1526 if (*tokenptr == ',')
1527 tokenptr ++;
1528 else if (!isdigit(*tokenptr & 255) && *tokenptr != '-')
1529 break;
1530
1531 values[num_values] = strtol(tokenptr, &tokenptr, 0);
1532 num_values ++;
1533 }
1534
1535 ippAddIntegers(request, group, value, attr, num_values, values);
1536 }
1537
1538 if (!tokenptr || *tokenptr)
1539 {
1540 print_fatal_error("Bad %s value \"%s\" on line %d.",
1541 ippTagString(value), token, linenum);
1542 pass = 0;
1543 goto test_exit;
1544 }
1545 break;
1546
1547 case IPP_TAG_RESOLUTION :
1548 {
1549 int xres, /* X resolution */
1550 yres; /* Y resolution */
1551 char *ptr; /* Pointer into value */
1552
1553 xres = yres = strtol(token, (char **)&ptr, 10);
1554 if (ptr > token && xres > 0)
1555 {
1556 if (*ptr == 'x')
1557 yres = strtol(ptr + 1, (char **)&ptr, 10);
1558 }
1559
1560 if (ptr <= token || xres <= 0 || yres <= 0 || !ptr ||
1561 (_cups_strcasecmp(ptr, "dpi") && _cups_strcasecmp(ptr, "dpc") &&
1562 _cups_strcasecmp(ptr, "other")))
1563 {
1564 print_fatal_error("Bad resolution value \"%s\" on line %d.",
1565 token, linenum);
1566 pass = 0;
1567 goto test_exit;
1568 }
1569
1570 if (!_cups_strcasecmp(ptr, "dpi"))
1571 ippAddResolution(request, group, attr, IPP_RES_PER_INCH,
1572 xres, yres);
1573 else if (!_cups_strcasecmp(ptr, "dpc"))
1574 ippAddResolution(request, group, attr, IPP_RES_PER_CM,
1575 xres, yres);
1576 else
1577 ippAddResolution(request, group, attr, (ipp_res_t)0,
1578 xres, yres);
1579 }
1580 break;
1581
1582 case IPP_TAG_RANGE :
1583 {
1584 int lowers[4], /* Lower value */
1585 uppers[4], /* Upper values */
1586 num_vals; /* Number of values */
1587
1588
1589 num_vals = sscanf(token, "%d-%d,%d-%d,%d-%d,%d-%d",
1590 lowers + 0, uppers + 0,
1591 lowers + 1, uppers + 1,
1592 lowers + 2, uppers + 2,
1593 lowers + 3, uppers + 3);
1594
1595 if ((num_vals & 1) || num_vals == 0)
1596 {
1597 print_fatal_error("Bad rangeOfInteger value \"%s\" on line "
1598 "%d.", token, linenum);
1599 pass = 0;
1600 goto test_exit;
1601 }
1602
1603 ippAddRanges(request, group, attr, num_vals / 2, lowers,
1604 uppers);
1605 }
1606 break;
1607
1608 case IPP_TAG_BEGIN_COLLECTION :
1609 if (!strcmp(token, "{"))
1610 {
1611 ipp_t *col = get_collection(vars, fp, &linenum);
1612 /* Collection value */
1613
1614 if (col)
1615 {
1616 lastcol = ippAddCollection(request, group, attr, col);
1617 ippDelete(col);
1618 }
1619 else
1620 {
1621 pass = 0;
1622 goto test_exit;
1623 }
1624 }
1625 else
1626 {
1627 print_fatal_error("Bad ATTR collection value on line %d.",
1628 linenum);
1629 pass = 0;
1630 goto test_exit;
1631 }
1632 break;
1633
1634 default :
1635 print_fatal_error("Unsupported ATTR value tag %s on line %d.",
1636 ippTagString(value), linenum);
1637 pass = 0;
1638 goto test_exit;
1639
1640 case IPP_TAG_TEXTLANG :
1641 case IPP_TAG_NAMELANG :
1642 case IPP_TAG_TEXT :
1643 case IPP_TAG_NAME :
1644 case IPP_TAG_KEYWORD :
1645 case IPP_TAG_URI :
1646 case IPP_TAG_URISCHEME :
1647 case IPP_TAG_CHARSET :
1648 case IPP_TAG_LANGUAGE :
1649 case IPP_TAG_MIMETYPE :
1650 if (!strchr(token, ','))
1651 ippAddString(request, group, value, attr, NULL, token);
1652 else
1653 {
1654 /*
1655 * Multiple string values...
1656 */
1657
1658 int num_values; /* Number of values */
1659 char *values[100], /* Values */
1660 *ptr; /* Pointer to next value */
1661
1662
1663 values[0] = token;
1664 num_values = 1;
1665
1666 for (ptr = strchr(token, ','); ptr; ptr = strchr(ptr, ','))
1667 {
1668 *ptr++ = '\0';
1669 values[num_values] = ptr;
1670 num_values ++;
1671 }
1672
1673 ippAddStrings(request, group, value, attr, num_values,
1674 NULL, (const char **)values);
1675 }
1676 break;
1677 }
1678 }
1679 else if (!_cups_strcasecmp(token, "FILE"))
1680 {
1681 /*
1682 * File...
1683 */
1684
1685 if (!get_token(fp, temp, sizeof(temp), &linenum))
1686 {
1687 print_fatal_error("Missing FILE filename on line %d.", linenum);
1688 pass = 0;
1689 goto test_exit;
1690 }
1691
1692 expand_variables(vars, token, temp, sizeof(token));
1693 get_filename(testfile, filename, token, sizeof(filename));
1694 }
1695 else if (!_cups_strcasecmp(token, "STATUS"))
1696 {
1697 /*
1698 * Status...
1699 */
1700
1701 if (num_statuses >= (int)(sizeof(statuses) / sizeof(statuses[0])))
1702 {
1703 print_fatal_error("Too many STATUS's on line %d.", linenum);
1704 pass = 0;
1705 goto test_exit;
1706 }
1707
1708 if (!get_token(fp, token, sizeof(token), &linenum))
1709 {
1710 print_fatal_error("Missing STATUS code on line %d.", linenum);
1711 pass = 0;
1712 goto test_exit;
1713 }
1714
1715 if ((statuses[num_statuses].status = ippErrorValue(token))
1716 == (ipp_status_t)-1 &&
1717 (statuses[num_statuses].status = strtol(token, NULL, 0)) == 0)
1718 {
1719 print_fatal_error("Bad STATUS code \"%s\" on line %d.", token,
1720 linenum);
1721 pass = 0;
1722 goto test_exit;
1723 }
1724
1725 last_status = statuses + num_statuses;
1726 num_statuses ++;
1727
1728 last_status->if_defined = NULL;
1729 last_status->if_not_defined = NULL;
1730 last_status->repeat_match = 0;
1731 last_status->repeat_no_match = 0;
1732 }
1733 else if (!_cups_strcasecmp(token, "EXPECT"))
1734 {
1735 /*
1736 * Expected attributes...
1737 */
1738
1739 if (num_expects >= (int)(sizeof(expects) / sizeof(expects[0])))
1740 {
1741 print_fatal_error("Too many EXPECT's on line %d.", linenum);
1742 pass = 0;
1743 goto test_exit;
1744 }
1745
1746 if (!get_token(fp, token, sizeof(token), &linenum))
1747 {
1748 print_fatal_error("Missing EXPECT name on line %d.", linenum);
1749 pass = 0;
1750 goto test_exit;
1751 }
1752
1753 last_expect = expects + num_expects;
1754 num_expects ++;
1755
1756 memset(last_expect, 0, sizeof(_cups_expect_t));
1757
1758 if (token[0] == '!')
1759 {
1760 last_expect->not_expect = 1;
1761 last_expect->name = strdup(token + 1);
1762 }
1763 else if (token[0] == '?')
1764 {
1765 last_expect->optional = 1;
1766 last_expect->name = strdup(token + 1);
1767 }
1768 else
1769 last_expect->name = strdup(token);
1770 }
1771 else if (!_cups_strcasecmp(token, "COUNT"))
1772 {
1773 if (!get_token(fp, token, sizeof(token), &linenum))
1774 {
1775 print_fatal_error("Missing COUNT number on line %d.", linenum);
1776 pass = 0;
1777 goto test_exit;
1778 }
1779
1780 if ((i = atoi(token)) <= 0)
1781 {
1782 print_fatal_error("Bad COUNT \"%s\" on line %d.", token, linenum);
1783 pass = 0;
1784 goto test_exit;
1785 }
1786
1787 if (last_expect)
1788 last_expect->count = i;
1789 else
1790 {
1791 print_fatal_error("COUNT without a preceding EXPECT on line %d.",
1792 linenum);
1793 pass = 0;
1794 goto test_exit;
1795 }
1796 }
1797 else if (!_cups_strcasecmp(token, "DEFINE-MATCH"))
1798 {
1799 if (!get_token(fp, token, sizeof(token), &linenum))
1800 {
1801 print_fatal_error("Missing DEFINE-MATCH variable on line %d.",
1802 linenum);
1803 pass = 0;
1804 goto test_exit;
1805 }
1806
1807 if (last_expect)
1808 last_expect->define_match = strdup(token);
1809 else
1810 {
1811 print_fatal_error("DEFINE-MATCH without a preceding EXPECT on line "
1812 "%d.", linenum);
1813 pass = 0;
1814 goto test_exit;
1815 }
1816 }
1817 else if (!_cups_strcasecmp(token, "DEFINE-NO-MATCH"))
1818 {
1819 if (!get_token(fp, token, sizeof(token), &linenum))
1820 {
1821 print_fatal_error("Missing DEFINE-NO-MATCH variable on line %d.",
1822 linenum);
1823 pass = 0;
1824 goto test_exit;
1825 }
1826
1827 if (last_expect)
1828 last_expect->define_no_match = strdup(token);
1829 else
1830 {
1831 print_fatal_error("DEFINE-NO-MATCH without a preceding EXPECT on "
1832 "line %d.", linenum);
1833 pass = 0;
1834 goto test_exit;
1835 }
1836 }
1837 else if (!_cups_strcasecmp(token, "DEFINE-VALUE"))
1838 {
1839 if (!get_token(fp, token, sizeof(token), &linenum))
1840 {
1841 print_fatal_error("Missing DEFINE-VALUE variable on line %d.",
1842 linenum);
1843 pass = 0;
1844 goto test_exit;
1845 }
1846
1847 if (last_expect)
1848 last_expect->define_value = strdup(token);
1849 else
1850 {
1851 print_fatal_error("DEFINE-VALUE without a preceding EXPECT on line "
1852 "%d.", linenum);
1853 pass = 0;
1854 goto test_exit;
1855 }
1856 }
1857 else if (!_cups_strcasecmp(token, "OF-TYPE"))
1858 {
1859 if (!get_token(fp, token, sizeof(token), &linenum))
1860 {
1861 print_fatal_error("Missing OF-TYPE value tag(s) on line %d.",
1862 linenum);
1863 pass = 0;
1864 goto test_exit;
1865 }
1866
1867 if (last_expect)
1868 last_expect->of_type = strdup(token);
1869 else
1870 {
1871 print_fatal_error("OF-TYPE without a preceding EXPECT on line %d.",
1872 linenum);
1873 pass = 0;
1874 goto test_exit;
1875 }
1876 }
1877 else if (!_cups_strcasecmp(token, "IN-GROUP"))
1878 {
1879 ipp_tag_t in_group; /* IN-GROUP value */
1880
1881
1882 if (!get_token(fp, token, sizeof(token), &linenum))
1883 {
1884 print_fatal_error("Missing IN-GROUP group tag on line %d.", linenum);
1885 pass = 0;
1886 goto test_exit;
1887 }
1888
1889 if ((in_group = ippTagValue(token)) == (ipp_tag_t)-1)
1890 {
1891 }
1892 else if (last_expect)
1893 last_expect->in_group = in_group;
1894 else
1895 {
1896 print_fatal_error("IN-GROUP without a preceding EXPECT on line %d.",
1897 linenum);
1898 pass = 0;
1899 goto test_exit;
1900 }
1901 }
1902 else if (!_cups_strcasecmp(token, "REPEAT-MATCH"))
1903 {
1904 if (last_status)
1905 last_status->repeat_match = 1;
1906 else if (last_expect)
1907 last_expect->repeat_match = 1;
1908 else
1909 {
1910 print_fatal_error("REPEAT-MATCH without a preceding EXPECT or STATUS "
1911 "on line %d.", linenum);
1912 pass = 0;
1913 goto test_exit;
1914 }
1915 }
1916 else if (!_cups_strcasecmp(token, "REPEAT-NO-MATCH"))
1917 {
1918 if (last_status)
1919 last_status->repeat_no_match = 1;
1920 else if (last_expect)
1921 last_expect->repeat_no_match = 1;
1922 else
1923 {
1924 print_fatal_error("REPEAT-NO-MATCH without a preceding EXPECT or "
1925 "STATUS on ine %d.", linenum);
1926 pass = 0;
1927 goto test_exit;
1928 }
1929 }
1930 else if (!_cups_strcasecmp(token, "SAME-COUNT-AS"))
1931 {
1932 if (!get_token(fp, token, sizeof(token), &linenum))
1933 {
1934 print_fatal_error("Missing SAME-COUNT-AS name on line %d.", linenum);
1935 pass = 0;
1936 goto test_exit;
1937 }
1938
1939 if (last_expect)
1940 last_expect->same_count_as = strdup(token);
1941 else
1942 {
1943 print_fatal_error("SAME-COUNT-AS without a preceding EXPECT on line "
1944 "%d.", linenum);
1945 pass = 0;
1946 goto test_exit;
1947 }
1948 }
1949 else if (!_cups_strcasecmp(token, "IF-DEFINED"))
1950 {
1951 if (!get_token(fp, token, sizeof(token), &linenum))
1952 {
1953 print_fatal_error("Missing IF-DEFINED name on line %d.", linenum);
1954 pass = 0;
1955 goto test_exit;
1956 }
1957
1958 if (last_expect)
1959 last_expect->if_defined = strdup(token);
1960 else if (last_status)
1961 last_status->if_defined = strdup(token);
1962 else
1963 {
1964 print_fatal_error("IF-DEFINED without a preceding EXPECT or STATUS "
1965 "on line %d.", linenum);
1966 pass = 0;
1967 goto test_exit;
1968 }
1969 }
1970 else if (!_cups_strcasecmp(token, "IF-NOT-DEFINED"))
1971 {
1972 if (!get_token(fp, token, sizeof(token), &linenum))
1973 {
1974 print_fatal_error("Missing IF-NOT-DEFINED name on line %d.", linenum);
1975 pass = 0;
1976 goto test_exit;
1977 }
1978
1979 if (last_expect)
1980 last_expect->if_not_defined = strdup(token);
1981 else if (last_status)
1982 last_status->if_not_defined = strdup(token);
1983 else
1984 {
1985 print_fatal_error("IF-NOT-DEFINED without a preceding EXPECT or STATUS "
1986 "on line %d.", linenum);
1987 pass = 0;
1988 goto test_exit;
1989 }
1990 }
1991 else if (!_cups_strcasecmp(token, "WITH-VALUE"))
1992 {
1993 if (!get_token(fp, temp, sizeof(temp), &linenum))
1994 {
1995 print_fatal_error("Missing WITH-VALUE value on line %d.", linenum);
1996 pass = 0;
1997 goto test_exit;
1998 }
1999
2000 if (last_expect)
2001 {
2002 /*
2003 * Expand any variables in the value and then save it.
2004 */
2005
2006 expand_variables(vars, token, temp, sizeof(token));
2007
2008 tokenptr = token + strlen(token) - 1;
2009
2010 if (token[0] == '/' && tokenptr > token && *tokenptr == '/')
2011 {
2012 /*
2013 * WITH-VALUE is a POSIX extended regular expression.
2014 */
2015
2016 last_expect->with_value = calloc(1, tokenptr - token);
2017 last_expect->with_regex = 1;
2018
2019 if (last_expect->with_value)
2020 memcpy(last_expect->with_value, token + 1, tokenptr - token - 1);
2021 }
2022 else
2023 {
2024 /*
2025 * WITH-VALUE is a literal value...
2026 */
2027
2028 last_expect->with_value = strdup(token);
2029 }
2030 }
2031 else
2032 {
2033 print_fatal_error("WITH-VALUE without a preceding EXPECT on line %d.",
2034 linenum);
2035 pass = 0;
2036 goto test_exit;
2037 }
2038 }
2039 else if (!_cups_strcasecmp(token, "DISPLAY"))
2040 {
2041 /*
2042 * Display attributes...
2043 */
2044
2045 if (num_displayed >= (int)(sizeof(displayed) / sizeof(displayed[0])))
2046 {
2047 print_fatal_error("Too many DISPLAY's on line %d", linenum);
2048 pass = 0;
2049 goto test_exit;
2050 }
2051
2052 if (!get_token(fp, token, sizeof(token), &linenum))
2053 {
2054 print_fatal_error("Missing DISPLAY name on line %d.", linenum);
2055 pass = 0;
2056 goto test_exit;
2057 }
2058
2059 displayed[num_displayed] = strdup(token);
2060 num_displayed ++;
2061 }
2062 else
2063 {
2064 print_fatal_error("Unexpected token %s seen on line %d.", token,
2065 linenum);
2066 pass = 0;
2067 goto test_exit;
2068 }
2069 }
2070
2071 /*
2072 * Submit the IPP request...
2073 */
2074
2075 TestCount ++;
2076
2077 request->request.op.version[0] = version / 10;
2078 request->request.op.version[1] = version % 10;
2079 request->request.op.operation_id = op;
2080 request->request.op.request_id = request_id;
2081
2082 if (Output == _CUPS_OUTPUT_PLIST)
2083 {
2084 puts("<dict>");
2085 puts("<key>Name</key>");
2086 print_xml_string("string", name);
2087 puts("<key>Operation</key>");
2088 print_xml_string("string", ippOpString(op));
2089 puts("<key>RequestAttributes</key>");
2090 puts("<array>");
2091 if (request->attrs)
2092 {
2093 puts("<dict>");
2094 for (attrptr = request->attrs, group = attrptr->group_tag;
2095 attrptr;
2096 attrptr = attrptr->next)
2097 print_attr(attrptr, &group);
2098 puts("</dict>");
2099 }
2100 puts("</array>");
2101 }
2102 else if (Output == _CUPS_OUTPUT_TEST)
2103 {
2104 if (Verbosity)
2105 {
2106 printf(" %s:\n", ippOpString(op));
2107
2108 for (attrptr = request->attrs; attrptr; attrptr = attrptr->next)
2109 print_attr(attrptr, NULL);
2110 }
2111
2112 printf(" %-68.68s [", name);
2113 fflush(stdout);
2114 }
2115
2116 if ((skip_previous && !prev_pass) || skip_test)
2117 {
2118 SkipCount ++;
2119
2120 ippDelete(request);
2121 request = NULL;
2122
2123 if (Output == _CUPS_OUTPUT_PLIST)
2124 {
2125 puts("<key>Successful</key>");
2126 puts("<true />");
2127 puts("<key>StatusCode</key>");
2128 print_xml_string("string", "skip");
2129 puts("<key>ResponseAttributes</key>");
2130 puts("<dict>");
2131 puts("</dict>");
2132 }
2133 else if (Output == _CUPS_OUTPUT_TEST)
2134 puts("SKIP]");
2135
2136 goto skip_error;
2137 }
2138
2139 do
2140 {
2141 status = HTTP_OK;
2142
2143 if (transfer == _CUPS_TRANSFER_CHUNKED ||
2144 (transfer == _CUPS_TRANSFER_AUTO && filename[0]))
2145 {
2146 /*
2147 * Send request using chunking - a 0 length means "chunk".
2148 */
2149
2150 length = 0;
2151 }
2152 else
2153 {
2154 /*
2155 * Send request using content length...
2156 */
2157
2158 length = ippLength(request);
2159
2160 if (filename[0] && (reqfile = cupsFileOpen(filename, "r")) != NULL)
2161 {
2162 /*
2163 * Read the file to get the uncompressed file size...
2164 */
2165
2166 while ((bytes = cupsFileRead(reqfile, buffer, sizeof(buffer))) > 0)
2167 length += bytes;
2168
2169 cupsFileClose(reqfile);
2170 }
2171 }
2172
2173 /*
2174 * Send the request...
2175 */
2176
2177 response = NULL;
2178 repeat_test = 0;
2179 prev_pass = 1;
2180
2181 if (status != HTTP_ERROR)
2182 {
2183 while (!response && !Cancel && prev_pass)
2184 {
2185 status = cupsSendRequest(http, request, resource, length);
2186
2187 if (!Cancel && status == HTTP_CONTINUE &&
2188 request->state == IPP_DATA && filename[0])
2189 {
2190 if ((reqfile = cupsFileOpen(filename, "r")) != NULL)
2191 {
2192 while (!Cancel &&
2193 (bytes = cupsFileRead(reqfile, buffer,
2194 sizeof(buffer))) > 0)
2195 if ((status = cupsWriteRequestData(http, buffer,
2196 bytes)) != HTTP_CONTINUE)
2197 break;
2198
2199 cupsFileClose(reqfile);
2200 }
2201 else
2202 {
2203 snprintf(buffer, sizeof(buffer), "%s: %s", filename,
2204 strerror(errno));
2205 _cupsSetError(IPP_INTERNAL_ERROR, buffer, 0);
2206
2207 status = HTTP_ERROR;
2208 }
2209 }
2210
2211 /*
2212 * Get the server's response...
2213 */
2214
2215 if (!Cancel && status != HTTP_ERROR)
2216 {
2217 response = cupsGetResponse(http, resource);
2218 status = httpGetStatus(http);
2219 }
2220
2221 if (!Cancel && status == HTTP_ERROR &&
2222 #ifdef WIN32
2223 http->error != WSAETIMEDOUT)
2224 #else
2225 http->error != ETIMEDOUT)
2226 #endif /* WIN32 */
2227 {
2228 if (httpReconnect(http))
2229 prev_pass = 0;
2230 }
2231 else if (status == HTTP_ERROR)
2232 {
2233 if (!Cancel)
2234 httpReconnect(http);
2235
2236 prev_pass = 0;
2237 }
2238 }
2239 }
2240
2241 /*
2242 * Check results of request...
2243 */
2244
2245 if (!response)
2246 prev_pass = pass = 0;
2247 else
2248 {
2249 if (http->version != HTTP_1_1)
2250 prev_pass = pass = 0;
2251
2252 if (response->state != IPP_DATA)
2253 prev_pass = pass = 0;
2254
2255 if (response->request.status.request_id != request_id)
2256 prev_pass = pass = 0;
2257
2258 if (version &&
2259 (response->request.status.version[0] != (version / 10) ||
2260 response->request.status.version[1] != (version % 10)))
2261 prev_pass = pass = 0;
2262
2263 if ((attrptr = ippFindAttribute(response, "job-id",
2264 IPP_TAG_INTEGER)) != NULL)
2265 {
2266 snprintf(temp, sizeof(temp), "%d", attrptr->values[0].integer);
2267 set_variable(vars, "job-id", temp);
2268 }
2269
2270 if ((attrptr = ippFindAttribute(response, "job-uri",
2271 IPP_TAG_URI)) != NULL)
2272 set_variable(vars, "job-uri", attrptr->values[0].string.text);
2273
2274 if ((attrptr = ippFindAttribute(response, "notify-subscription-id",
2275 IPP_TAG_INTEGER)) != NULL)
2276 {
2277 snprintf(temp, sizeof(temp), "%d", attrptr->values[0].integer);
2278 set_variable(vars, "notify-subscription-id", temp);
2279 }
2280
2281 attrptr = response->attrs;
2282 if (!attrptr || !attrptr->name ||
2283 attrptr->value_tag != IPP_TAG_CHARSET ||
2284 attrptr->group_tag != IPP_TAG_OPERATION ||
2285 attrptr->num_values != 1 ||
2286 strcmp(attrptr->name, "attributes-charset"))
2287 prev_pass = pass = 0;
2288
2289 if (attrptr)
2290 {
2291 attrptr = attrptr->next;
2292 if (!attrptr || !attrptr->name ||
2293 attrptr->value_tag != IPP_TAG_LANGUAGE ||
2294 attrptr->group_tag != IPP_TAG_OPERATION ||
2295 attrptr->num_values != 1 ||
2296 strcmp(attrptr->name, "attributes-natural-language"))
2297 prev_pass = pass = 0;
2298 }
2299
2300 if ((attrptr = ippFindAttribute(response, "status-message",
2301 IPP_TAG_ZERO)) != NULL &&
2302 (attrptr->value_tag != IPP_TAG_TEXT ||
2303 attrptr->group_tag != IPP_TAG_OPERATION ||
2304 attrptr->num_values != 1 ||
2305 (attrptr->value_tag == IPP_TAG_TEXT &&
2306 strlen(attrptr->values[0].string.text) > 255)))
2307 prev_pass = pass = 0;
2308
2309 if ((attrptr = ippFindAttribute(response, "detailed-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) > 1023)))
2316 prev_pass = pass = 0;
2317
2318 a = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2319
2320 for (attrptr = response->attrs, group = attrptr->group_tag;
2321 attrptr;
2322 attrptr = attrptr->next)
2323 {
2324 if (attrptr->group_tag != group)
2325 {
2326 cupsArrayClear(a);
2327
2328 switch (attrptr->group_tag)
2329 {
2330 case IPP_TAG_OPERATION :
2331 prev_pass = pass = 0;
2332 break;
2333
2334 case IPP_TAG_UNSUPPORTED_GROUP :
2335 if (group != IPP_TAG_OPERATION)
2336 prev_pass = pass = 0;
2337 break;
2338
2339 case IPP_TAG_JOB :
2340 case IPP_TAG_PRINTER :
2341 if (group != IPP_TAG_OPERATION &&
2342 group != IPP_TAG_UNSUPPORTED_GROUP)
2343 prev_pass = pass = 0;
2344 break;
2345
2346 case IPP_TAG_SUBSCRIPTION :
2347 if (group > attrptr->group_tag &&
2348 group != IPP_TAG_DOCUMENT)
2349 prev_pass = pass = 0;
2350 break;
2351
2352 default :
2353 if (group > attrptr->group_tag)
2354 prev_pass = pass = 0;
2355 break;
2356 }
2357
2358 if (!pass)
2359 break;
2360
2361 group = attrptr->group_tag;
2362 }
2363
2364 if (!validate_attr(attrptr, 0))
2365 {
2366 prev_pass = pass = 0;
2367 break;
2368 }
2369
2370 if (attrptr->name)
2371 {
2372 if (cupsArrayFind(a, attrptr->name))
2373 {
2374 prev_pass = pass = 0;
2375 break;
2376 }
2377
2378 cupsArrayAdd(a, attrptr->name);
2379 }
2380 }
2381
2382 cupsArrayDelete(a);
2383
2384 for (i = 0; i < num_statuses; i ++)
2385 {
2386 if (statuses[i].if_defined &&
2387 !get_variable(vars, statuses[i].if_defined))
2388 continue;
2389
2390 if (statuses[i].if_not_defined &&
2391 get_variable(vars, statuses[i].if_not_defined))
2392 continue;
2393
2394 if (response->request.status.status_code == statuses[i].status)
2395 {
2396 if (statuses[i].repeat_match)
2397 repeat_test = 1;
2398
2399 break;
2400 }
2401 else if (statuses[i].repeat_no_match)
2402 repeat_test = 1;
2403 }
2404
2405 if (i == num_statuses && num_statuses > 0)
2406 prev_pass = pass = 0;
2407
2408 for (i = num_expects, expect = expects; i > 0; i --, expect ++)
2409 {
2410 if (expect->if_defined && !get_variable(vars, expect->if_defined))
2411 continue;
2412
2413 if (expect->if_not_defined &&
2414 get_variable(vars, expect->if_not_defined))
2415 continue;
2416
2417 found = ippFindAttribute(response, expect->name, IPP_TAG_ZERO);
2418
2419 if ((found && expect->not_expect) ||
2420 (!found && !(expect->not_expect || expect->optional)) ||
2421 (found && !expect_matches(expect, found->value_tag)) ||
2422 (found && expect->in_group &&
2423 found->group_tag != expect->in_group))
2424 {
2425 if (expect->define_no_match)
2426 set_variable(vars, expect->define_no_match, "1");
2427 else if (!expect->define_match && !expect->define_value)
2428 prev_pass = pass = 0;
2429
2430 if (expect->repeat_no_match)
2431 repeat_test = 1;
2432
2433 continue;
2434 }
2435
2436 if (found)
2437 _ippAttrString(found, buffer, sizeof(buffer));
2438
2439 if (found &&
2440 !with_value(expect->with_value, expect->with_regex, found, 0,
2441 buffer, sizeof(buffer)))
2442 {
2443 if (expect->define_no_match)
2444 set_variable(vars, expect->define_no_match, "1");
2445 else if (!expect->define_match && !expect->define_value)
2446 prev_pass = pass = 0;
2447
2448 if (expect->repeat_no_match)
2449 repeat_test = 1;
2450
2451 continue;
2452 }
2453
2454 if (found && expect->count > 0 &&
2455 found->num_values != expect->count)
2456 {
2457 if (expect->define_no_match)
2458 set_variable(vars, expect->define_no_match, "1");
2459 else if (!expect->define_match && !expect->define_value)
2460 prev_pass = pass = 0;
2461
2462 if (expect->repeat_no_match)
2463 repeat_test = 1;
2464
2465 continue;
2466 }
2467
2468 if (found && expect->same_count_as)
2469 {
2470 attrptr = ippFindAttribute(response, expect->same_count_as,
2471 IPP_TAG_ZERO);
2472
2473 if (!attrptr || attrptr->num_values != found->num_values)
2474 {
2475 if (expect->define_no_match)
2476 set_variable(vars, expect->define_no_match, "1");
2477 else if (!expect->define_match && !expect->define_value)
2478 prev_pass = pass = 0;
2479
2480 if (expect->repeat_no_match)
2481 repeat_test = 1;
2482
2483 continue;
2484 }
2485 }
2486
2487 if (found && expect->define_match)
2488 set_variable(vars, expect->define_match, "1");
2489
2490 if (found && expect->define_value)
2491 set_variable(vars, expect->define_value, buffer);
2492
2493 if (found && expect->repeat_match)
2494 repeat_test = 1;
2495 }
2496 }
2497
2498 /*
2499 * If we are going to repeat this test, sleep 1 second so we don't flood
2500 * the printer with requests...
2501 */
2502
2503 if (repeat_test)
2504 sleep(1);
2505 }
2506 while (repeat_test);
2507
2508 ippDelete(request);
2509
2510 request = NULL;
2511
2512 if (prev_pass)
2513 PassCount ++;
2514 else
2515 FailCount ++;
2516
2517 if (Output == _CUPS_OUTPUT_PLIST)
2518 {
2519 puts("<key>Successful</key>");
2520 puts(prev_pass ? "<true />" : "<false />");
2521 puts("<key>StatusCode</key>");
2522 print_xml_string("string", ippErrorString(cupsLastError()));
2523 puts("<key>ResponseAttributes</key>");
2524 puts("<array>");
2525 puts("<dict>");
2526 for (attrptr = response ? response->attrs : NULL,
2527 group = attrptr ? attrptr->group_tag : IPP_TAG_ZERO;
2528 attrptr;
2529 attrptr = attrptr->next)
2530 print_attr(attrptr, &group);
2531 puts("</dict>");
2532 puts("</array>");
2533 }
2534 else if (Output == _CUPS_OUTPUT_TEST)
2535 {
2536 puts(prev_pass ? "PASS]" : "FAIL]");
2537
2538 if (Verbosity && response)
2539 {
2540 printf(" RECEIVED: %lu bytes in response\n",
2541 (unsigned long)ippLength(response));
2542 printf(" status-code = %x (%s)\n", cupsLastError(),
2543 ippErrorString(cupsLastError()));
2544
2545 for (attrptr = response->attrs;
2546 attrptr != NULL;
2547 attrptr = attrptr->next)
2548 {
2549 print_attr(attrptr, NULL);
2550 }
2551 }
2552 }
2553 else if (!prev_pass)
2554 fprintf(stderr, "%s\n", cupsLastErrorString());
2555
2556 if (prev_pass && Output >= _CUPS_OUTPUT_LIST && !Verbosity &&
2557 num_displayed > 0)
2558 {
2559 size_t width; /* Length of value */
2560
2561 for (i = 0; i < num_displayed; i ++)
2562 {
2563 widths[i] = strlen(displayed[i]);
2564
2565 for (attrptr = ippFindAttribute(response, displayed[i], IPP_TAG_ZERO);
2566 attrptr;
2567 attrptr = ippFindNextAttribute(response, displayed[i],
2568 IPP_TAG_ZERO))
2569 {
2570 width = _ippAttrString(attrptr, NULL, 0);
2571 if (width > widths[i])
2572 widths[i] = width;
2573 }
2574 }
2575
2576 if (Output == _CUPS_OUTPUT_CSV)
2577 print_csv(NULL, num_displayed, displayed, widths);
2578 else
2579 print_line(NULL, num_displayed, displayed, widths);
2580
2581 attrptr = response->attrs;
2582
2583 while (attrptr)
2584 {
2585 while (attrptr && attrptr->group_tag <= IPP_TAG_OPERATION)
2586 attrptr = attrptr->next;
2587
2588 if (attrptr)
2589 {
2590 if (Output == _CUPS_OUTPUT_CSV)
2591 print_csv(attrptr, num_displayed, displayed, widths);
2592 else
2593 print_line(attrptr, num_displayed, displayed, widths);
2594
2595 while (attrptr && attrptr->group_tag > IPP_TAG_OPERATION)
2596 attrptr = attrptr->next;
2597 }
2598 }
2599 }
2600 else if (!prev_pass)
2601 {
2602 if (Output == _CUPS_OUTPUT_PLIST)
2603 {
2604 puts("<key>Errors</key>");
2605 puts("<array>");
2606 }
2607
2608 if (http->version != HTTP_1_1)
2609 print_test_error("Bad HTTP version (%d.%d)", http->version / 100,
2610 http->version % 100);
2611
2612 if (!response)
2613 print_test_error("IPP request failed with status %s (%s)",
2614 ippErrorString(cupsLastError()),
2615 cupsLastErrorString());
2616 else
2617 {
2618 if (response->state != IPP_DATA)
2619 print_test_error("Missing end-of-attributes-tag in response "
2620 "(RFC 2910 section 3.5.1)");
2621
2622 if (version &&
2623 (response->request.status.version[0] != (version / 10) ||
2624 response->request.status.version[1] != (version % 10)))
2625 print_test_error("Bad version %d.%d in response - expected %d.%d "
2626 "(RFC 2911 section 3.1.8).",
2627 response->request.status.version[0],
2628 response->request.status.version[1],
2629 version / 10, version % 10);
2630
2631 if (response->request.status.request_id != request_id)
2632 print_test_error("Bad request ID %d in response - expected %d "
2633 "(RFC 2911 section 3.1.1)",
2634 response->request.status.request_id, request_id);
2635
2636 attrptr = response->attrs;
2637 if (!attrptr)
2638 print_test_error("Missing first attribute \"attributes-charset "
2639 "(charset)\" in group operation-attributes-tag "
2640 "(RFC 2911 section 3.1.4).");
2641 else
2642 {
2643 if (!attrptr->name ||
2644 attrptr->value_tag != IPP_TAG_CHARSET ||
2645 attrptr->group_tag != IPP_TAG_OPERATION ||
2646 attrptr->num_values != 1 ||
2647 strcmp(attrptr->name, "attributes-charset"))
2648 print_test_error("Bad first attribute \"%s (%s%s)\" in group %s, "
2649 "expected \"attributes-charset (charset)\" in "
2650 "group operation-attributes-tag (RFC 2911 section "
2651 "3.1.4).",
2652 attrptr->name ? attrptr->name : "(null)",
2653 attrptr->num_values > 1 ? "1setOf " : "",
2654 ippTagString(attrptr->value_tag),
2655 ippTagString(attrptr->group_tag));
2656
2657 attrptr = attrptr->next;
2658 if (!attrptr)
2659 print_test_error("Missing second attribute \"attributes-natural-"
2660 "language (naturalLanguage)\" in group "
2661 "operation-attributes-tag (RFC 2911 section "
2662 "3.1.4).");
2663 else if (!attrptr->name ||
2664 attrptr->value_tag != IPP_TAG_LANGUAGE ||
2665 attrptr->group_tag != IPP_TAG_OPERATION ||
2666 attrptr->num_values != 1 ||
2667 strcmp(attrptr->name, "attributes-natural-language"))
2668 print_test_error("Bad first attribute \"%s (%s%s)\" in group %s, "
2669 "expected \"attributes-natural-language "
2670 "(naturalLanguage)\" in group "
2671 "operation-attributes-tag (RFC 2911 section "
2672 "3.1.4).",
2673 attrptr->name ? attrptr->name : "(null)",
2674 attrptr->num_values > 1 ? "1setOf " : "",
2675 ippTagString(attrptr->value_tag),
2676 ippTagString(attrptr->group_tag));
2677 }
2678
2679 if ((attrptr = ippFindAttribute(response, "status-message",
2680 IPP_TAG_ZERO)) != NULL)
2681 {
2682 if (attrptr->value_tag != IPP_TAG_TEXT)
2683 print_test_error("status-message (text(255)) has wrong value tag "
2684 "%s (RFC 2911 section 3.1.6.2).",
2685 ippTagString(attrptr->value_tag));
2686 if (attrptr->group_tag != IPP_TAG_OPERATION)
2687 print_test_error("status-message (text(255)) has wrong group tag "
2688 "%s (RFC 2911 section 3.1.6.2).",
2689 ippTagString(attrptr->group_tag));
2690 if (attrptr->num_values != 1)
2691 print_test_error("status-message (text(255)) has %d values "
2692 "(RFC 2911 section 3.1.6.2).",
2693 attrptr->num_values);
2694 if (attrptr->value_tag == IPP_TAG_TEXT &&
2695 strlen(attrptr->values[0].string.text) > 255)
2696 print_test_error("status-message (text(255)) has bad length %d"
2697 " (RFC 2911 section 3.1.6.2).",
2698 (int)strlen(attrptr->values[0].string.text));
2699 }
2700
2701 if ((attrptr = ippFindAttribute(response, "detailed-status-message",
2702 IPP_TAG_ZERO)) != NULL)
2703 {
2704 if (attrptr->value_tag != IPP_TAG_TEXT)
2705 print_test_error("detailed-status-message (text(MAX)) has wrong "
2706 "value tag %s (RFC 2911 section 3.1.6.3).",
2707 ippTagString(attrptr->value_tag));
2708 if (attrptr->group_tag != IPP_TAG_OPERATION)
2709 print_test_error("detailed-status-message (text(MAX)) has wrong "
2710 "group tag %s (RFC 2911 section 3.1.6.3).",
2711 ippTagString(attrptr->group_tag));
2712 if (attrptr->num_values != 1)
2713 print_test_error("detailed-status-message (text(MAX)) has %d values"
2714 " (RFC 2911 section 3.1.6.3).",
2715 attrptr->num_values);
2716 if (attrptr->value_tag == IPP_TAG_TEXT &&
2717 strlen(attrptr->values[0].string.text) > 1023)
2718 print_test_error("detailed-status-message (text(MAX)) has bad "
2719 "length %d (RFC 2911 section 3.1.6.3).",
2720 (int)strlen(attrptr->values[0].string.text));
2721 }
2722
2723 a = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2724
2725 for (attrptr = response->attrs, group = attrptr->group_tag;
2726 attrptr;
2727 attrptr = attrptr->next)
2728 {
2729 if (attrptr->group_tag != group)
2730 {
2731 cupsArrayClear(a);
2732
2733 switch (attrptr->group_tag)
2734 {
2735 case IPP_TAG_OPERATION :
2736 prev_pass = pass = 0;
2737 break;
2738
2739 case IPP_TAG_UNSUPPORTED_GROUP :
2740 if (group != IPP_TAG_OPERATION)
2741 print_test_error("Attribute groups out of order (%s < %s)",
2742 ippTagString(attrptr->group_tag),
2743 ippTagString(group));
2744 break;
2745
2746 case IPP_TAG_JOB :
2747 case IPP_TAG_PRINTER :
2748 if (group != IPP_TAG_OPERATION &&
2749 group != IPP_TAG_UNSUPPORTED_GROUP)
2750 print_test_error("Attribute groups out of order (%s < %s)",
2751 ippTagString(attrptr->group_tag),
2752 ippTagString(group));
2753 break;
2754
2755 case IPP_TAG_SUBSCRIPTION :
2756 if (group > attrptr->group_tag &&
2757 group != IPP_TAG_DOCUMENT)
2758 print_test_error("Attribute groups out of order (%s < %s)",
2759 ippTagString(attrptr->group_tag),
2760 ippTagString(group));
2761 break;
2762
2763 default :
2764 if (group > attrptr->group_tag)
2765 print_test_error("Attribute groups out of order (%s < %s)",
2766 ippTagString(attrptr->group_tag),
2767 ippTagString(group));
2768 break;
2769 }
2770
2771 group = attrptr->group_tag;
2772 }
2773
2774 validate_attr(attrptr, 1);
2775
2776 if (attrptr->name)
2777 {
2778 if (cupsArrayFind(a, attrptr->name))
2779 print_test_error("Duplicate \"%s\" attribute in %s group",
2780 attrptr->name, ippTagString(group));
2781
2782 cupsArrayAdd(a, attrptr->name);
2783 }
2784 }
2785
2786 cupsArrayDelete(a);
2787
2788 for (i = 0; i < num_statuses; i ++)
2789 {
2790 if (statuses[i].if_defined &&
2791 !get_variable(vars, statuses[i].if_defined))
2792 continue;
2793
2794 if (statuses[i].if_not_defined &&
2795 get_variable(vars, statuses[i].if_not_defined))
2796 continue;
2797
2798 if (response->request.status.status_code == statuses[i].status)
2799 break;
2800 }
2801
2802 if (i == num_statuses && num_statuses > 0)
2803 {
2804 for (i = 0; i < num_statuses; i ++)
2805 {
2806 if (statuses[i].if_defined &&
2807 !get_variable(vars, statuses[i].if_defined))
2808 continue;
2809
2810 if (statuses[i].if_not_defined &&
2811 get_variable(vars, statuses[i].if_not_defined))
2812 continue;
2813
2814 print_test_error("EXPECTED: STATUS %s (got %s)",
2815 ippErrorString(statuses[i].status),
2816 ippErrorString(cupsLastError()));
2817 }
2818
2819 if ((attrptr = ippFindAttribute(response, "status-message",
2820 IPP_TAG_TEXT)) != NULL)
2821 print_test_error("status-message=\"%s\"",
2822 attrptr->values[0].string.text);
2823 }
2824
2825 for (i = num_expects, expect = expects; i > 0; i --, expect ++)
2826 {
2827 if (expect->define_match || expect->define_no_match ||
2828 expect->define_value)
2829 continue;
2830
2831 if (expect->if_defined && !get_variable(vars, expect->if_defined))
2832 continue;
2833
2834 if (expect->if_not_defined &&
2835 get_variable(vars, expect->if_not_defined))
2836 continue;
2837
2838 found = ippFindAttribute(response, expect->name, IPP_TAG_ZERO);
2839
2840 if (found && expect->not_expect)
2841 print_test_error("NOT EXPECTED: %s", expect->name);
2842 else if (!found && !(expect->not_expect || expect->optional))
2843 print_test_error("EXPECTED: %s", expect->name);
2844 else if (found)
2845 {
2846 if (!expect_matches(expect, found->value_tag))
2847 print_test_error("EXPECTED: %s OF-TYPE %s (got %s)",
2848 expect->name, expect->of_type,
2849 ippTagString(found->value_tag));
2850
2851 if (expect->in_group && found->group_tag != expect->in_group)
2852 print_test_error("EXPECTED: %s IN-GROUP %s (got %s).",
2853 expect->name, ippTagString(expect->in_group),
2854 ippTagString(found->group_tag));
2855
2856 if (!with_value(expect->with_value, expect->with_regex, found, 0,
2857 buffer, sizeof(buffer)))
2858 {
2859 if (expect->with_regex)
2860 print_test_error("EXPECTED: %s WITH-VALUE /%s/",
2861 expect->name, expect->with_value);
2862 else
2863 print_test_error("EXPECTED: %s WITH-VALUE \"%s\"",
2864 expect->name, expect->with_value);
2865
2866 with_value(expect->with_value, expect->with_regex, found, 1,
2867 buffer, sizeof(buffer));
2868 }
2869
2870 if (expect->count > 0 && found->num_values != expect->count)
2871 {
2872 print_test_error("EXPECTED: %s COUNT %d (got %d)", expect->name,
2873 expect->count, found->num_values);
2874 }
2875
2876 if (expect->same_count_as)
2877 {
2878 attrptr = ippFindAttribute(response, expect->same_count_as,
2879 IPP_TAG_ZERO);
2880
2881 if (!attrptr)
2882 print_test_error("EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2883 "(not returned)", expect->name,
2884 found->num_values, expect->same_count_as);
2885 else if (attrptr->num_values != found->num_values)
2886 print_test_error("EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2887 "(%d values)", expect->name, found->num_values,
2888 expect->same_count_as, attrptr->num_values);
2889 }
2890 }
2891 }
2892 }
2893
2894 if (Output == _CUPS_OUTPUT_PLIST)
2895 puts("</array>");
2896 }
2897
2898 if (num_displayed > 0 && !Verbosity &&
2899 (Output == _CUPS_OUTPUT_TEST || Output == _CUPS_OUTPUT_PLIST))
2900 {
2901 for (attrptr = response->attrs;
2902 attrptr != NULL;
2903 attrptr = attrptr->next)
2904 {
2905 if (attrptr->name)
2906 {
2907 for (i = 0; i < num_displayed; i ++)
2908 {
2909 if (!strcmp(displayed[i], attrptr->name))
2910 {
2911 print_attr(attrptr, NULL);
2912 break;
2913 }
2914 }
2915 }
2916 }
2917 }
2918
2919 skip_error:
2920
2921 if (Output == _CUPS_OUTPUT_PLIST)
2922 puts("</dict>");
2923
2924 fflush(stdout);
2925
2926 ippDelete(response);
2927 response = NULL;
2928
2929 for (i = 0; i < num_statuses; i ++)
2930 {
2931 if (statuses[i].if_defined)
2932 free(statuses[i].if_defined);
2933 if (statuses[i].if_not_defined)
2934 free(statuses[i].if_not_defined);
2935 }
2936 num_statuses = 0;
2937
2938 for (i = num_expects, expect = expects; i > 0; i --, expect ++)
2939 {
2940 free(expect->name);
2941 if (expect->of_type)
2942 free(expect->of_type);
2943 if (expect->same_count_as)
2944 free(expect->same_count_as);
2945 if (expect->if_defined)
2946 free(expect->if_defined);
2947 if (expect->if_not_defined)
2948 free(expect->if_not_defined);
2949 if (expect->with_value)
2950 free(expect->with_value);
2951 if (expect->define_match)
2952 free(expect->define_match);
2953 if (expect->define_no_match)
2954 free(expect->define_no_match);
2955 if (expect->define_value)
2956 free(expect->define_value);
2957 }
2958 num_expects = 0;
2959
2960 for (i = 0; i < num_displayed; i ++)
2961 free(displayed[i]);
2962 num_displayed = 0;
2963
2964 if (!ignore_errors && !prev_pass)
2965 break;
2966 }
2967
2968 test_exit:
2969
2970 if (fp)
2971 fclose(fp);
2972
2973 httpClose(http);
2974 ippDelete(request);
2975 ippDelete(response);
2976
2977 for (i = 0; i < num_statuses; i ++)
2978 {
2979 if (statuses[i].if_defined)
2980 free(statuses[i].if_defined);
2981 if (statuses[i].if_not_defined)
2982 free(statuses[i].if_not_defined);
2983 }
2984
2985 for (i = num_expects, expect = expects; i > 0; i --, expect ++)
2986 {
2987 free(expect->name);
2988 if (expect->of_type)
2989 free(expect->of_type);
2990 if (expect->same_count_as)
2991 free(expect->same_count_as);
2992 if (expect->if_defined)
2993 free(expect->if_defined);
2994 if (expect->if_not_defined)
2995 free(expect->if_not_defined);
2996 if (expect->with_value)
2997 free(expect->with_value);
2998 if (expect->define_match)
2999 free(expect->define_match);
3000 if (expect->define_no_match)
3001 free(expect->define_no_match);
3002 if (expect->define_value)
3003 free(expect->define_value);
3004 }
3005
3006 for (i = 0; i < num_displayed; i ++)
3007 free(displayed[i]);
3008
3009 return (pass);
3010 }
3011
3012
3013 /*
3014 * 'expand_variables()' - Expand variables in a string.
3015 */
3016
3017 static void
3018 expand_variables(_cups_vars_t *vars, /* I - Variables */
3019 char *dst, /* I - Destination string buffer */
3020 const char *src, /* I - Source string */
3021 size_t dstsize) /* I - Size of destination buffer */
3022 {
3023 char *dstptr, /* Pointer into destination */
3024 *dstend, /* End of destination */
3025 temp[256], /* Temporary string */
3026 *tempptr; /* Pointer into temporary string */
3027 const char *value; /* Value to substitute */
3028
3029
3030 dstptr = dst;
3031 dstend = dst + dstsize - 1;
3032
3033 while (*src && dstptr < dstend)
3034 {
3035 if (*src == '$')
3036 {
3037 /*
3038 * Substitute a string/number...
3039 */
3040
3041 if (!strncmp(src, "$$", 2))
3042 {
3043 value = "$";
3044 src += 2;
3045 }
3046 else if (!strncmp(src, "$ENV[", 5))
3047 {
3048 strlcpy(temp, src + 5, sizeof(temp));
3049
3050 for (tempptr = temp; *tempptr; tempptr ++)
3051 if (*tempptr == ']')
3052 break;
3053
3054 if (*tempptr)
3055 *tempptr++ = '\0';
3056
3057 value = getenv(temp);
3058 src += tempptr - temp + 5;
3059 }
3060 else if (vars)
3061 {
3062 strlcpy(temp, src + 1, sizeof(temp));
3063
3064 for (tempptr = temp; *tempptr; tempptr ++)
3065 if (!isalnum(*tempptr & 255) && *tempptr != '-' && *tempptr != '_')
3066 break;
3067
3068 if (*tempptr)
3069 *tempptr = '\0';
3070
3071 if (!strcmp(temp, "uri"))
3072 value = vars->uri;
3073 else if (!strcmp(temp, "filename"))
3074 value = vars->filename;
3075 else if (!strcmp(temp, "scheme") || !strcmp(temp, "method"))
3076 value = vars->scheme;
3077 else if (!strcmp(temp, "username"))
3078 value = vars->userpass;
3079 else if (!strcmp(temp, "hostname"))
3080 value = vars->hostname;
3081 else if (!strcmp(temp, "port"))
3082 {
3083 snprintf(temp, sizeof(temp), "%d", vars->port);
3084 value = temp;
3085 }
3086 else if (!strcmp(temp, "resource"))
3087 value = vars->resource;
3088 else if (!strcmp(temp, "user"))
3089 value = cupsUser();
3090 else
3091 value = get_variable(vars, temp);
3092
3093 src += tempptr - temp + 1;
3094 }
3095 else
3096 {
3097 value = "$";
3098 src ++;
3099 }
3100
3101 if (value)
3102 {
3103 strlcpy(dstptr, value, dstend - dstptr + 1);
3104 dstptr += strlen(dstptr);
3105 }
3106 }
3107 else
3108 *dstptr++ = *src++;
3109 }
3110
3111 *dstptr = '\0';
3112 }
3113
3114
3115 /*
3116 * 'expect_matches()' - Return true if the tag matches the specification.
3117 */
3118
3119 static int /* O - 1 if matches, 0 otherwise */
3120 expect_matches(
3121 _cups_expect_t *expect, /* I - Expected attribute */
3122 ipp_tag_t value_tag) /* I - Value tag for attribute */
3123 {
3124 int match; /* Match? */
3125 char *of_type, /* Type name to match */
3126 *next, /* Next name to match */
3127 sep; /* Separator character */
3128
3129
3130 /*
3131 * If we don't expect a particular type, return immediately...
3132 */
3133
3134 if (!expect->of_type)
3135 return (1);
3136
3137 /*
3138 * Parse the "of_type" value since the string can contain multiple attribute
3139 * types separated by "," or "|"...
3140 */
3141
3142 for (of_type = expect->of_type, match = 0; !match && *of_type; of_type = next)
3143 {
3144 /*
3145 * Find the next separator, and set it (temporarily) to nul if present.
3146 */
3147
3148 for (next = of_type; *next && *next != '|' && *next != ','; next ++);
3149
3150 if ((sep = *next) != '\0')
3151 *next = '\0';
3152
3153 /*
3154 * Support some meta-types to make it easier to write the test file.
3155 */
3156
3157 if (!strcmp(of_type, "text"))
3158 match = value_tag == IPP_TAG_TEXTLANG || value_tag == IPP_TAG_TEXT;
3159 else if (!strcmp(of_type, "name"))
3160 match = value_tag == IPP_TAG_NAMELANG || value_tag == IPP_TAG_NAME;
3161 else if (!strcmp(of_type, "collection"))
3162 match = value_tag == IPP_TAG_BEGIN_COLLECTION;
3163 else
3164 match = value_tag == ippTagValue(of_type);
3165
3166 /*
3167 * Restore the separator if we have one...
3168 */
3169
3170 if (sep)
3171 *next++ = sep;
3172 }
3173
3174 return (match);
3175 }
3176
3177
3178 /*
3179 * 'get_collection()' - Get a collection value from the current test file.
3180 */
3181
3182 static ipp_t * /* O - Collection value */
3183 get_collection(_cups_vars_t *vars, /* I - Variables */
3184 FILE *fp, /* I - File to read from */
3185 int *linenum) /* IO - Line number */
3186 {
3187 char token[1024], /* Token from file */
3188 temp[1024], /* Temporary string */
3189 attr[128]; /* Attribute name */
3190 ipp_tag_t value; /* Current value type */
3191 ipp_t *col = ippNew(); /* Collection value */
3192 ipp_attribute_t *lastcol = NULL; /* Last collection attribute */
3193
3194
3195 while (get_token(fp, token, sizeof(token), linenum) != NULL)
3196 {
3197 if (!strcmp(token, "}"))
3198 break;
3199 else if (!strcmp(token, "{") && lastcol)
3200 {
3201 /*
3202 * Another collection value
3203 */
3204
3205 ipp_t *subcol = get_collection(vars, fp, linenum);
3206 /* Collection value */
3207
3208 if (subcol)
3209 {
3210 ipp_attribute_t *tempcol; /* Pointer to new buffer */
3211
3212
3213 /*
3214 * Reallocate memory...
3215 */
3216
3217 if ((tempcol = realloc(lastcol, sizeof(ipp_attribute_t) +
3218 (lastcol->num_values + 1) *
3219 sizeof(ipp_value_t))) == NULL)
3220 {
3221 print_fatal_error("Unable to allocate memory on line %d.", *linenum);
3222 goto col_error;
3223 }
3224
3225 if (tempcol != lastcol)
3226 {
3227 /*
3228 * Reset pointers in the list...
3229 */
3230
3231 if (col->prev)
3232 col->prev->next = tempcol;
3233 else
3234 col->attrs = tempcol;
3235
3236 lastcol = col->current = col->last = tempcol;
3237 }
3238
3239 lastcol->values[lastcol->num_values].collection = subcol;
3240 lastcol->num_values ++;
3241 }
3242 else
3243 goto col_error;
3244 }
3245 else if (!_cups_strcasecmp(token, "MEMBER"))
3246 {
3247 /*
3248 * Attribute...
3249 */
3250
3251 lastcol = NULL;
3252
3253 if (!get_token(fp, token, sizeof(token), linenum))
3254 {
3255 print_fatal_error("Missing MEMBER value tag on line %d.", *linenum);
3256 goto col_error;
3257 }
3258
3259 if ((value = ippTagValue(token)) == IPP_TAG_ZERO)
3260 {
3261 print_fatal_error("Bad MEMBER value tag \"%s\" on line %d.", token,
3262 *linenum);
3263 goto col_error;
3264 }
3265
3266 if (!get_token(fp, attr, sizeof(attr), linenum))
3267 {
3268 print_fatal_error("Missing MEMBER name on line %d.", *linenum);
3269 goto col_error;
3270 }
3271
3272 if (!get_token(fp, temp, sizeof(temp), linenum))
3273 {
3274 print_fatal_error("Missing MEMBER value on line %d.", *linenum);
3275 goto col_error;
3276 }
3277
3278 expand_variables(vars, token, temp, sizeof(token));
3279
3280 switch (value)
3281 {
3282 case IPP_TAG_BOOLEAN :
3283 if (!_cups_strcasecmp(token, "true"))
3284 ippAddBoolean(col, IPP_TAG_ZERO, attr, 1);
3285 else
3286 ippAddBoolean(col, IPP_TAG_ZERO, attr, atoi(token));
3287 break;
3288
3289 case IPP_TAG_INTEGER :
3290 case IPP_TAG_ENUM :
3291 ippAddInteger(col, IPP_TAG_ZERO, value, attr, atoi(token));
3292 break;
3293
3294 case IPP_TAG_RESOLUTION :
3295 {
3296 int xres, /* X resolution */
3297 yres; /* Y resolution */
3298 char units[6]; /* Units */
3299
3300 if (sscanf(token, "%dx%d%5s", &xres, &yres, units) != 3 ||
3301 (_cups_strcasecmp(units, "dpi") && _cups_strcasecmp(units, "dpc") &&
3302 _cups_strcasecmp(units, "other")))
3303 {
3304 print_fatal_error("Bad resolution value \"%s\" on line %d.",
3305 token, *linenum);
3306 goto col_error;
3307 }
3308
3309 if (!_cups_strcasecmp(units, "dpi"))
3310 ippAddResolution(col, IPP_TAG_ZERO, attr, xres, yres,
3311 IPP_RES_PER_INCH);
3312 else if (!_cups_strcasecmp(units, "dpc"))
3313 ippAddResolution(col, IPP_TAG_ZERO, attr, xres, yres,
3314 IPP_RES_PER_CM);
3315 else
3316 ippAddResolution(col, IPP_TAG_ZERO, attr, xres, yres,
3317 (ipp_res_t)0);
3318 }
3319 break;
3320
3321 case IPP_TAG_RANGE :
3322 {
3323 int lowers[4], /* Lower value */
3324 uppers[4], /* Upper values */
3325 num_vals; /* Number of values */
3326
3327
3328 num_vals = sscanf(token, "%d-%d,%d-%d,%d-%d,%d-%d",
3329 lowers + 0, uppers + 0,
3330 lowers + 1, uppers + 1,
3331 lowers + 2, uppers + 2,
3332 lowers + 3, uppers + 3);
3333
3334 if ((num_vals & 1) || num_vals == 0)
3335 {
3336 print_fatal_error("Bad rangeOfInteger value \"%s\" on line %d.",
3337 token, *linenum);
3338 goto col_error;
3339 }
3340
3341 ippAddRanges(col, IPP_TAG_ZERO, attr, num_vals / 2, lowers,
3342 uppers);
3343 }
3344 break;
3345
3346 case IPP_TAG_BEGIN_COLLECTION :
3347 if (!strcmp(token, "{"))
3348 {
3349 ipp_t *subcol = get_collection(vars, fp, linenum);
3350 /* Collection value */
3351
3352 if (subcol)
3353 {
3354 lastcol = ippAddCollection(col, IPP_TAG_ZERO, attr, subcol);
3355 ippDelete(subcol);
3356 }
3357 else
3358 goto col_error;
3359 }
3360 else
3361 {
3362 print_fatal_error("Bad collection value on line %d.", *linenum);
3363 goto col_error;
3364 }
3365 break;
3366
3367 default :
3368 if (!strchr(token, ','))
3369 ippAddString(col, IPP_TAG_ZERO, value, attr, NULL, token);
3370 else
3371 {
3372 /*
3373 * Multiple string values...
3374 */
3375
3376 int num_values; /* Number of values */
3377 char *values[100], /* Values */
3378 *ptr; /* Pointer to next value */
3379
3380
3381 values[0] = token;
3382 num_values = 1;
3383
3384 for (ptr = strchr(token, ','); ptr; ptr = strchr(ptr, ','))
3385 {
3386 *ptr++ = '\0';
3387 values[num_values] = ptr;
3388 num_values ++;
3389 }
3390
3391 ippAddStrings(col, IPP_TAG_ZERO, value, attr, num_values,
3392 NULL, (const char **)values);
3393 }
3394 break;
3395 }
3396 }
3397 }
3398
3399 return (col);
3400
3401 /*
3402 * If we get here there was a parse error; free memory and return.
3403 */
3404
3405 col_error:
3406
3407 ippDelete(col);
3408
3409 return (NULL);
3410 }
3411
3412
3413 /*
3414 * 'get_filename()' - Get a filename based on the current test file.
3415 */
3416
3417 static char * /* O - Filename */
3418 get_filename(const char *testfile, /* I - Current test file */
3419 char *dst, /* I - Destination filename */
3420 const char *src, /* I - Source filename */
3421 size_t dstsize) /* I - Size of destination buffer */
3422 {
3423 char *dstptr; /* Pointer into destination */
3424 _cups_globals_t *cg = _cupsGlobals();
3425 /* Global data */
3426
3427
3428 if (*src == '<' && src[strlen(src) - 1] == '>')
3429 {
3430 /*
3431 * Map <filename> to CUPS_DATADIR/ipptool/filename...
3432 */
3433
3434 snprintf(dst, dstsize, "%s/ipptool/%s", cg->cups_datadir, src + 1);
3435 dstptr = dst + strlen(dst) - 1;
3436 if (*dstptr == '>')
3437 *dstptr = '\0';
3438 }
3439 else if (*src == '/' || !strchr(testfile, '/'))
3440 {
3441 /*
3442 * Use the path as-is...
3443 */
3444
3445 strlcpy(dst, src, dstsize);
3446 }
3447 else
3448 {
3449 /*
3450 * Make path relative to testfile...
3451 */
3452
3453 strlcpy(dst, testfile, dstsize);
3454 if ((dstptr = strrchr(dst, '/')) != NULL)
3455 dstptr ++;
3456 else
3457 dstptr = dst; /* Should never happen */
3458
3459 strlcpy(dstptr, src, dstsize - (dstptr - dst));
3460 }
3461
3462 return (dst);
3463 }
3464
3465
3466 /*
3467 * 'get_token()' - Get a token from a file.
3468 */
3469
3470 static char * /* O - Token from file or NULL on EOF */
3471 get_token(FILE *fp, /* I - File to read from */
3472 char *buf, /* I - Buffer to read into */
3473 int buflen, /* I - Length of buffer */
3474 int *linenum) /* IO - Current line number */
3475 {
3476 int ch, /* Character from file */
3477 quote; /* Quoting character */
3478 char *bufptr, /* Pointer into buffer */
3479 *bufend; /* End of buffer */
3480
3481
3482 for (;;)
3483 {
3484 /*
3485 * Skip whitespace...
3486 */
3487
3488 while (isspace(ch = getc(fp)))
3489 {
3490 if (ch == '\n')
3491 (*linenum) ++;
3492 }
3493
3494 /*
3495 * Read a token...
3496 */
3497
3498 if (ch == EOF)
3499 return (NULL);
3500 else if (ch == '\'' || ch == '\"')
3501 {
3502 /*
3503 * Quoted text or regular expression...
3504 */
3505
3506 quote = ch;
3507 bufptr = buf;
3508 bufend = buf + buflen - 1;
3509
3510 while ((ch = getc(fp)) != EOF)
3511 {
3512 if (ch == '\\')
3513 {
3514 /*
3515 * Escape next character...
3516 */
3517
3518 if (bufptr < bufend)
3519 *bufptr++ = ch;
3520
3521 if ((ch = getc(fp)) != EOF && bufptr < bufend)
3522 *bufptr++ = ch;
3523 }
3524 else if (ch == quote)
3525 break;
3526 else if (bufptr < bufend)
3527 *bufptr++ = ch;
3528 }
3529
3530 *bufptr = '\0';
3531
3532 return (buf);
3533 }
3534 else if (ch == '#')
3535 {
3536 /*
3537 * Comment...
3538 */
3539
3540 while ((ch = getc(fp)) != EOF)
3541 if (ch == '\n')
3542 break;
3543
3544 (*linenum) ++;
3545 }
3546 else
3547 {
3548 /*
3549 * Whitespace delimited text...
3550 */
3551
3552 ungetc(ch, fp);
3553
3554 bufptr = buf;
3555 bufend = buf + buflen - 1;
3556
3557 while ((ch = getc(fp)) != EOF)
3558 if (isspace(ch) || ch == '#')
3559 break;
3560 else if (bufptr < bufend)
3561 *bufptr++ = ch;
3562
3563 if (ch == '#')
3564 ungetc(ch, fp);
3565 else if (ch == '\n')
3566 (*linenum) ++;
3567
3568 *bufptr = '\0';
3569
3570 return (buf);
3571 }
3572 }
3573 }
3574
3575
3576 /*
3577 * 'get_variable()' - Get the value of a variable.
3578 */
3579
3580 static char * /* O - Value or NULL */
3581 get_variable(_cups_vars_t *vars, /* I - Variables */
3582 const char *name) /* I - Variable name */
3583 {
3584 _cups_var_t key, /* Search key */
3585 *match; /* Matching variable, if any */
3586
3587
3588 key.name = (char *)name;
3589 match = cupsArrayFind(vars->vars, &key);
3590
3591 return (match ? match->value : NULL);
3592 }
3593
3594
3595 /*
3596 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
3597 * value.
3598 */
3599
3600 static char * /* O - ISO 8601 date/time string */
3601 iso_date(ipp_uchar_t *date) /* I - IPP (RFC 1903) date/time value */
3602 {
3603 time_t utctime; /* UTC time since 1970 */
3604 struct tm *utcdate; /* UTC date/time */
3605 static char buffer[255]; /* String buffer */
3606
3607
3608 utctime = ippDateToTime(date);
3609 utcdate = gmtime(&utctime);
3610
3611 snprintf(buffer, sizeof(buffer), "%04d-%02d-%02dT%02d:%02d:%02dZ",
3612 utcdate->tm_year + 1900, utcdate->tm_mon + 1, utcdate->tm_mday,
3613 utcdate->tm_hour, utcdate->tm_min, utcdate->tm_sec);
3614
3615 return (buffer);
3616 }
3617
3618
3619 /*
3620 * 'password_cb()' - Password callback for authenticated tests.
3621 */
3622
3623 static const char * /* O - Password */
3624 password_cb(const char *prompt) /* I - Prompt (unused) */
3625 {
3626 (void)prompt;
3627
3628 return (Password);
3629 }
3630
3631
3632 /*
3633 * 'print_attr()' - Print an attribute on the screen.
3634 */
3635
3636 static void
3637 print_attr(ipp_attribute_t *attr, /* I - Attribute to print */
3638 ipp_tag_t *group) /* IO - Current group */
3639 {
3640 int i; /* Looping var */
3641 ipp_attribute_t *colattr; /* Collection attribute */
3642
3643
3644 if (Output == _CUPS_OUTPUT_PLIST)
3645 {
3646 if (!attr->name || (group && *group != attr->group_tag))
3647 {
3648 puts("</dict>");
3649 puts("<dict>");
3650
3651 if (group)
3652 *group = attr->group_tag;
3653 }
3654
3655 if (!attr->name)
3656 return;
3657
3658 print_xml_string("key", attr->name);
3659 if (attr->num_values > 1)
3660 puts("<array>");
3661
3662 switch (attr->value_tag)
3663 {
3664 case IPP_TAG_INTEGER :
3665 case IPP_TAG_ENUM :
3666 for (i = 0; i < attr->num_values; i ++)
3667 if (Output == _CUPS_OUTPUT_PLIST)
3668 printf("<integer>%d</integer>\n", attr->values[i].integer);
3669 else
3670 printf("%d ", attr->values[i].integer);
3671 break;
3672
3673 case IPP_TAG_BOOLEAN :
3674 for (i = 0; i < attr->num_values; i ++)
3675 if (Output == _CUPS_OUTPUT_PLIST)
3676 puts(attr->values[i].boolean ? "<true />" : "<false />");
3677 else if (attr->values[i].boolean)
3678 fputs("true ", stdout);
3679 else
3680 fputs("false ", stdout);
3681 break;
3682
3683 case IPP_TAG_RANGE :
3684 for (i = 0; i < attr->num_values; i ++)
3685 if (Output == _CUPS_OUTPUT_PLIST)
3686 printf("<dict><key>lower</key><integer>%d</integer>"
3687 "<key>upper</key><integer>%d</integer></dict>\n",
3688 attr->values[i].range.lower, attr->values[i].range.upper);
3689 else
3690 printf("%d-%d ", attr->values[i].range.lower,
3691 attr->values[i].range.upper);
3692 break;
3693
3694 case IPP_TAG_RESOLUTION :
3695 for (i = 0; i < attr->num_values; i ++)
3696 if (Output == _CUPS_OUTPUT_PLIST)
3697 printf("<dict><key>xres</key><integer>%d</integer>"
3698 "<key>yres</key><integer>%d</integer>"
3699 "<key>units</key><string>%s</string></dict>\n",
3700 attr->values[i].resolution.xres,
3701 attr->values[i].resolution.yres,
3702 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
3703 "dpi" : "dpc");
3704 else
3705 printf("%dx%d%s ", attr->values[i].resolution.xres,
3706 attr->values[i].resolution.yres,
3707 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
3708 "dpi" : "dpc");
3709 break;
3710
3711 case IPP_TAG_DATE :
3712 for (i = 0; i < attr->num_values; i ++)
3713 if (Output == _CUPS_OUTPUT_PLIST)
3714 printf("<date>%s</date>\n", iso_date(attr->values[i].date));
3715 else
3716 printf("%s ", iso_date(attr->values[i].date));
3717 break;
3718
3719 case IPP_TAG_STRING :
3720 case IPP_TAG_TEXT :
3721 case IPP_TAG_NAME :
3722 case IPP_TAG_KEYWORD :
3723 case IPP_TAG_CHARSET :
3724 case IPP_TAG_URI :
3725 case IPP_TAG_MIMETYPE :
3726 case IPP_TAG_LANGUAGE :
3727 for (i = 0; i < attr->num_values; i ++)
3728 if (Output == _CUPS_OUTPUT_PLIST)
3729 print_xml_string("string", attr->values[i].string.text);
3730 else
3731 printf("\"%s\" ", attr->values[i].string.text);
3732 break;
3733
3734 case IPP_TAG_TEXTLANG :
3735 case IPP_TAG_NAMELANG :
3736 for (i = 0; i < attr->num_values; i ++)
3737 if (Output == _CUPS_OUTPUT_PLIST)
3738 {
3739 fputs("<dict><key>language</key><string>", stdout);
3740 print_xml_string(NULL, attr->values[i].string.charset);
3741 fputs("</string><key>string</key><string>", stdout);
3742 print_xml_string(NULL, attr->values[i].string.text);
3743 puts("</string></dict>");
3744 }
3745 else
3746 printf("\"%s\"(%s) ", attr->values[i].string.text,
3747 attr->values[i].string.charset);
3748 break;
3749
3750 case IPP_TAG_BEGIN_COLLECTION :
3751 for (i = 0; i < attr->num_values; i ++)
3752 {
3753 if (Output == _CUPS_OUTPUT_PLIST)
3754 {
3755 puts("<dict>");
3756 for (colattr = attr->values[i].collection->attrs;
3757 colattr;
3758 colattr = colattr->next)
3759 print_attr(colattr, NULL);
3760 puts("</dict>");
3761 }
3762 else
3763 {
3764 if (i)
3765 putchar(' ');
3766
3767 print_col(attr->values[i].collection);
3768 }
3769 }
3770 break;
3771
3772 default :
3773 if (Output == _CUPS_OUTPUT_PLIST)
3774 printf("<string>&lt;&lt;%s&gt;&gt;</string>\n",
3775 ippTagString(attr->value_tag));
3776 else
3777 fputs(ippTagString(attr->value_tag), stdout);
3778 break;
3779 }
3780
3781 if (attr->num_values > 1)
3782 puts("</array>");
3783 }
3784 else
3785 {
3786 char buffer[8192]; /* Value buffer */
3787
3788 if (Output == _CUPS_OUTPUT_TEST)
3789 {
3790 if (!attr->name)
3791 {
3792 puts(" -- separator --");
3793 return;
3794 }
3795
3796 printf(" %s (%s%s) = ", attr->name,
3797 attr->num_values > 1 ? "1setOf " : "",
3798 ippTagString(attr->value_tag));
3799 }
3800
3801 _ippAttrString(attr, buffer, sizeof(buffer));
3802 puts(buffer);
3803 }
3804 }
3805
3806
3807 /*
3808 * 'print_col()' - Print a collection attribute on the screen.
3809 */
3810
3811 static void
3812 print_col(ipp_t *col) /* I - Collection attribute to print */
3813 {
3814 int i; /* Looping var */
3815 ipp_attribute_t *attr; /* Current attribute in collection */
3816
3817
3818 fputs("{ ", stdout);
3819 for (attr = col->attrs; attr; attr = attr->next)
3820 {
3821 printf("%s (%s%s) = ", attr->name, attr->num_values > 1 ? "1setOf " : "",
3822 ippTagString(attr->value_tag));
3823
3824 switch (attr->value_tag)
3825 {
3826 case IPP_TAG_INTEGER :
3827 case IPP_TAG_ENUM :
3828 for (i = 0; i < attr->num_values; i ++)
3829 printf("%d ", attr->values[i].integer);
3830 break;
3831
3832 case IPP_TAG_BOOLEAN :
3833 for (i = 0; i < attr->num_values; i ++)
3834 if (attr->values[i].boolean)
3835 printf("true ");
3836 else
3837 printf("false ");
3838 break;
3839
3840 case IPP_TAG_NOVALUE :
3841 printf("novalue");
3842 break;
3843
3844 case IPP_TAG_RANGE :
3845 for (i = 0; i < attr->num_values; i ++)
3846 printf("%d-%d ", attr->values[i].range.lower,
3847 attr->values[i].range.upper);
3848 break;
3849
3850 case IPP_TAG_RESOLUTION :
3851 for (i = 0; i < attr->num_values; i ++)
3852 printf("%dx%d%s ", attr->values[i].resolution.xres,
3853 attr->values[i].resolution.yres,
3854 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
3855 "dpi" : "dpc");
3856 break;
3857
3858 case IPP_TAG_STRING :
3859 case IPP_TAG_TEXT :
3860 case IPP_TAG_NAME :
3861 case IPP_TAG_KEYWORD :
3862 case IPP_TAG_CHARSET :
3863 case IPP_TAG_URI :
3864 case IPP_TAG_MIMETYPE :
3865 case IPP_TAG_LANGUAGE :
3866 for (i = 0; i < attr->num_values; i ++)
3867 printf("\"%s\" ", attr->values[i].string.text);
3868 break;
3869
3870 case IPP_TAG_TEXTLANG :
3871 case IPP_TAG_NAMELANG :
3872 for (i = 0; i < attr->num_values; i ++)
3873 printf("\"%s\",%s ", attr->values[i].string.text,
3874 attr->values[i].string.charset);
3875 break;
3876
3877 case IPP_TAG_BEGIN_COLLECTION :
3878 for (i = 0; i < attr->num_values; i ++)
3879 {
3880 print_col(attr->values[i].collection);
3881 putchar(' ');
3882 }
3883 break;
3884
3885 default :
3886 break; /* anti-compiler-warning-code */
3887 }
3888 }
3889
3890 putchar('}');
3891 }
3892
3893
3894 /*
3895 * 'print_csv()' - Print a line of CSV text.
3896 */
3897
3898 static void
3899 print_csv(
3900 ipp_attribute_t *attr, /* I - First attribute for line */
3901 int num_displayed, /* I - Number of attributes to display */
3902 char **displayed, /* I - Attributes to display */
3903 size_t *widths) /* I - Column widths */
3904 {
3905 int i; /* Looping var */
3906 size_t maxlength; /* Max length of all columns */
3907 char *buffer, /* String buffer */
3908 *bufptr; /* Pointer into buffer */
3909 ipp_attribute_t *current; /* Current attribute */
3910
3911
3912 /*
3913 * Get the maximum string length we have to show and allocate...
3914 */
3915
3916 for (i = 1, maxlength = widths[0]; i < num_displayed; i ++)
3917 if (widths[i] > maxlength)
3918 maxlength = widths[i];
3919
3920 maxlength += 2;
3921
3922 if ((buffer = malloc(maxlength)) == NULL)
3923 return;
3924
3925 /*
3926 * Loop through the attributes to display...
3927 */
3928
3929 if (attr)
3930 {
3931 for (i = 0; i < num_displayed; i ++)
3932 {
3933 if (i)
3934 putchar(',');
3935
3936 buffer[0] = '\0';
3937
3938 for (current = attr; current; current = current->next)
3939 {
3940 if (!current->name)
3941 break;
3942 else if (!strcmp(current->name, displayed[i]))
3943 {
3944 _ippAttrString(current, buffer, maxlength);
3945 break;
3946 }
3947 }
3948
3949 if (strchr(buffer, ',') != NULL || strchr(buffer, '\"') != NULL ||
3950 strchr(buffer, '\\') != NULL)
3951 {
3952 putchar('\"');
3953 for (bufptr = buffer; *bufptr; bufptr ++)
3954 {
3955 if (*bufptr == '\\' || *bufptr == '\"')
3956 putchar('\\');
3957 putchar(*bufptr);
3958 }
3959 putchar('\"');
3960 }
3961 else
3962 fputs(buffer, stdout);
3963 }
3964 putchar('\n');
3965 }
3966 else
3967 {
3968 for (i = 0; i < num_displayed; i ++)
3969 {
3970 if (i)
3971 putchar(',');
3972
3973 fputs(displayed[i], stdout);
3974 }
3975 putchar('\n');
3976 }
3977
3978 free(buffer);
3979 }
3980
3981
3982 /*
3983 * 'print_fatal_error()' - Print a fatal error message.
3984 */
3985
3986 static void
3987 print_fatal_error(const char *s, /* I - Printf-style format string */
3988 ...) /* I - Additional arguments as needed */
3989 {
3990 char buffer[10240]; /* Format buffer */
3991 va_list ap; /* Pointer to arguments */
3992
3993
3994 /*
3995 * Format the error message...
3996 */
3997
3998 va_start(ap, s);
3999 vsnprintf(buffer, sizeof(buffer), s, ap);
4000 va_end(ap);
4001
4002 /*
4003 * Then output it...
4004 */
4005
4006 if (Output == _CUPS_OUTPUT_PLIST)
4007 {
4008 print_xml_header();
4009 print_xml_trailer(0, buffer);
4010 }
4011 else
4012 _cupsLangPrintf(stderr, "ipptool: %s", buffer);
4013 }
4014
4015
4016 /*
4017 * 'print_line()' - Print a line of formatted or CSV text.
4018 */
4019
4020 static void
4021 print_line(
4022 ipp_attribute_t *attr, /* I - First attribute for line */
4023 int num_displayed, /* I - Number of attributes to display */
4024 char **displayed, /* I - Attributes to display */
4025 size_t *widths) /* I - Column widths */
4026 {
4027 int i; /* Looping var */
4028 size_t maxlength; /* Max length of all columns */
4029 char *buffer; /* String buffer */
4030 ipp_attribute_t *current; /* Current attribute */
4031
4032
4033 /*
4034 * Get the maximum string length we have to show and allocate...
4035 */
4036
4037 for (i = 1, maxlength = widths[0]; i < num_displayed; i ++)
4038 if (widths[i] > maxlength)
4039 maxlength = widths[i];
4040
4041 maxlength += 2;
4042
4043 if ((buffer = malloc(maxlength)) == NULL)
4044 return;
4045
4046 /*
4047 * Loop through the attributes to display...
4048 */
4049
4050 if (attr)
4051 {
4052 for (i = 0; i < num_displayed; i ++)
4053 {
4054 if (i)
4055 putchar(' ');
4056
4057 buffer[0] = '\0';
4058
4059 for (current = attr; current; current = current->next)
4060 {
4061 if (!current->name)
4062 break;
4063 else if (!strcmp(current->name, displayed[i]))
4064 {
4065 _ippAttrString(current, buffer, maxlength);
4066 break;
4067 }
4068 }
4069
4070 printf("%*s", (int)-widths[i], buffer);
4071 }
4072 putchar('\n');
4073 }
4074 else
4075 {
4076 for (i = 0; i < num_displayed; i ++)
4077 {
4078 if (i)
4079 putchar(' ');
4080
4081 printf("%*s", (int)-widths[i], displayed[i]);
4082 }
4083 putchar('\n');
4084
4085 for (i = 0; i < num_displayed; i ++)
4086 {
4087 if (i)
4088 putchar(' ');
4089
4090 memset(buffer, '-', widths[i]);
4091 buffer[widths[i]] = '\0';
4092 fputs(buffer, stdout);
4093 }
4094 putchar('\n');
4095 }
4096
4097 free(buffer);
4098 }
4099
4100
4101 /*
4102 * 'print_test_error()' - Print a test error message.
4103 */
4104
4105 static void
4106 print_test_error(const char *s, /* I - Printf-style format string */
4107 ...) /* I - Additional arguments as needed */
4108 {
4109 char buffer[10240]; /* Format buffer */
4110 va_list ap; /* Pointer to arguments */
4111
4112
4113 /*
4114 * Format the error message...
4115 */
4116
4117 va_start(ap, s);
4118 vsnprintf(buffer, sizeof(buffer), s, ap);
4119 va_end(ap);
4120
4121 /*
4122 * Then output it...
4123 */
4124
4125 if (Output == _CUPS_OUTPUT_PLIST)
4126 print_xml_string("string", buffer);
4127 else
4128 printf(" %s\n", buffer);
4129 }
4130
4131
4132 /*
4133 * 'print_xml_header()' - Print a standard XML plist header.
4134 */
4135
4136 static void
4137 print_xml_header(void)
4138 {
4139 if (!XMLHeader)
4140 {
4141 puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
4142 puts("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
4143 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
4144 puts("<plist version=\"1.0\">");
4145 puts("<dict>");
4146 puts("<key>Transfer</key>");
4147 printf("<string>%s</string>\n",
4148 Transfer == _CUPS_TRANSFER_AUTO ? "auto" :
4149 Transfer == _CUPS_TRANSFER_CHUNKED ? "chunked" : "length");
4150 puts("<key>Tests</key>");
4151 puts("<array>");
4152
4153 XMLHeader = 1;
4154 }
4155 }
4156
4157
4158 /*
4159 * 'print_xml_string()' - Print an XML string with escaping.
4160 */
4161
4162 static void
4163 print_xml_string(const char *element, /* I - Element name or NULL */
4164 const char *s) /* I - String to print */
4165 {
4166 if (element)
4167 printf("<%s>", element);
4168
4169 while (*s)
4170 {
4171 if (*s == '&')
4172 fputs("&amp;", stdout);
4173 else if (*s == '<')
4174 fputs("&lt;", stdout);
4175 else if (*s == '>')
4176 fputs("&gt;", stdout);
4177 else if ((*s & 0xe0) == 0xc0)
4178 {
4179 /*
4180 * Validate UTF-8 two-byte sequence...
4181 */
4182
4183 if ((s[1] & 0xc0) != 0x80)
4184 {
4185 putchar('?');
4186 s ++;
4187 }
4188 else
4189 {
4190 putchar(*s++);
4191 putchar(*s);
4192 }
4193 }
4194 else if ((*s & 0xf0) == 0xe0)
4195 {
4196 /*
4197 * Validate UTF-8 three-byte sequence...
4198 */
4199
4200 if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80)
4201 {
4202 putchar('?');
4203 s += 2;
4204 }
4205 else
4206 {
4207 putchar(*s++);
4208 putchar(*s++);
4209 putchar(*s);
4210 }
4211 }
4212 else if ((*s & 0xf8) == 0xf0)
4213 {
4214 /*
4215 * Validate UTF-8 four-byte sequence...
4216 */
4217
4218 if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 ||
4219 (s[3] & 0xc0) != 0x80)
4220 {
4221 putchar('?');
4222 s += 3;
4223 }
4224 else
4225 {
4226 putchar(*s++);
4227 putchar(*s++);
4228 putchar(*s++);
4229 putchar(*s);
4230 }
4231 }
4232 else if ((*s & 0x80) || (*s < ' ' && !isspace(*s & 255)))
4233 {
4234 /*
4235 * Invalid control character...
4236 */
4237
4238 putchar('?');
4239 }
4240 else
4241 putchar(*s);
4242
4243 s ++;
4244 }
4245
4246 if (element)
4247 printf("</%s>\n", element);
4248 }
4249
4250
4251 /*
4252 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
4253 */
4254
4255 static void
4256 print_xml_trailer(int success, /* I - 1 on success, 0 on failure */
4257 const char *message) /* I - Error message or NULL */
4258 {
4259 if (XMLHeader)
4260 {
4261 puts("</array>");
4262 puts("<key>Successful</key>");
4263 puts(success ? "<true />" : "<false />");
4264 if (message)
4265 {
4266 puts("<key>ErrorMessage</key>");
4267 print_xml_string("string", message);
4268 }
4269 puts("</dict>");
4270 puts("</plist>");
4271
4272 XMLHeader = 0;
4273 }
4274 }
4275
4276
4277 /*
4278 * 'set_variable()' - Set a variable value.
4279 */
4280
4281 static void
4282 set_variable(_cups_vars_t *vars, /* I - Variables */
4283 const char *name, /* I - Variable name */
4284 const char *value) /* I - Value string */
4285 {
4286 _cups_var_t key, /* Search key */
4287 *var; /* New variable */
4288
4289
4290 if (!_cups_strcasecmp(name, "filename"))
4291 {
4292 if (vars->filename)
4293 free(vars->filename);
4294
4295 vars->filename = strdup(value);
4296 return;
4297 }
4298
4299 key.name = (char *)name;
4300 if ((var = cupsArrayFind(vars->vars, &key)) != NULL)
4301 {
4302 free(var->value);
4303 var->value = strdup(value);
4304 }
4305 else if ((var = malloc(sizeof(_cups_var_t))) == NULL)
4306 {
4307 print_fatal_error("Unable to allocate memory for variable \"%s\".", name);
4308 exit(1);
4309 }
4310 else
4311 {
4312 var->name = strdup(name);
4313 var->value = strdup(value);
4314
4315 cupsArrayAdd(vars->vars, var);
4316 }
4317 }
4318
4319
4320 #ifndef WIN32
4321 /*
4322 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
4323 */
4324
4325 static void
4326 sigterm_handler(int sig) /* I - Signal number (unused) */
4327 {
4328 (void)sig;
4329
4330 Cancel = 1;
4331
4332 signal(SIGINT, SIG_DFL);
4333 signal(SIGTERM, SIG_DFL);
4334 }
4335 #endif /* !WIN32 */
4336
4337
4338 /*
4339 * 'timeout_cb()' - Handle HTTP timeouts.
4340 */
4341
4342 static int /* O - 1 to continue, 0 to cancel */
4343 timeout_cb(http_t *http, /* I - Connection to server (unused) */
4344 void *user_data) /* I - User data (unused) */
4345 {
4346 (void)http;
4347 (void)user_data;
4348
4349 /* Always cancel on timeout */
4350 return (0);
4351 }
4352
4353
4354 /*
4355 * 'usage()' - Show program usage.
4356 */
4357
4358 static void
4359 usage(void)
4360 {
4361 _cupsLangPuts(stderr, _("Usage: ipptool [options] URI filename [ ... "
4362 "filenameN ]"));
4363 _cupsLangPuts(stderr, _("Options:"));
4364 _cupsLangPuts(stderr, _(" -4 Connect using IPv4."));
4365 _cupsLangPuts(stderr, _(" -6 Connect using IPv6."));
4366 _cupsLangPuts(stderr, _(" -C Send requests using "
4367 "chunking (default)."));
4368 _cupsLangPuts(stderr, _(" -E Test with TLS "
4369 "encryption."));
4370 _cupsLangPuts(stderr, _(" -I Ignore errors."));
4371 _cupsLangPuts(stderr, _(" -L Send requests using "
4372 "content-length."));
4373 _cupsLangPuts(stderr, _(" -S Test with SSL "
4374 "encryption."));
4375 _cupsLangPuts(stderr, _(" -T Set the receive/send "
4376 "timeout in seconds."));
4377 _cupsLangPuts(stderr, _(" -V version Set default IPP "
4378 "version."));
4379 _cupsLangPuts(stderr, _(" -X Produce XML plist instead "
4380 "of plain text."));
4381 _cupsLangPuts(stderr, _(" -d name=value Set named variable to "
4382 "value."));
4383 _cupsLangPuts(stderr, _(" -f filename Set default request "
4384 "filename."));
4385 _cupsLangPuts(stderr, _(" -i seconds Repeat the last file with "
4386 "the given time interval."));
4387 _cupsLangPuts(stderr, _(" -n count Repeat the last file the "
4388 "given number of times."));
4389 _cupsLangPuts(stderr, _(" -q Be quiet - no output "
4390 "except errors."));
4391 _cupsLangPuts(stderr, _(" -t Produce a test report."));
4392 _cupsLangPuts(stderr, _(" -v Show all attributes sent "
4393 "and received."));
4394
4395 exit(1);
4396 }
4397
4398
4399 /*
4400 * 'validate_attr()' - Determine whether an attribute is valid.
4401 */
4402
4403 static int /* O - 1 if valid, 0 otherwise */
4404 validate_attr(ipp_attribute_t *attr, /* I - Attribute to validate */
4405 int print) /* I - 1 = report issues to stdout */
4406 {
4407 int i; /* Looping var */
4408 char scheme[64], /* Scheme from URI */
4409 userpass[256], /* Username/password from URI */
4410 hostname[256], /* Hostname from URI */
4411 resource[1024]; /* Resource from URI */
4412 int port, /* Port number from URI */
4413 uri_status, /* URI separation status */
4414 valid = 1; /* Is the attribute valid? */
4415 const char *ptr; /* Pointer into string */
4416 ipp_attribute_t *colattr; /* Collection attribute */
4417 regex_t re; /* Regular expression */
4418 ipp_uchar_t *date; /* Current date value */
4419
4420
4421 /*
4422 * Skip separators.
4423 */
4424
4425 if (!attr->name)
4426 return (1);
4427
4428 /*
4429 * Validate the attribute name.
4430 */
4431
4432 for (ptr = attr->name; *ptr; ptr ++)
4433 if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' && *ptr != '_')
4434 break;
4435
4436 if (*ptr || ptr == attr->name)
4437 {
4438 valid = 0;
4439
4440 if (print)
4441 print_test_error("\"%s\": Bad attribute name - invalid character (RFC "
4442 "2911 section 4.1.3).", attr->name);
4443 }
4444
4445 if ((ptr - attr->name) > 255)
4446 {
4447 valid = 0;
4448
4449 if (print)
4450 print_test_error("\"%s\": Bad attribute name - bad length (RFC 2911 "
4451 "section 4.1.3).", attr->name);
4452 }
4453
4454 switch (attr->value_tag)
4455 {
4456 case IPP_TAG_INTEGER :
4457 break;
4458
4459 case IPP_TAG_BOOLEAN :
4460 for (i = 0; i < attr->num_values; i ++)
4461 {
4462 if (attr->values[i].boolean != 0 &&
4463 attr->values[i].boolean != 1)
4464 {
4465 valid = 0;
4466
4467 if (print)
4468 print_test_error("\"%s\": Bad boolen value %d (RFC 2911 section "
4469 "4.1.10).", attr->name, attr->values[i].boolean);
4470 else
4471 break;
4472 }
4473 }
4474 break;
4475
4476 case IPP_TAG_ENUM :
4477 for (i = 0; i < attr->num_values; i ++)
4478 {
4479 if (attr->values[i].integer < 1)
4480 {
4481 valid = 0;
4482
4483 if (print)
4484 print_test_error("\"%s\": Bad enum value %d - out of range "
4485 "(RFC 2911 section 4.1.4).", attr->name,
4486 attr->values[i].integer);
4487 else
4488 break;
4489 }
4490 }
4491 break;
4492
4493 case IPP_TAG_STRING :
4494 for (i = 0; i < attr->num_values; i ++)
4495 {
4496 if (attr->values[i].unknown.length > 1023)
4497 {
4498 valid = 0;
4499
4500 if (print)
4501 print_test_error("\"%s\": Bad octetString value - bad length %d "
4502 "(RFC 2911 section 4.1.10).", attr->name,
4503 attr->values[i].unknown.length);
4504 else
4505 break;
4506 }
4507 }
4508 break;
4509
4510 case IPP_TAG_DATE :
4511 for (i = 0; i < attr->num_values; i ++)
4512 {
4513 date = attr->values[i].date;
4514
4515 if (date[2] < 1 || date[2] > 12)
4516 {
4517 valid = 0;
4518
4519 if (print)
4520 print_test_error("\"%s\": Bad dateTime month %u (RFC 2911 "
4521 "section 4.1.13).", attr->name, date[2]);
4522 else
4523 break;
4524 }
4525
4526 if (date[3] < 1 || date[3] > 31)
4527 {
4528 valid = 0;
4529
4530 if (print)
4531 print_test_error("\"%s\": Bad dateTime day %u (RFC 2911 "
4532 "section 4.1.13).", attr->name, date[3]);
4533 else
4534 break;
4535 }
4536
4537 if (date[4] > 23)
4538 {
4539 valid = 0;
4540
4541 if (print)
4542 print_test_error("\"%s\": Bad dateTime hours %u (RFC 2911 "
4543 "section 4.1.13).", attr->name, date[4]);
4544 else
4545 break;
4546 }
4547
4548 if (date[5] > 59)
4549 {
4550 valid = 0;
4551
4552 if (print)
4553 print_test_error("\"%s\": Bad dateTime minutes %u (RFC 2911 "
4554 "section 4.1.13).", attr->name, date[5]);
4555 else
4556 break;
4557 }
4558
4559 if (date[6] > 60)
4560 {
4561 valid = 0;
4562
4563 if (print)
4564 print_test_error("\"%s\": Bad dateTime seconds %u (RFC 2911 "
4565 "section 4.1.13).", attr->name, date[6]);
4566 else
4567 break;
4568 }
4569
4570 if (date[7] > 9)
4571 {
4572 valid = 0;
4573
4574 if (print)
4575 print_test_error("\"%s\": Bad dateTime deciseconds %u (RFC 2911 "
4576 "section 4.1.13).", attr->name, date[7]);
4577 else
4578 break;
4579 }
4580
4581 if (date[8] != '-' && date[8] != '+')
4582 {
4583 valid = 0;
4584
4585 if (print)
4586 print_test_error("\"%s\": Bad dateTime UTC sign '%c' (RFC 2911 "
4587 "section 4.1.13).", attr->name, date[8]);
4588 else
4589 break;
4590 }
4591
4592 if (date[9] > 11)
4593 {
4594 valid = 0;
4595
4596 if (print)
4597 print_test_error("\"%s\": Bad dateTime UTC hours %u (RFC 2911 "
4598 "section 4.1.13).", attr->name, date[9]);
4599 else
4600 break;
4601 }
4602
4603 if (date[10] > 59)
4604 {
4605 valid = 0;
4606
4607 if (print)
4608 print_test_error("\"%s\": Bad dateTime UTC minutes %u (RFC 2911 "
4609 "section 4.1.13).", attr->name, date[10]);
4610 else
4611 break;
4612 }
4613 }
4614 break;
4615
4616 case IPP_TAG_RESOLUTION :
4617 for (i = 0; i < attr->num_values; i ++)
4618 {
4619 if (attr->values[i].resolution.xres <= 0)
4620 {
4621 valid = 0;
4622
4623 if (print)
4624 print_test_error("\"%s\": Bad resolution value %dx%d%s - cross "
4625 "feed resolution must be positive (RFC 2911 "
4626 "section 4.1.13).", attr->name,
4627 attr->values[i].resolution.xres,
4628 attr->values[i].resolution.yres,
4629 attr->values[i].resolution.units ==
4630 IPP_RES_PER_INCH ? "dpi" :
4631 attr->values[i].resolution.units ==
4632 IPP_RES_PER_CM ? "dpc" : "unknown");
4633 else
4634 break;
4635 }
4636
4637 if (attr->values[i].resolution.yres <= 0)
4638 {
4639 valid = 0;
4640
4641 if (print)
4642 print_test_error("\"%s\": Bad resolution value %dx%d%s - feed "
4643 "resolution must be positive (RFC 2911 section "
4644 "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.units != IPP_RES_PER_INCH &&
4656 attr->values[i].resolution.units != IPP_RES_PER_CM)
4657 {
4658 valid = 0;
4659
4660 if (print)
4661 print_test_error("\"%s\": Bad resolution value %dx%d%s - bad "
4662 "units value (RFC 2911 section 4.1.13).",
4663 attr->name, 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 break;
4674
4675 case IPP_TAG_RANGE :
4676 for (i = 0; i < attr->num_values; i ++)
4677 {
4678 if (attr->values[i].range.lower > attr->values[i].range.upper)
4679 {
4680 valid = 0;
4681
4682 if (print)
4683 print_test_error("\"%s\": Bad rangeOfInteger value %d-%d - lower "
4684 "greater than upper (RFC 2911 section 4.1.13).",
4685 attr->name, attr->values[i].range.lower,
4686 attr->values[i].range.upper);
4687 else
4688 break;
4689 }
4690 }
4691 break;
4692
4693 case IPP_TAG_BEGIN_COLLECTION :
4694 for (i = 0; i < attr->num_values; i ++)
4695 {
4696 for (colattr = attr->values[i].collection->attrs;
4697 colattr;
4698 colattr = colattr->next)
4699 {
4700 if (!validate_attr(colattr, 0))
4701 {
4702 valid = 0;
4703 break;
4704 }
4705 }
4706
4707 if (colattr && print)
4708 {
4709 print_test_error("\"%s\": Bad collection value.", attr->name);
4710
4711 while (colattr)
4712 {
4713 validate_attr(colattr, print);
4714 colattr = colattr->next;
4715 }
4716 }
4717 }
4718 break;
4719
4720 case IPP_TAG_TEXT :
4721 case IPP_TAG_TEXTLANG :
4722 for (i = 0; i < attr->num_values; i ++)
4723 {
4724 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
4725 {
4726 if ((*ptr & 0xe0) == 0xc0)
4727 {
4728 ptr ++;
4729 if ((*ptr & 0xc0) != 0x80)
4730 break;
4731 }
4732 else if ((*ptr & 0xf0) == 0xe0)
4733 {
4734 ptr ++;
4735 if ((*ptr & 0xc0) != 0x80)
4736 break;
4737 ptr ++;
4738 if ((*ptr & 0xc0) != 0x80)
4739 break;
4740 }
4741 else if ((*ptr & 0xf8) == 0xf0)
4742 {
4743 ptr ++;
4744 if ((*ptr & 0xc0) != 0x80)
4745 break;
4746 ptr ++;
4747 if ((*ptr & 0xc0) != 0x80)
4748 break;
4749 ptr ++;
4750 if ((*ptr & 0xc0) != 0x80)
4751 break;
4752 }
4753 else if (*ptr & 0x80)
4754 break;
4755 }
4756
4757 if (*ptr)
4758 {
4759 valid = 0;
4760
4761 if (print)
4762 print_test_error("\"%s\": Bad text value \"%s\" - bad UTF-8 "
4763 "sequence (RFC 2911 section 4.1.1).", attr->name,
4764 attr->values[i].string.text);
4765 else
4766 break;
4767 }
4768
4769 if ((ptr - attr->values[i].string.text) > 1023)
4770 {
4771 valid = 0;
4772
4773 if (print)
4774 print_test_error("\"%s\": Bad text value \"%s\" - bad length %d "
4775 "(RFC 2911 section 4.1.1).", attr->name,
4776 attr->values[i].string.text,
4777 (int)strlen(attr->values[i].string.text));
4778 else
4779 break;
4780 }
4781 }
4782 break;
4783
4784 case IPP_TAG_NAME :
4785 case IPP_TAG_NAMELANG :
4786 for (i = 0; i < attr->num_values; i ++)
4787 {
4788 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
4789 {
4790 if ((*ptr & 0xe0) == 0xc0)
4791 {
4792 ptr ++;
4793 if ((*ptr & 0xc0) != 0x80)
4794 break;
4795 }
4796 else if ((*ptr & 0xf0) == 0xe0)
4797 {
4798 ptr ++;
4799 if ((*ptr & 0xc0) != 0x80)
4800 break;
4801 ptr ++;
4802 if ((*ptr & 0xc0) != 0x80)
4803 break;
4804 }
4805 else if ((*ptr & 0xf8) == 0xf0)
4806 {
4807 ptr ++;
4808 if ((*ptr & 0xc0) != 0x80)
4809 break;
4810 ptr ++;
4811 if ((*ptr & 0xc0) != 0x80)
4812 break;
4813 ptr ++;
4814 if ((*ptr & 0xc0) != 0x80)
4815 break;
4816 }
4817 else if (*ptr & 0x80)
4818 break;
4819 }
4820
4821 if (*ptr)
4822 {
4823 valid = 0;
4824
4825 if (print)
4826 print_test_error("\"%s\": Bad name value \"%s\" - bad UTF-8 "
4827 "sequence (RFC 2911 section 4.1.2).", attr->name,
4828 attr->values[i].string.text);
4829 else
4830 break;
4831 }
4832
4833 if ((ptr - attr->values[i].string.text) > 1023)
4834 {
4835 valid = 0;
4836
4837 if (print)
4838 print_test_error("\"%s\": Bad name value \"%s\" - bad length %d "
4839 "(RFC 2911 section 4.1.2).", attr->name,
4840 attr->values[i].string.text,
4841 (int)strlen(attr->values[i].string.text));
4842 else
4843 break;
4844 }
4845 }
4846 break;
4847
4848 case IPP_TAG_KEYWORD :
4849 for (i = 0; i < attr->num_values; i ++)
4850 {
4851 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
4852 if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' &&
4853 *ptr != '_')
4854 break;
4855
4856 if (*ptr || ptr == attr->values[i].string.text)
4857 {
4858 valid = 0;
4859
4860 if (print)
4861 print_test_error("\"%s\": Bad keyword value \"%s\" - invalid "
4862 "character (RFC 2911 section 4.1.3).",
4863 attr->name, attr->values[i].string.text);
4864 else
4865 break;
4866 }
4867
4868 if ((ptr - attr->values[i].string.text) > 255)
4869 {
4870 valid = 0;
4871
4872 if (print)
4873 print_test_error("\"%s\": Bad keyword value \"%s\" - bad "
4874 "length %d (RFC 2911 section 4.1.3).",
4875 attr->name, attr->values[i].string.text,
4876 (int)strlen(attr->values[i].string.text));
4877 else
4878 break;
4879 }
4880 }
4881 break;
4882
4883 case IPP_TAG_URI :
4884 for (i = 0; i < attr->num_values; i ++)
4885 {
4886 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL,
4887 attr->values[i].string.text,
4888 scheme, sizeof(scheme),
4889 userpass, sizeof(userpass),
4890 hostname, sizeof(hostname),
4891 &port, resource, sizeof(resource));
4892
4893 if (uri_status < HTTP_URI_OK)
4894 {
4895 valid = 0;
4896
4897 if (print)
4898 print_test_error("\"%s\": Bad URI value \"%s\" - %s "
4899 "(RFC 2911 section 4.1.5).", attr->name,
4900 attr->values[i].string.text,
4901 URIStatusStrings[uri_status -
4902 HTTP_URI_OVERFLOW]);
4903 else
4904 break;
4905 }
4906
4907 if (strlen(attr->values[i].string.text) > 1023)
4908 {
4909 valid = 0;
4910
4911 if (print)
4912 print_test_error("\"%s\": Bad URI value \"%s\" - bad length %d "
4913 "(RFC 2911 section 4.1.5).", attr->name,
4914 attr->values[i].string.text,
4915 (int)strlen(attr->values[i].string.text));
4916 else
4917 break;
4918 }
4919 }
4920 break;
4921
4922 case IPP_TAG_URISCHEME :
4923 for (i = 0; i < attr->num_values; i ++)
4924 {
4925 ptr = attr->values[i].string.text;
4926 if (islower(*ptr & 255))
4927 {
4928 for (ptr ++; *ptr; ptr ++)
4929 if (!islower(*ptr & 255) && !isdigit(*ptr & 255) &&
4930 *ptr != '+' && *ptr != '-' && *ptr != '.')
4931 break;
4932 }
4933
4934 if (*ptr || ptr == attr->values[i].string.text)
4935 {
4936 valid = 0;
4937
4938 if (print)
4939 print_test_error("\"%s\": Bad uriScheme value \"%s\" - bad "
4940 "characters (RFC 2911 section 4.1.6).",
4941 attr->name, attr->values[i].string.text);
4942 else
4943 break;
4944 }
4945
4946 if ((ptr - attr->values[i].string.text) > 63)
4947 {
4948 valid = 0;
4949
4950 if (print)
4951 print_test_error("\"%s\": Bad uriScheme value \"%s\" - bad "
4952 "length %d (RFC 2911 section 4.1.6).",
4953 attr->name, attr->values[i].string.text,
4954 (int)strlen(attr->values[i].string.text));
4955 else
4956 break;
4957 }
4958 }
4959 break;
4960
4961 case IPP_TAG_CHARSET :
4962 for (i = 0; i < attr->num_values; i ++)
4963 {
4964 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
4965 if (!isprint(*ptr & 255) || isupper(*ptr & 255) ||
4966 isspace(*ptr & 255))
4967 break;
4968
4969 if (*ptr || ptr == attr->values[i].string.text)
4970 {
4971 valid = 0;
4972
4973 if (print)
4974 print_test_error("\"%s\": Bad charset value \"%s\" - bad "
4975 "characters (RFC 2911 section 4.1.7).",
4976 attr->name, attr->values[i].string.text);
4977 else
4978 break;
4979 }
4980
4981 if ((ptr - attr->values[i].string.text) > 40)
4982 {
4983 valid = 0;
4984
4985 if (print)
4986 print_test_error("\"%s\": Bad charset value \"%s\" - bad "
4987 "length %d (RFC 2911 section 4.1.7).",
4988 attr->name, attr->values[i].string.text,
4989 (int)strlen(attr->values[i].string.text));
4990 else
4991 break;
4992 }
4993 }
4994 break;
4995
4996 case IPP_TAG_LANGUAGE :
4997 /*
4998 * The following regular expression is derived from the ABNF for
4999 * language tags in RFC 4646. All I can say is that this is the
5000 * easiest way to check the values...
5001 */
5002
5003 if ((i = regcomp(&re,
5004 "^("
5005 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
5006 /* language */
5007 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
5008 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
5009 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
5010 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
5011 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
5012 "|"
5013 "x(-[a-z0-9]{1,8})+" /* privateuse */
5014 "|"
5015 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
5016 ")$",
5017 REG_NOSUB | REG_EXTENDED)) != 0)
5018 {
5019 char temp[256]; /* Temporary error string */
5020
5021 regerror(i, &re, temp, sizeof(temp));
5022 print_fatal_error("Unable to compile naturalLanguage regular "
5023 "expression: %s.", temp);
5024 }
5025
5026 for (i = 0; i < attr->num_values; i ++)
5027 {
5028 if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
5029 {
5030 valid = 0;
5031
5032 if (print)
5033 print_test_error("\"%s\": Bad naturalLanguage value \"%s\" - bad "
5034 "characters (RFC 2911 section 4.1.8).",
5035 attr->name, attr->values[i].string.text);
5036 else
5037 break;
5038 }
5039
5040 if (strlen(attr->values[i].string.text) > 63)
5041 {
5042 valid = 0;
5043
5044 if (print)
5045 print_test_error("\"%s\": Bad naturalLanguage value \"%s\" - bad "
5046 "length %d (RFC 2911 section 4.1.8).",
5047 attr->name, attr->values[i].string.text,
5048 (int)strlen(attr->values[i].string.text));
5049 else
5050 break;
5051 }
5052 }
5053
5054 regfree(&re);
5055 break;
5056
5057 case IPP_TAG_MIMETYPE :
5058 /*
5059 * The following regular expression is derived from the ABNF for
5060 * language tags in RFC 2045 and 4288. All I can say is that this is
5061 * the easiest way to check the values...
5062 */
5063
5064 if ((i = regcomp(&re,
5065 "^"
5066 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
5067 "/"
5068 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
5069 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
5070 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
5071 /* value */
5072 "$",
5073 REG_NOSUB | REG_EXTENDED)) != 0)
5074 {
5075 char temp[256]; /* Temporary error string */
5076
5077 regerror(i, &re, temp, sizeof(temp));
5078 print_fatal_error("Unable to compile mimeMediaType regular "
5079 "expression: %s.", temp);
5080 }
5081
5082 for (i = 0; i < attr->num_values; i ++)
5083 {
5084 if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
5085 {
5086 valid = 0;
5087
5088 if (print)
5089 print_test_error("\"%s\": Bad mimeMediaType value \"%s\" - bad "
5090 "characters (RFC 2911 section 4.1.9).",
5091 attr->name, attr->values[i].string.text);
5092 else
5093 break;
5094 }
5095
5096 if (strlen(attr->values[i].string.text) > 255)
5097 {
5098 valid = 0;
5099
5100 if (print)
5101 print_test_error("\"%s\": Bad mimeMediaType value \"%s\" - bad "
5102 "length %d (RFC 2911 section 4.1.9).",
5103 attr->name, attr->values[i].string.text,
5104 (int)strlen(attr->values[i].string.text));
5105 else
5106 break;
5107 }
5108 }
5109 break;
5110
5111 default :
5112 break;
5113 }
5114
5115 return (valid);
5116 }
5117
5118
5119 /*
5120 * 'with_value()' - Test a WITH-VALUE predicate.
5121 */
5122
5123 static int /* O - 1 on match, 0 on non-match */
5124 with_value(char *value, /* I - Value string */
5125 int regex, /* I - Value is a regular expression */
5126 ipp_attribute_t *attr, /* I - Attribute to compare */
5127 int report, /* I - 1 = report failures */
5128 char *matchbuf, /* I - Buffer to hold matching value */
5129 size_t matchlen) /* I - Length of match buffer */
5130 {
5131 int i; /* Looping var */
5132 char *valptr; /* Pointer into value */
5133
5134
5135 *matchbuf = '\0';
5136
5137 /*
5138 * NULL matches everything.
5139 */
5140
5141 if (!value || !*value)
5142 return (1);
5143
5144 /*
5145 * Compare the value string to the attribute value.
5146 */
5147
5148 switch (attr->value_tag)
5149 {
5150 case IPP_TAG_INTEGER :
5151 case IPP_TAG_ENUM :
5152 for (i = 0; i < attr->num_values; i ++)
5153 {
5154 char op, /* Comparison operator */
5155 *nextptr; /* Next pointer */
5156 int intvalue; /* Integer value */
5157
5158
5159 valptr = value;
5160
5161 while (isspace(*valptr & 255) || isdigit(*valptr & 255) ||
5162 *valptr == '-' || *valptr == ',' || *valptr == '<' ||
5163 *valptr == '=' || *valptr == '>')
5164 {
5165 op = '=';
5166 while (*valptr && !isdigit(*valptr & 255) && *valptr != '-')
5167 {
5168 if (*valptr == '<' || *valptr == '>' || *valptr == '=')
5169 op = *valptr;
5170 valptr ++;
5171 }
5172
5173 if (!*valptr)
5174 break;
5175
5176 intvalue = strtol(valptr, &nextptr, 0);
5177 if (nextptr == valptr)
5178 break;
5179 valptr = nextptr;
5180
5181 switch (op)
5182 {
5183 case '=' :
5184 if (attr->values[i].integer == intvalue)
5185 {
5186 snprintf(matchbuf, matchlen, "%d", attr->values[i].integer);
5187 return (1);
5188 }
5189 break;
5190 case '<' :
5191 if (attr->values[i].integer < intvalue)
5192 {
5193 snprintf(matchbuf, matchlen, "%d", attr->values[i].integer);
5194 return (1);
5195 }
5196 break;
5197 case '>' :
5198 if (attr->values[i].integer > intvalue)
5199 {
5200 snprintf(matchbuf, matchlen, "%d", attr->values[i].integer);
5201 return (1);
5202 }
5203 break;
5204 }
5205 }
5206 }
5207
5208 if (report)
5209 {
5210 for (i = 0; i < attr->num_values; i ++)
5211 print_test_error("GOT: %s=%d", attr->name, attr->values[i].integer);
5212 }
5213 break;
5214
5215 case IPP_TAG_RANGE :
5216 for (i = 0; i < attr->num_values; i ++)
5217 {
5218 char op, /* Comparison operator */
5219 *nextptr; /* Next pointer */
5220 int intvalue; /* Integer value */
5221
5222
5223 valptr = value;
5224
5225 while (isspace(*valptr & 255) || isdigit(*valptr & 255) ||
5226 *valptr == '-' || *valptr == ',' || *valptr == '<' ||
5227 *valptr == '=' || *valptr == '>')
5228 {
5229 op = '=';
5230 while (*valptr && !isdigit(*valptr & 255) && *valptr != '-')
5231 {
5232 if (*valptr == '<' || *valptr == '>' || *valptr == '=')
5233 op = *valptr;
5234 valptr ++;
5235 }
5236
5237 if (!*valptr)
5238 break;
5239
5240 intvalue = strtol(valptr, &nextptr, 0);
5241 if (nextptr == valptr)
5242 break;
5243 valptr = nextptr;
5244
5245 switch (op)
5246 {
5247 case '=' :
5248 if (attr->values[i].range.lower == intvalue ||
5249 attr->values[i].range.upper == intvalue)
5250 {
5251 snprintf(matchbuf, matchlen, "%d-%d",
5252 attr->values[i].range.lower,
5253 attr->values[i].range.upper);
5254 return (1);
5255 }
5256 break;
5257 case '<' :
5258 if (attr->values[i].range.upper < intvalue)
5259 {
5260 snprintf(matchbuf, matchlen, "%d-%d",
5261 attr->values[i].range.lower,
5262 attr->values[i].range.upper);
5263 return (1);
5264 }
5265 break;
5266 case '>' :
5267 if (attr->values[i].range.lower > 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 }
5276 }
5277 }
5278
5279 if (report)
5280 {
5281 for (i = 0; i < attr->num_values; i ++)
5282 print_test_error("GOT: %s=%d-%d", attr->name,
5283 attr->values[i].range.lower,
5284 attr->values[i].range.upper);
5285 }
5286 break;
5287
5288 case IPP_TAG_BOOLEAN :
5289 for (i = 0; i < attr->num_values; i ++)
5290 {
5291 if (!strcmp(value, "true") == attr->values[i].boolean)
5292 {
5293 strlcpy(matchbuf, value, matchlen);
5294 return (1);
5295 }
5296 }
5297
5298 if (report)
5299 {
5300 for (i = 0; i < attr->num_values; i ++)
5301 print_test_error("GOT: %s=%s", attr->name,
5302 attr->values[i].boolean ? "true" : "false");
5303 }
5304 break;
5305
5306 case IPP_TAG_NOVALUE :
5307 case IPP_TAG_UNKNOWN :
5308 return (1);
5309
5310 case IPP_TAG_CHARSET :
5311 case IPP_TAG_KEYWORD :
5312 case IPP_TAG_LANGUAGE :
5313 case IPP_TAG_MIMETYPE :
5314 case IPP_TAG_NAME :
5315 case IPP_TAG_NAMELANG :
5316 case IPP_TAG_TEXT :
5317 case IPP_TAG_TEXTLANG :
5318 case IPP_TAG_URI :
5319 case IPP_TAG_URISCHEME :
5320 if (regex)
5321 {
5322 /*
5323 * Value is an extended, case-sensitive POSIX regular expression...
5324 */
5325
5326 regex_t re; /* Regular expression */
5327
5328 if ((i = regcomp(&re, value, REG_EXTENDED | REG_NOSUB)) != 0)
5329 {
5330 char temp[256]; /* Temporary string */
5331
5332 regerror(i, &re, temp, sizeof(temp));
5333
5334 print_fatal_error("Unable to compile WITH-VALUE regular expression "
5335 "\"%s\" - %s", value, temp);
5336 return (0);
5337 }
5338
5339 /*
5340 * See if ALL of the values match the given regular expression.
5341 */
5342
5343 for (i = 0; i < attr->num_values; i ++)
5344 {
5345 if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
5346 {
5347 if (report)
5348 print_test_error("GOT: %s=\"%s\"", attr->name,
5349 attr->values[i].string.text);
5350 else
5351 break;
5352 }
5353 }
5354
5355 regfree(&re);
5356
5357 if (i == attr->num_values)
5358 strlcpy(matchbuf, attr->values[0].string.text, matchlen);
5359
5360 return (i == attr->num_values);
5361 }
5362 else
5363 {
5364 /*
5365 * Value is a literal string, see if at least one value matches the
5366 * literal string...
5367 */
5368
5369 for (i = 0; i < attr->num_values; i ++)
5370 {
5371 if (!strcmp(value, attr->values[i].string.text))
5372 {
5373 strlcpy(matchbuf, attr->values[i].string.text, matchlen);
5374 return (1);
5375 }
5376 }
5377
5378 if (report)
5379 {
5380 for (i = 0; i < attr->num_values; i ++)
5381 print_test_error("GOT: %s=\"%s\"", attr->name,
5382 attr->values[i].string.text);
5383 }
5384 }
5385 break;
5386
5387 default :
5388 break;
5389 }
5390
5391 return (0);
5392 }
5393
5394
5395 /*
5396 * End of "$Id$".
5397 */