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