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