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