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