]> git.ipfire.org Git - thirdparty/cups.git/blob - test/ipptool.c
46dd44d1876264a3bd3607aeb0c9539aed70e848
[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 printf("\nSummary: %d tests, %d passed, %d failed, %d skipped\n"
701 "Score: %d%%\n", TestCount, PassCount, FailCount, SkipCount,
702 100 * (PassCount + SkipCount) / TestCount);
703 }
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 printf("\"%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 printf(" %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 cupsFileFlush(cupsFileStdout());
2634 }
2635
2636 if ((skip_previous && !prev_pass) || skip_test)
2637 {
2638 SkipCount ++;
2639
2640 ippDelete(request);
2641 request = NULL;
2642
2643 if (Output == _CUPS_OUTPUT_PLIST)
2644 {
2645 cupsFilePuts(outfile, "<key>Successful</key>\n");
2646 cupsFilePuts(outfile, "<true />\n");
2647 cupsFilePuts(outfile, "<key>Skipped</key>\n");
2648 cupsFilePuts(outfile, "<true />\n");
2649 cupsFilePuts(outfile, "<key>StatusCode</key>\n");
2650 print_xml_string(outfile, "string", "skip");
2651 cupsFilePuts(outfile, "<key>ResponseAttributes</key>\n");
2652 cupsFilePuts(outfile, "<dict />\n");
2653 }
2654
2655 if (Output == _CUPS_OUTPUT_TEST || (Output == _CUPS_OUTPUT_PLIST && outfile != cupsFileStdout()))
2656 cupsFilePuts(cupsFileStdout(), "SKIP]");
2657
2658 goto skip_error;
2659 }
2660
2661 PasswordTries = 0;
2662
2663 do
2664 {
2665 if (delay > 0)
2666 usleep(delay);
2667
2668 delay = repeat_interval;
2669 repeat_count ++;
2670
2671 status = HTTP_STATUS_OK;
2672
2673 if (transfer == _CUPS_TRANSFER_CHUNKED ||
2674 (transfer == _CUPS_TRANSFER_AUTO && filename[0]))
2675 {
2676 /*
2677 * Send request using chunking - a 0 length means "chunk".
2678 */
2679
2680 length = 0;
2681 }
2682 else
2683 {
2684 /*
2685 * Send request using content length...
2686 */
2687
2688 length = ippLength(request);
2689
2690 if (filename[0] && (reqfile = cupsFileOpen(filename, "r")) != NULL)
2691 {
2692 /*
2693 * Read the file to get the uncompressed file size...
2694 */
2695
2696 while ((bytes = cupsFileRead(reqfile, buffer, sizeof(buffer))) > 0)
2697 length += (size_t)bytes;
2698
2699 cupsFileClose(reqfile);
2700 }
2701 }
2702
2703 /*
2704 * Send the request...
2705 */
2706
2707 response = NULL;
2708 repeat_test = 0;
2709 prev_pass = 1;
2710
2711 if (status != HTTP_STATUS_ERROR)
2712 {
2713 while (!response && !Cancel && prev_pass)
2714 {
2715 status = cupsSendRequest(http, request, resource, length);
2716
2717 #ifdef HAVE_LIBZ
2718 if (compression[0])
2719 httpSetField(http, HTTP_FIELD_CONTENT_ENCODING, compression);
2720 #endif /* HAVE_LIBZ */
2721
2722 if (!Cancel && status == HTTP_STATUS_CONTINUE &&
2723 request->state == IPP_DATA && filename[0])
2724 {
2725 if ((reqfile = cupsFileOpen(filename, "r")) != NULL)
2726 {
2727 while (!Cancel &&
2728 (bytes = cupsFileRead(reqfile, buffer, sizeof(buffer))) > 0)
2729 if ((status = cupsWriteRequestData(http, buffer, (size_t)bytes)) != HTTP_STATUS_CONTINUE)
2730 break;
2731
2732 cupsFileClose(reqfile);
2733 }
2734 else
2735 {
2736 snprintf(buffer, sizeof(buffer), "%s: %s", filename,
2737 strerror(errno));
2738 _cupsSetError(IPP_INTERNAL_ERROR, buffer, 0);
2739
2740 status = HTTP_STATUS_ERROR;
2741 }
2742 }
2743
2744 /*
2745 * Get the server's response...
2746 */
2747
2748 if (!Cancel && status != HTTP_STATUS_ERROR)
2749 {
2750 response = cupsGetResponse(http, resource);
2751 status = httpGetStatus(http);
2752 }
2753
2754 if (!Cancel && status == HTTP_STATUS_ERROR && http->error != EINVAL &&
2755 #ifdef WIN32
2756 http->error != WSAETIMEDOUT)
2757 #else
2758 http->error != ETIMEDOUT)
2759 #endif /* WIN32 */
2760 {
2761 if (httpReconnect2(http, 30000, NULL))
2762 prev_pass = 0;
2763 }
2764 else if (status == HTTP_STATUS_ERROR || status == HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED)
2765 {
2766 prev_pass = 0;
2767 break;
2768 }
2769 else if (status != HTTP_STATUS_OK)
2770 {
2771 httpFlush(http);
2772
2773 if (status == HTTP_STATUS_UNAUTHORIZED)
2774 continue;
2775
2776 break;
2777 }
2778 }
2779 }
2780
2781 if (!Cancel && status == HTTP_STATUS_ERROR && http->error != EINVAL &&
2782 #ifdef WIN32
2783 http->error != WSAETIMEDOUT)
2784 #else
2785 http->error != ETIMEDOUT)
2786 #endif /* WIN32 */
2787 {
2788 if (httpReconnect2(http, 30000, NULL))
2789 prev_pass = 0;
2790 }
2791 else if (status == HTTP_STATUS_ERROR)
2792 {
2793 if (!Cancel)
2794 httpReconnect2(http, 30000, NULL);
2795
2796 prev_pass = 0;
2797 }
2798 else if (status != HTTP_STATUS_OK)
2799 {
2800 httpFlush(http);
2801 prev_pass = 0;
2802 }
2803
2804 /*
2805 * Check results of request...
2806 */
2807
2808 cupsArrayClear(errors);
2809
2810 if (http->version != HTTP_1_1)
2811 add_stringf(errors, "Bad HTTP version (%d.%d)", http->version / 100,
2812 http->version % 100);
2813
2814 if (!response)
2815 {
2816 /*
2817 * No response, log error...
2818 */
2819
2820 add_stringf(errors, "IPP request failed with status %s (%s)",
2821 ippErrorString(cupsLastError()),
2822 cupsLastErrorString());
2823 }
2824 else
2825 {
2826 /*
2827 * Collect common attribute values...
2828 */
2829
2830 if ((attrptr = ippFindAttribute(response, "job-id",
2831 IPP_TAG_INTEGER)) != NULL)
2832 {
2833 snprintf(temp, sizeof(temp), "%d", attrptr->values[0].integer);
2834 set_variable(outfile, vars, "job-id", temp);
2835 }
2836
2837 if ((attrptr = ippFindAttribute(response, "job-uri",
2838 IPP_TAG_URI)) != NULL)
2839 set_variable(outfile, vars, "job-uri", attrptr->values[0].string.text);
2840
2841 if ((attrptr = ippFindAttribute(response, "notify-subscription-id",
2842 IPP_TAG_INTEGER)) != NULL)
2843 {
2844 snprintf(temp, sizeof(temp), "%d", attrptr->values[0].integer);
2845 set_variable(outfile, vars, "notify-subscription-id", temp);
2846 }
2847
2848 /*
2849 * Check response, validating groups and attributes and logging errors
2850 * as needed...
2851 */
2852
2853 if (response->state != IPP_DATA)
2854 add_stringf(errors,
2855 "Missing end-of-attributes-tag in response "
2856 "(RFC 2910 section 3.5.1)");
2857
2858 if (version &&
2859 (response->request.status.version[0] != (version / 10) ||
2860 response->request.status.version[1] != (version % 10)))
2861 add_stringf(errors,
2862 "Bad version %d.%d in response - expected %d.%d "
2863 "(RFC 2911 section 3.1.8).",
2864 response->request.status.version[0],
2865 response->request.status.version[1],
2866 version / 10, version % 10);
2867
2868 if (response->request.status.request_id != request_id)
2869 add_stringf(errors,
2870 "Bad request ID %d in response - expected %d "
2871 "(RFC 2911 section 3.1.1)",
2872 response->request.status.request_id, request_id);
2873
2874 attrptr = response->attrs;
2875 if (!attrptr)
2876 add_stringf(errors,
2877 "Missing first attribute \"attributes-charset "
2878 "(charset)\" in group operation-attributes-tag "
2879 "(RFC 2911 section 3.1.4).");
2880 else
2881 {
2882 if (!attrptr->name ||
2883 attrptr->value_tag != IPP_TAG_CHARSET ||
2884 attrptr->group_tag != IPP_TAG_OPERATION ||
2885 attrptr->num_values != 1 ||
2886 strcmp(attrptr->name, "attributes-charset"))
2887 add_stringf(errors,
2888 "Bad first attribute \"%s (%s%s)\" in group %s, "
2889 "expected \"attributes-charset (charset)\" in "
2890 "group operation-attributes-tag (RFC 2911 section "
2891 "3.1.4).",
2892 attrptr->name ? attrptr->name : "(null)",
2893 attrptr->num_values > 1 ? "1setOf " : "",
2894 ippTagString(attrptr->value_tag),
2895 ippTagString(attrptr->group_tag));
2896
2897 attrptr = attrptr->next;
2898 if (!attrptr)
2899 add_stringf(errors,
2900 "Missing second attribute \"attributes-natural-"
2901 "language (naturalLanguage)\" in group "
2902 "operation-attributes-tag (RFC 2911 section "
2903 "3.1.4).");
2904 else if (!attrptr->name ||
2905 attrptr->value_tag != IPP_TAG_LANGUAGE ||
2906 attrptr->group_tag != IPP_TAG_OPERATION ||
2907 attrptr->num_values != 1 ||
2908 strcmp(attrptr->name, "attributes-natural-language"))
2909 add_stringf(errors,
2910 "Bad first attribute \"%s (%s%s)\" in group %s, "
2911 "expected \"attributes-natural-language "
2912 "(naturalLanguage)\" in group "
2913 "operation-attributes-tag (RFC 2911 section "
2914 "3.1.4).",
2915 attrptr->name ? attrptr->name : "(null)",
2916 attrptr->num_values > 1 ? "1setOf " : "",
2917 ippTagString(attrptr->value_tag),
2918 ippTagString(attrptr->group_tag));
2919 }
2920
2921 if ((attrptr = ippFindAttribute(response, "status-message",
2922 IPP_TAG_ZERO)) != NULL)
2923 {
2924 if (attrptr->value_tag != IPP_TAG_TEXT)
2925 add_stringf(errors,
2926 "status-message (text(255)) has wrong value tag "
2927 "%s (RFC 2911 section 3.1.6.2).",
2928 ippTagString(attrptr->value_tag));
2929 if (attrptr->group_tag != IPP_TAG_OPERATION)
2930 add_stringf(errors,
2931 "status-message (text(255)) has wrong group tag "
2932 "%s (RFC 2911 section 3.1.6.2).",
2933 ippTagString(attrptr->group_tag));
2934 if (attrptr->num_values != 1)
2935 add_stringf(errors,
2936 "status-message (text(255)) has %d values "
2937 "(RFC 2911 section 3.1.6.2).",
2938 attrptr->num_values);
2939 if (attrptr->value_tag == IPP_TAG_TEXT &&
2940 strlen(attrptr->values[0].string.text) > 255)
2941 add_stringf(errors,
2942 "status-message (text(255)) has bad length %d"
2943 " (RFC 2911 section 3.1.6.2).",
2944 (int)strlen(attrptr->values[0].string.text));
2945 }
2946
2947 if ((attrptr = ippFindAttribute(response, "detailed-status-message",
2948 IPP_TAG_ZERO)) != NULL)
2949 {
2950 if (attrptr->value_tag != IPP_TAG_TEXT)
2951 add_stringf(errors,
2952 "detailed-status-message (text(MAX)) has wrong "
2953 "value tag %s (RFC 2911 section 3.1.6.3).",
2954 ippTagString(attrptr->value_tag));
2955 if (attrptr->group_tag != IPP_TAG_OPERATION)
2956 add_stringf(errors,
2957 "detailed-status-message (text(MAX)) has wrong "
2958 "group tag %s (RFC 2911 section 3.1.6.3).",
2959 ippTagString(attrptr->group_tag));
2960 if (attrptr->num_values != 1)
2961 add_stringf(errors,
2962 "detailed-status-message (text(MAX)) has %d values"
2963 " (RFC 2911 section 3.1.6.3).",
2964 attrptr->num_values);
2965 if (attrptr->value_tag == IPP_TAG_TEXT &&
2966 strlen(attrptr->values[0].string.text) > 1023)
2967 add_stringf(errors,
2968 "detailed-status-message (text(MAX)) has bad "
2969 "length %d (RFC 2911 section 3.1.6.3).",
2970 (int)strlen(attrptr->values[0].string.text));
2971 }
2972
2973 a = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2974
2975 for (attrptr = response->attrs,
2976 group = attrptr ? attrptr->group_tag : IPP_TAG_ZERO;
2977 attrptr;
2978 attrptr = attrptr->next)
2979 {
2980 if (attrptr->group_tag != group)
2981 {
2982 int out_of_order = 0; /* Are attribute groups out-of-order? */
2983 cupsArrayClear(a);
2984
2985 switch (attrptr->group_tag)
2986 {
2987 case IPP_TAG_ZERO :
2988 break;
2989
2990 case IPP_TAG_OPERATION :
2991 out_of_order = 1;
2992 break;
2993
2994 case IPP_TAG_UNSUPPORTED_GROUP :
2995 if (group != IPP_TAG_OPERATION)
2996 out_of_order = 1;
2997 break;
2998
2999 case IPP_TAG_JOB :
3000 case IPP_TAG_PRINTER :
3001 if (group != IPP_TAG_OPERATION &&
3002 group != IPP_TAG_UNSUPPORTED_GROUP)
3003 out_of_order = 1;
3004 break;
3005
3006 case IPP_TAG_SUBSCRIPTION :
3007 if (group > attrptr->group_tag &&
3008 group != IPP_TAG_DOCUMENT)
3009 out_of_order = 1;
3010 break;
3011
3012 default :
3013 if (group > attrptr->group_tag)
3014 out_of_order = 1;
3015 break;
3016 }
3017
3018 if (out_of_order)
3019 add_stringf(errors, "Attribute groups out of order (%s < %s)",
3020 ippTagString(attrptr->group_tag),
3021 ippTagString(group));
3022
3023 if (attrptr->group_tag != IPP_TAG_ZERO)
3024 group = attrptr->group_tag;
3025 }
3026
3027 validate_attr(outfile, errors, attrptr);
3028
3029 if (attrptr->name)
3030 {
3031 if (cupsArrayFind(a, attrptr->name))
3032 add_stringf(errors, "Duplicate \"%s\" attribute in %s group",
3033 attrptr->name, ippTagString(group));
3034
3035 cupsArrayAdd(a, attrptr->name);
3036 }
3037 }
3038
3039 cupsArrayDelete(a);
3040
3041 /*
3042 * Now check the test-defined expected status-code and attribute
3043 * values...
3044 */
3045
3046 for (i = 0, status_ok = 0; i < num_statuses; i ++)
3047 {
3048 if (statuses[i].if_defined &&
3049 !get_variable(vars, statuses[i].if_defined))
3050 continue;
3051
3052 if (statuses[i].if_not_defined &&
3053 get_variable(vars, statuses[i].if_not_defined))
3054 continue;
3055
3056 if (ippGetStatusCode(response) == statuses[i].status)
3057 {
3058 status_ok = 1;
3059
3060 if (statuses[i].repeat_match && repeat_count < statuses[i].repeat_limit)
3061 repeat_test = 1;
3062
3063 if (statuses[i].define_match)
3064 set_variable(outfile, vars, statuses[i].define_match, "1");
3065 }
3066 else
3067 {
3068 if (statuses[i].repeat_no_match && repeat_count < statuses[i].repeat_limit)
3069 repeat_test = 1;
3070
3071 if (statuses[i].define_no_match)
3072 {
3073 set_variable(outfile, vars, statuses[i].define_no_match, "1");
3074 status_ok = 1;
3075 }
3076 }
3077 }
3078
3079 if (!status_ok && num_statuses > 0)
3080 {
3081 for (i = 0; i < num_statuses; i ++)
3082 {
3083 if (statuses[i].if_defined &&
3084 !get_variable(vars, statuses[i].if_defined))
3085 continue;
3086
3087 if (statuses[i].if_not_defined &&
3088 get_variable(vars, statuses[i].if_not_defined))
3089 continue;
3090
3091 if (!statuses[i].repeat_match || repeat_count >= statuses[i].repeat_limit)
3092 add_stringf(errors, "EXPECTED: STATUS %s (got %s)",
3093 ippErrorString(statuses[i].status),
3094 ippErrorString(cupsLastError()));
3095 }
3096
3097 if ((attrptr = ippFindAttribute(response, "status-message",
3098 IPP_TAG_TEXT)) != NULL)
3099 add_stringf(errors, "status-message=\"%s\"",
3100 attrptr->values[0].string.text);
3101 }
3102
3103 for (i = num_expects, expect = expects; i > 0; i --, expect ++)
3104 {
3105 if (expect->if_defined && !get_variable(vars, expect->if_defined))
3106 continue;
3107
3108 if (expect->if_not_defined &&
3109 get_variable(vars, expect->if_not_defined))
3110 continue;
3111
3112 found = ippFindAttribute(response, expect->name, IPP_TAG_ZERO);
3113
3114 do
3115 {
3116 if ((found && expect->not_expect) ||
3117 (!found && !(expect->not_expect || expect->optional)) ||
3118 (found && !expect_matches(expect, found->value_tag)) ||
3119 (found && expect->in_group &&
3120 found->group_tag != expect->in_group))
3121 {
3122 if (expect->define_no_match)
3123 set_variable(outfile, vars, expect->define_no_match, "1");
3124 else if (!expect->define_match && !expect->define_value)
3125 {
3126 if (found && expect->not_expect)
3127 add_stringf(errors, "NOT EXPECTED: %s", expect->name);
3128 else if (!found && !(expect->not_expect || expect->optional))
3129 add_stringf(errors, "EXPECTED: %s", expect->name);
3130 else if (found)
3131 {
3132 if (!expect_matches(expect, found->value_tag))
3133 add_stringf(errors, "EXPECTED: %s OF-TYPE %s (got %s)",
3134 expect->name, expect->of_type,
3135 ippTagString(found->value_tag));
3136
3137 if (expect->in_group && found->group_tag != expect->in_group)
3138 add_stringf(errors, "EXPECTED: %s IN-GROUP %s (got %s).",
3139 expect->name, ippTagString(expect->in_group),
3140 ippTagString(found->group_tag));
3141 }
3142 }
3143
3144 if (expect->repeat_no_match && repeat_count < expect->repeat_limit)
3145 repeat_test = 1;
3146
3147 break;
3148 }
3149
3150 if (found)
3151 ippAttributeString(found, buffer, sizeof(buffer));
3152
3153 if (found && expect->with_value_from && !with_value_from(NULL, ippFindAttribute(response, expect->with_value_from, IPP_TAG_ZERO), found, buffer, sizeof(buffer)))
3154 {
3155 if (expect->define_no_match)
3156 set_variable(outfile, vars, expect->define_no_match, "1");
3157 else if (!expect->define_match && !expect->define_value && ((!expect->repeat_match && !expect->repeat_no_match) || repeat_count >= expect->repeat_limit))
3158 {
3159 add_stringf(errors, "EXPECTED: %s WITH-VALUES-FROM %s", expect->name, expect->with_value_from);
3160
3161 with_value_from(errors, ippFindAttribute(response, expect->with_value_from, IPP_TAG_ZERO), found, buffer, sizeof(buffer));
3162 }
3163
3164 if (expect->repeat_no_match && repeat_count < expect->repeat_limit)
3165 repeat_test = 1;
3166
3167 break;
3168 }
3169 else if (found && !with_value(outfile, NULL, expect->with_value, expect->with_flags, found, buffer, sizeof(buffer)))
3170 {
3171 if (expect->define_no_match)
3172 set_variable(outfile, vars, expect->define_no_match, "1");
3173 else if (!expect->define_match && !expect->define_value &&
3174 !expect->repeat_match && (!expect->repeat_no_match || repeat_count >= expect->repeat_limit))
3175 {
3176 if (expect->with_flags & _CUPS_WITH_REGEX)
3177 add_stringf(errors, "EXPECTED: %s %s /%s/",
3178 expect->name,
3179 (expect->with_flags & _CUPS_WITH_ALL) ?
3180 "WITH-ALL-VALUES" : "WITH-VALUE",
3181 expect->with_value);
3182 else
3183 add_stringf(errors, "EXPECTED: %s %s \"%s\"",
3184 expect->name,
3185 (expect->with_flags & _CUPS_WITH_ALL) ?
3186 "WITH-ALL-VALUES" : "WITH-VALUE",
3187 expect->with_value);
3188
3189 with_value(outfile, errors, expect->with_value, expect->with_flags, found,
3190 buffer, sizeof(buffer));
3191 }
3192
3193 if (expect->repeat_no_match &&
3194 repeat_count < expect->repeat_limit)
3195 repeat_test = 1;
3196
3197 break;
3198 }
3199
3200 if (found && expect->count > 0 &&
3201 found->num_values != expect->count)
3202 {
3203 if (expect->define_no_match)
3204 set_variable(outfile, vars, expect->define_no_match, "1");
3205 else if (!expect->define_match && !expect->define_value)
3206 {
3207 add_stringf(errors, "EXPECTED: %s COUNT %d (got %d)", expect->name,
3208 expect->count, found->num_values);
3209 }
3210
3211 if (expect->repeat_no_match &&
3212 repeat_count < expect->repeat_limit)
3213 repeat_test = 1;
3214
3215 break;
3216 }
3217
3218 if (found && expect->same_count_as)
3219 {
3220 attrptr = ippFindAttribute(response, expect->same_count_as,
3221 IPP_TAG_ZERO);
3222
3223 if (!attrptr || attrptr->num_values != found->num_values)
3224 {
3225 if (expect->define_no_match)
3226 set_variable(outfile, vars, expect->define_no_match, "1");
3227 else if (!expect->define_match && !expect->define_value)
3228 {
3229 if (!attrptr)
3230 add_stringf(errors,
3231 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
3232 "(not returned)", expect->name,
3233 found->num_values, expect->same_count_as);
3234 else if (attrptr->num_values != found->num_values)
3235 add_stringf(errors,
3236 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
3237 "(%d values)", expect->name, found->num_values,
3238 expect->same_count_as, attrptr->num_values);
3239 }
3240
3241 if (expect->repeat_no_match &&
3242 repeat_count < expect->repeat_limit)
3243 repeat_test = 1;
3244
3245 break;
3246 }
3247 }
3248
3249 if (found && expect->define_match)
3250 set_variable(outfile, vars, expect->define_match, "1");
3251
3252 if (found && expect->define_value)
3253 {
3254 if (!expect->with_value)
3255 {
3256 int last = ippGetCount(found) - 1;
3257 /* Last element in attribute */
3258
3259 switch (ippGetValueTag(found))
3260 {
3261 case IPP_TAG_ENUM :
3262 case IPP_TAG_INTEGER :
3263 snprintf(buffer, sizeof(buffer), "%d", ippGetInteger(found, last));
3264 break;
3265
3266 case IPP_TAG_BOOLEAN :
3267 if (ippGetBoolean(found, last))
3268 strlcpy(buffer, "true", sizeof(buffer));
3269 else
3270 strlcpy(buffer, "false", sizeof(buffer));
3271 break;
3272
3273 case IPP_TAG_RESOLUTION :
3274 {
3275 int xres, /* Horizontal resolution */
3276 yres; /* Vertical resolution */
3277 ipp_res_t units; /* Resolution units */
3278
3279 xres = ippGetResolution(found, last, &yres, &units);
3280
3281 if (xres == yres)
3282 snprintf(buffer, sizeof(buffer), "%d%s", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
3283 else
3284 snprintf(buffer, sizeof(buffer), "%dx%d%s", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
3285 }
3286 break;
3287
3288 case IPP_TAG_CHARSET :
3289 case IPP_TAG_KEYWORD :
3290 case IPP_TAG_LANGUAGE :
3291 case IPP_TAG_MIMETYPE :
3292 case IPP_TAG_NAME :
3293 case IPP_TAG_NAMELANG :
3294 case IPP_TAG_TEXT :
3295 case IPP_TAG_TEXTLANG :
3296 case IPP_TAG_URI :
3297 case IPP_TAG_URISCHEME :
3298 strlcpy(buffer, ippGetString(found, last, NULL), sizeof(buffer));
3299 break;
3300
3301 default :
3302 ippAttributeString(found, buffer, sizeof(buffer));
3303 break;
3304 }
3305 }
3306
3307 set_variable(outfile, vars, expect->define_value, buffer);
3308 }
3309
3310 if (found && expect->repeat_match &&
3311 repeat_count < expect->repeat_limit)
3312 repeat_test = 1;
3313 }
3314 while (expect->expect_all && (found = ippFindNextAttribute(response, expect->name, IPP_TAG_ZERO)) != NULL);
3315 }
3316 }
3317
3318 /*
3319 * If we are going to repeat this test, sleep 1 second so we don't flood
3320 * the printer with requests...
3321 */
3322
3323 if (repeat_test)
3324 {
3325 if (Output == _CUPS_OUTPUT_TEST || (Output == _CUPS_OUTPUT_PLIST && outfile != cupsFileStdout()))
3326 {
3327 cupsFilePrintf(cupsFileStdout(), "%04d]\n", repeat_count);
3328 cupsFileFlush(cupsFileStdout());
3329
3330 if (num_displayed > 0)
3331 {
3332 for (attrptr = ippFirstAttribute(response); attrptr; attrptr = ippNextAttribute(response))
3333 {
3334 const char *attrname = ippGetName(attrptr);
3335 if (attrname)
3336 {
3337 for (i = 0; i < num_displayed; i ++)
3338 {
3339 if (!strcmp(displayed[i], attrname))
3340 {
3341 print_attr(cupsFileStdout(), _CUPS_OUTPUT_TEST, attrptr, NULL);
3342 break;
3343 }
3344 }
3345 }
3346 }
3347 }
3348 }
3349
3350 if (Output == _CUPS_OUTPUT_TEST || (Output == _CUPS_OUTPUT_PLIST && outfile != cupsFileStdout()))
3351 {
3352 cupsFilePrintf(cupsFileStdout(), " %-68.68s [", name);
3353 cupsFileFlush(cupsFileStdout());
3354 }
3355 }
3356 }
3357 while (repeat_test);
3358
3359 ippDelete(request);
3360
3361 request = NULL;
3362
3363 if (cupsArrayCount(errors) > 0)
3364 prev_pass = pass = 0;
3365
3366 if (prev_pass)
3367 PassCount ++;
3368 else
3369 FailCount ++;
3370
3371 if (Output == _CUPS_OUTPUT_PLIST)
3372 {
3373 cupsFilePuts(outfile, "<key>Successful</key>\n");
3374 cupsFilePuts(outfile, prev_pass ? "<true />\n" : "<false />\n");
3375 cupsFilePuts(outfile, "<key>StatusCode</key>\n");
3376 print_xml_string(outfile, "string", ippErrorString(cupsLastError()));
3377 cupsFilePuts(outfile, "<key>ResponseAttributes</key>\n");
3378 cupsFilePuts(outfile, "<array>\n");
3379 cupsFilePuts(outfile, "<dict>\n");
3380 for (attrptr = response ? response->attrs : NULL,
3381 group = attrptr ? attrptr->group_tag : IPP_TAG_ZERO;
3382 attrptr;
3383 attrptr = attrptr->next)
3384 print_attr(outfile, Output, attrptr, &group);
3385 cupsFilePuts(outfile, "</dict>\n");
3386 cupsFilePuts(outfile, "</array>\n");
3387 }
3388
3389 if (Output == _CUPS_OUTPUT_TEST || (Output == _CUPS_OUTPUT_PLIST && outfile != cupsFileStdout()))
3390 {
3391 cupsFilePuts(cupsFileStdout(), prev_pass ? "PASS]" : "FAIL]");
3392
3393 if (!prev_pass || (Verbosity && response))
3394 {
3395 cupsFilePrintf(cupsFileStdout(), " RECEIVED: %lu bytes in response\n", (unsigned long)ippLength(response));
3396 cupsFilePrintf(cupsFileStdout(), " status-code = %s (%s)\n", ippErrorString(cupsLastError()), cupsLastErrorString());
3397
3398 if (Verbosity && response)
3399 {
3400 for (attrptr = response->attrs;
3401 attrptr != NULL;
3402 attrptr = attrptr->next)
3403 print_attr(cupsFileStdout(), _CUPS_OUTPUT_TEST, attrptr, NULL);
3404 }
3405 }
3406 }
3407 else if (!prev_pass && Output != _CUPS_OUTPUT_QUIET)
3408 fprintf(stderr, "%s\n", cupsLastErrorString());
3409
3410 if (prev_pass && Output >= _CUPS_OUTPUT_LIST && !Verbosity &&
3411 num_displayed > 0)
3412 {
3413 size_t width; /* Length of value */
3414
3415 for (i = 0; i < num_displayed; i ++)
3416 {
3417 widths[i] = strlen(displayed[i]);
3418
3419 for (attrptr = ippFindAttribute(response, displayed[i], IPP_TAG_ZERO);
3420 attrptr;
3421 attrptr = ippFindNextAttribute(response, displayed[i],
3422 IPP_TAG_ZERO))
3423 {
3424 width = ippAttributeString(attrptr, NULL, 0);
3425 if (width > widths[i])
3426 widths[i] = width;
3427 }
3428 }
3429
3430 if (Output == _CUPS_OUTPUT_CSV)
3431 print_csv(outfile, NULL, num_displayed, displayed, widths);
3432 else
3433 print_line(outfile, NULL, num_displayed, displayed, widths);
3434
3435 attrptr = response->attrs;
3436
3437 while (attrptr)
3438 {
3439 while (attrptr && attrptr->group_tag <= IPP_TAG_OPERATION)
3440 attrptr = attrptr->next;
3441
3442 if (attrptr)
3443 {
3444 if (Output == _CUPS_OUTPUT_CSV)
3445 print_csv(outfile, attrptr, num_displayed, displayed, widths);
3446 else
3447 print_line(outfile, attrptr, num_displayed, displayed, widths);
3448
3449 while (attrptr && attrptr->group_tag > IPP_TAG_OPERATION)
3450 attrptr = attrptr->next;
3451 }
3452 }
3453 }
3454 else if (!prev_pass)
3455 {
3456 if (Output == _CUPS_OUTPUT_PLIST)
3457 {
3458 cupsFilePuts(outfile, "<key>Errors</key>\n");
3459 cupsFilePuts(outfile, "<array>\n");
3460
3461 for (error = (char *)cupsArrayFirst(errors);
3462 error;
3463 error = (char *)cupsArrayNext(errors))
3464 print_xml_string(outfile, "string", error);
3465
3466 cupsFilePuts(outfile, "</array>\n");
3467 }
3468
3469 if (Output == _CUPS_OUTPUT_TEST || (Output == _CUPS_OUTPUT_PLIST && outfile != cupsFileStdout()))
3470 {
3471 for (error = (char *)cupsArrayFirst(errors);
3472 error;
3473 error = (char *)cupsArrayNext(errors))
3474 cupsFilePrintf(cupsFileStdout(), " %s\n", error);
3475 }
3476 }
3477
3478 if (num_displayed > 0 && !Verbosity && response && (Output == _CUPS_OUTPUT_TEST || (Output == _CUPS_OUTPUT_PLIST && outfile != cupsFileStdout())))
3479 {
3480 for (attrptr = response->attrs;
3481 attrptr != NULL;
3482 attrptr = attrptr->next)
3483 {
3484 if (attrptr->name)
3485 {
3486 for (i = 0; i < num_displayed; i ++)
3487 {
3488 if (!strcmp(displayed[i], attrptr->name))
3489 {
3490 print_attr(outfile, Output, attrptr, NULL);
3491 break;
3492 }
3493 }
3494 }
3495 }
3496 }
3497
3498 skip_error:
3499
3500 if (Output == _CUPS_OUTPUT_PLIST)
3501 cupsFilePuts(outfile, "</dict>\n");
3502
3503 cupsFileFlush(cupsFileStdout());
3504
3505 ippDelete(response);
3506 response = NULL;
3507
3508 for (i = 0; i < num_statuses; i ++)
3509 {
3510 if (statuses[i].if_defined)
3511 free(statuses[i].if_defined);
3512 if (statuses[i].if_not_defined)
3513 free(statuses[i].if_not_defined);
3514 if (statuses[i].define_match)
3515 free(statuses[i].define_match);
3516 if (statuses[i].define_no_match)
3517 free(statuses[i].define_no_match);
3518 }
3519 num_statuses = 0;
3520
3521 for (i = num_expects, expect = expects; i > 0; i --, expect ++)
3522 {
3523 free(expect->name);
3524 if (expect->of_type)
3525 free(expect->of_type);
3526 if (expect->same_count_as)
3527 free(expect->same_count_as);
3528 if (expect->if_defined)
3529 free(expect->if_defined);
3530 if (expect->if_not_defined)
3531 free(expect->if_not_defined);
3532 if (expect->with_value)
3533 free(expect->with_value);
3534 if (expect->define_match)
3535 free(expect->define_match);
3536 if (expect->define_no_match)
3537 free(expect->define_no_match);
3538 if (expect->define_value)
3539 free(expect->define_value);
3540 }
3541 num_expects = 0;
3542
3543 for (i = 0; i < num_displayed; i ++)
3544 free(displayed[i]);
3545 num_displayed = 0;
3546
3547 if (!ignore_errors && !prev_pass)
3548 break;
3549 }
3550
3551 test_exit:
3552
3553 cupsArrayDelete(errors);
3554
3555 if (fp)
3556 cupsFileClose(fp);
3557
3558 httpClose(http);
3559 ippDelete(request);
3560 ippDelete(response);
3561
3562 for (i = 0; i < num_statuses; i ++)
3563 {
3564 if (statuses[i].if_defined)
3565 free(statuses[i].if_defined);
3566 if (statuses[i].if_not_defined)
3567 free(statuses[i].if_not_defined);
3568 if (statuses[i].define_match)
3569 free(statuses[i].define_match);
3570 if (statuses[i].define_no_match)
3571 free(statuses[i].define_no_match);
3572 }
3573
3574 for (i = num_expects, expect = expects; i > 0; i --, expect ++)
3575 {
3576 free(expect->name);
3577 if (expect->of_type)
3578 free(expect->of_type);
3579 if (expect->same_count_as)
3580 free(expect->same_count_as);
3581 if (expect->if_defined)
3582 free(expect->if_defined);
3583 if (expect->if_not_defined)
3584 free(expect->if_not_defined);
3585 if (expect->with_value)
3586 free(expect->with_value);
3587 if (expect->define_match)
3588 free(expect->define_match);
3589 if (expect->define_no_match)
3590 free(expect->define_no_match);
3591 if (expect->define_value)
3592 free(expect->define_value);
3593 }
3594
3595 for (i = 0; i < num_displayed; i ++)
3596 free(displayed[i]);
3597
3598 return (pass);
3599 }
3600
3601
3602 /*
3603 * 'expand_variables()' - Expand variables in a string.
3604 */
3605
3606 static void
3607 expand_variables(_cups_vars_t *vars, /* I - Variables */
3608 char *dst, /* I - Destination string buffer */
3609 const char *src, /* I - Source string */
3610 size_t dstsize) /* I - Size of destination buffer */
3611 {
3612 char *dstptr, /* Pointer into destination */
3613 *dstend, /* End of destination */
3614 temp[256], /* Temporary string */
3615 *tempptr; /* Pointer into temporary string */
3616 const char *value; /* Value to substitute */
3617
3618
3619 dstptr = dst;
3620 dstend = dst + dstsize - 1;
3621
3622 while (*src && dstptr < dstend)
3623 {
3624 if (*src == '$')
3625 {
3626 /*
3627 * Substitute a string/number...
3628 */
3629
3630 if (!strncmp(src, "$$", 2))
3631 {
3632 value = "$";
3633 src += 2;
3634 }
3635 else if (!strncmp(src, "$ENV[", 5))
3636 {
3637 strlcpy(temp, src + 5, sizeof(temp));
3638
3639 for (tempptr = temp; *tempptr; tempptr ++)
3640 if (*tempptr == ']')
3641 break;
3642
3643 if (*tempptr)
3644 *tempptr++ = '\0';
3645
3646 value = getenv(temp);
3647 src += tempptr - temp + 5;
3648 }
3649 else
3650 {
3651 if (src[1] == '{')
3652 {
3653 src += 2;
3654 strlcpy(temp, src, sizeof(temp));
3655 if ((tempptr = strchr(temp, '}')) != NULL)
3656 *tempptr = '\0';
3657 else
3658 tempptr = temp + strlen(temp);
3659 }
3660 else
3661 {
3662 strlcpy(temp, src + 1, sizeof(temp));
3663
3664 for (tempptr = temp; *tempptr; tempptr ++)
3665 if (!isalnum(*tempptr & 255) && *tempptr != '-' && *tempptr != '_')
3666 break;
3667
3668 if (*tempptr)
3669 *tempptr = '\0';
3670 }
3671
3672 if (!strcmp(temp, "uri"))
3673 value = vars->uri;
3674 else if (!strcmp(temp, "filename"))
3675 value = vars->filename;
3676 else if (!strcmp(temp, "scheme") || !strcmp(temp, "method"))
3677 value = vars->scheme;
3678 else if (!strcmp(temp, "username"))
3679 value = vars->userpass;
3680 else if (!strcmp(temp, "hostname"))
3681 value = vars->hostname;
3682 else if (!strcmp(temp, "port"))
3683 {
3684 snprintf(temp, sizeof(temp), "%d", vars->port);
3685 value = temp;
3686 }
3687 else if (!strcmp(temp, "resource"))
3688 value = vars->resource;
3689 else if (!strcmp(temp, "user"))
3690 value = cupsUser();
3691 else
3692 value = get_variable(vars, temp);
3693
3694 src += tempptr - temp + 1;
3695 }
3696
3697 if (value)
3698 {
3699 strlcpy(dstptr, value, (size_t)(dstend - dstptr + 1));
3700 dstptr += strlen(dstptr);
3701 }
3702 }
3703 else
3704 *dstptr++ = *src++;
3705 }
3706
3707 *dstptr = '\0';
3708 }
3709
3710
3711 /*
3712 * 'expect_matches()' - Return true if the tag matches the specification.
3713 */
3714
3715 static int /* O - 1 if matches, 0 otherwise */
3716 expect_matches(
3717 _cups_expect_t *expect, /* I - Expected attribute */
3718 ipp_tag_t value_tag) /* I - Value tag for attribute */
3719 {
3720 int match; /* Match? */
3721 char *of_type, /* Type name to match */
3722 *next, /* Next name to match */
3723 sep; /* Separator character */
3724
3725
3726 /*
3727 * If we don't expect a particular type, return immediately...
3728 */
3729
3730 if (!expect->of_type)
3731 return (1);
3732
3733 /*
3734 * Parse the "of_type" value since the string can contain multiple attribute
3735 * types separated by "," or "|"...
3736 */
3737
3738 for (of_type = expect->of_type, match = 0; !match && *of_type; of_type = next)
3739 {
3740 /*
3741 * Find the next separator, and set it (temporarily) to nul if present.
3742 */
3743
3744 for (next = of_type; *next && *next != '|' && *next != ','; next ++);
3745
3746 if ((sep = *next) != '\0')
3747 *next = '\0';
3748
3749 /*
3750 * Support some meta-types to make it easier to write the test file.
3751 */
3752
3753 if (!strcmp(of_type, "text"))
3754 match = value_tag == IPP_TAG_TEXTLANG || value_tag == IPP_TAG_TEXT;
3755 else if (!strcmp(of_type, "name"))
3756 match = value_tag == IPP_TAG_NAMELANG || value_tag == IPP_TAG_NAME;
3757 else if (!strcmp(of_type, "collection"))
3758 match = value_tag == IPP_TAG_BEGIN_COLLECTION;
3759 else
3760 match = value_tag == ippTagValue(of_type);
3761
3762 /*
3763 * Restore the separator if we have one...
3764 */
3765
3766 if (sep)
3767 *next++ = sep;
3768 }
3769
3770 return (match);
3771 }
3772
3773
3774 /*
3775 * 'get_collection()' - Get a collection value from the current test file.
3776 */
3777
3778 static ipp_t * /* O - Collection value */
3779 get_collection(cups_file_t *outfile, /* I - Output file */
3780 _cups_vars_t *vars, /* I - Variables */
3781 cups_file_t *fp, /* I - File to read from */
3782 int *linenum) /* IO - Line number */
3783 {
3784 char token[1024], /* Token from file */
3785 temp[1024], /* Temporary string */
3786 attr[128]; /* Attribute name */
3787 ipp_tag_t value; /* Current value type */
3788 ipp_t *col = ippNew(); /* Collection value */
3789 ipp_attribute_t *lastcol = NULL; /* Last collection attribute */
3790
3791
3792 while (get_token(fp, token, sizeof(token), linenum) != NULL)
3793 {
3794 if (!strcmp(token, "}"))
3795 break;
3796 else if (!strcmp(token, "{") && lastcol)
3797 {
3798 /*
3799 * Another collection value
3800 */
3801
3802 ipp_t *subcol = get_collection(outfile, vars, fp, linenum);
3803 /* Collection value */
3804
3805 if (subcol)
3806 ippSetCollection(col, &lastcol, ippGetCount(lastcol), subcol);
3807 else
3808 goto col_error;
3809 }
3810 else if (!_cups_strcasecmp(token, "MEMBER"))
3811 {
3812 /*
3813 * Attribute...
3814 */
3815
3816 lastcol = NULL;
3817
3818 if (!get_token(fp, token, sizeof(token), linenum))
3819 {
3820 print_fatal_error(outfile, "Missing MEMBER value tag on line %d.", *linenum);
3821 goto col_error;
3822 }
3823
3824 if ((value = ippTagValue(token)) < IPP_TAG_UNSUPPORTED_VALUE)
3825 {
3826 print_fatal_error(outfile, "Bad MEMBER value tag \"%s\" on line %d.", token,
3827 *linenum);
3828 goto col_error;
3829 }
3830
3831 if (!get_token(fp, attr, sizeof(attr), linenum))
3832 {
3833 print_fatal_error(outfile, "Missing MEMBER name on line %d.", *linenum);
3834 goto col_error;
3835 }
3836
3837 if (value < IPP_TAG_INTEGER)
3838 {
3839 /*
3840 * Out-of-band member attributes have no value...
3841 */
3842
3843 token[0] = '\0';
3844 }
3845 else if (!get_token(fp, temp, sizeof(temp), linenum))
3846 {
3847 print_fatal_error(outfile, "Missing MEMBER value on line %d.", *linenum);
3848 goto col_error;
3849 }
3850 else
3851 {
3852 expand_variables(vars, token, temp, sizeof(token));
3853 }
3854
3855 switch (value)
3856 {
3857 default :
3858 if (value < IPP_TAG_INTEGER)
3859 {
3860 /*
3861 * Add out-of-band value...
3862 */
3863
3864 ippAddOutOfBand(col, IPP_TAG_ZERO, value, attr);
3865 }
3866 else
3867 {
3868 print_fatal_error(outfile, "Unsupported MEMBER value tag %s for \"%s\" on line %d.", ippTagString(value), attr, *linenum);
3869 goto col_error;
3870 }
3871 break;
3872
3873 case IPP_TAG_BOOLEAN :
3874 if (!_cups_strcasecmp(token, "true"))
3875 ippAddBoolean(col, IPP_TAG_ZERO, attr, 1);
3876 else
3877 ippAddBoolean(col, IPP_TAG_ZERO, attr, (char)atoi(token));
3878 break;
3879
3880 case IPP_TAG_INTEGER :
3881 case IPP_TAG_ENUM :
3882 ippAddInteger(col, IPP_TAG_ZERO, value, attr, atoi(token));
3883 break;
3884
3885 case IPP_TAG_RESOLUTION :
3886 {
3887 int xres, /* X resolution */
3888 yres; /* Y resolution */
3889 char units[6]; /* Units */
3890
3891 if (sscanf(token, "%dx%d%5s", &xres, &yres, units) != 3 ||
3892 (_cups_strcasecmp(units, "dpi") &&
3893 _cups_strcasecmp(units, "dpc") &&
3894 _cups_strcasecmp(units, "dpcm") &&
3895 _cups_strcasecmp(units, "other")))
3896 {
3897 print_fatal_error(outfile, "Bad resolution value \"%s\" on line %d.",
3898 token, *linenum);
3899 goto col_error;
3900 }
3901
3902 if (!_cups_strcasecmp(units, "dpi"))
3903 ippAddResolution(col, IPP_TAG_ZERO, attr, IPP_RES_PER_INCH, xres, yres);
3904 else if (!_cups_strcasecmp(units, "dpc") ||
3905 !_cups_strcasecmp(units, "dpcm"))
3906 ippAddResolution(col, IPP_TAG_ZERO, attr, IPP_RES_PER_CM, xres, yres);
3907 else
3908 ippAddResolution(col, IPP_TAG_ZERO, attr, (ipp_res_t)0, xres, yres);
3909 }
3910 break;
3911
3912 case IPP_TAG_RANGE :
3913 {
3914 int lowers[4], /* Lower value */
3915 uppers[4], /* Upper values */
3916 num_vals; /* Number of values */
3917
3918
3919 num_vals = sscanf(token, "%d-%d,%d-%d,%d-%d,%d-%d",
3920 lowers + 0, uppers + 0,
3921 lowers + 1, uppers + 1,
3922 lowers + 2, uppers + 2,
3923 lowers + 3, uppers + 3);
3924
3925 if ((num_vals & 1) || num_vals == 0)
3926 {
3927 print_fatal_error(outfile, "Bad rangeOfInteger value \"%s\" on line %d.",
3928 token, *linenum);
3929 goto col_error;
3930 }
3931
3932 ippAddRanges(col, IPP_TAG_ZERO, attr, num_vals / 2, lowers,
3933 uppers);
3934 }
3935 break;
3936
3937 case IPP_TAG_BEGIN_COLLECTION :
3938 if (!strcmp(token, "{"))
3939 {
3940 ipp_t *subcol = get_collection(outfile, vars, fp, linenum);
3941 /* Collection value */
3942
3943 if (subcol)
3944 {
3945 lastcol = ippAddCollection(col, IPP_TAG_ZERO, attr, subcol);
3946 ippDelete(subcol);
3947 }
3948 else
3949 goto col_error;
3950 }
3951 else
3952 {
3953 print_fatal_error(outfile, "Bad collection value on line %d.", *linenum);
3954 goto col_error;
3955 }
3956 break;
3957
3958 case IPP_TAG_STRING :
3959 ippAddOctetString(col, IPP_TAG_ZERO, attr, token, (int)strlen(token));
3960 break;
3961
3962 case IPP_TAG_TEXTLANG :
3963 case IPP_TAG_NAMELANG :
3964 case IPP_TAG_TEXT :
3965 case IPP_TAG_NAME :
3966 case IPP_TAG_KEYWORD :
3967 case IPP_TAG_URI :
3968 case IPP_TAG_URISCHEME :
3969 case IPP_TAG_CHARSET :
3970 case IPP_TAG_LANGUAGE :
3971 case IPP_TAG_MIMETYPE :
3972 if (!strchr(token, ','))
3973 ippAddString(col, IPP_TAG_ZERO, value, attr, NULL, token);
3974 else
3975 {
3976 /*
3977 * Multiple string values...
3978 */
3979
3980 int num_values; /* Number of values */
3981 char *values[100], /* Values */
3982 *ptr; /* Pointer to next value */
3983
3984
3985 values[0] = token;
3986 num_values = 1;
3987
3988 for (ptr = strchr(token, ','); ptr; ptr = strchr(ptr, ','))
3989 {
3990 if (ptr > token && ptr[-1] == '\\')
3991 _cups_strcpy(ptr - 1, ptr);
3992 else
3993 {
3994 *ptr++ = '\0';
3995 values[num_values] = ptr;
3996 num_values ++;
3997 }
3998 }
3999
4000 ippAddStrings(col, IPP_TAG_ZERO, value, attr, num_values,
4001 NULL, (const char **)values);
4002 }
4003 break;
4004 }
4005 }
4006 else
4007 {
4008 print_fatal_error(outfile, "Unexpected token %s seen on line %d.", token, *linenum);
4009 goto col_error;
4010 }
4011 }
4012
4013 return (col);
4014
4015 /*
4016 * If we get here there was a parse error; free memory and return.
4017 */
4018
4019 col_error:
4020
4021 ippDelete(col);
4022
4023 return (NULL);
4024 }
4025
4026
4027 /*
4028 * 'get_filename()' - Get a filename based on the current test file.
4029 */
4030
4031 static char * /* O - Filename */
4032 get_filename(const char *testfile, /* I - Current test file */
4033 char *dst, /* I - Destination filename */
4034 const char *src, /* I - Source filename */
4035 size_t dstsize) /* I - Size of destination buffer */
4036 {
4037 char *dstptr; /* Pointer into destination */
4038 _cups_globals_t *cg = _cupsGlobals();
4039 /* Global data */
4040
4041
4042 if (*src == '<' && src[strlen(src) - 1] == '>')
4043 {
4044 /*
4045 * Map <filename> to CUPS_DATADIR/ipptool/filename...
4046 */
4047
4048 snprintf(dst, dstsize, "%s/ipptool/%s", cg->cups_datadir, src + 1);
4049 dstptr = dst + strlen(dst) - 1;
4050 if (*dstptr == '>')
4051 *dstptr = '\0';
4052 }
4053 else if (*src == '/' || !strchr(testfile, '/')
4054 #ifdef WIN32
4055 || (isalpha(*src & 255) && src[1] == ':')
4056 #endif /* WIN32 */
4057 )
4058 {
4059 /*
4060 * Use the path as-is...
4061 */
4062
4063 strlcpy(dst, src, dstsize);
4064 }
4065 else
4066 {
4067 /*
4068 * Make path relative to testfile...
4069 */
4070
4071 strlcpy(dst, testfile, dstsize);
4072 if ((dstptr = strrchr(dst, '/')) != NULL)
4073 dstptr ++;
4074 else
4075 dstptr = dst; /* Should never happen */
4076
4077 strlcpy(dstptr, src, dstsize - (size_t)(dstptr - dst));
4078 }
4079
4080 return (dst);
4081 }
4082
4083
4084 /*
4085 * 'get_string()' - Get a pointer to a string value or the portion of interest.
4086 */
4087
4088 static const char * /* O - Pointer to string */
4089 get_string(ipp_attribute_t *attr, /* I - IPP attribute */
4090 int element, /* I - Element to fetch */
4091 int flags, /* I - Value ("with") flags */
4092 char *buffer, /* I - Temporary buffer */
4093 size_t bufsize) /* I - Size of temporary buffer */
4094 {
4095 const char *value; /* Value */
4096 char *ptr, /* Pointer into value */
4097 scheme[256], /* URI scheme */
4098 userpass[256], /* Username/password */
4099 hostname[256], /* Hostname */
4100 resource[1024]; /* Resource */
4101 int port; /* Port number */
4102
4103
4104 value = ippGetString(attr, element, NULL);
4105
4106 if (flags & _CUPS_WITH_HOSTNAME)
4107 {
4108 if (httpSeparateURI(HTTP_URI_CODING_ALL, value, scheme, sizeof(scheme), userpass, sizeof(userpass), buffer, (int)bufsize, &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
4109 buffer[0] = '\0';
4110
4111 ptr = buffer + strlen(buffer) - 1;
4112 if (ptr >= buffer && *ptr == '.')
4113 *ptr = '\0'; /* Drop trailing "." */
4114
4115 return (buffer);
4116 }
4117 else if (flags & _CUPS_WITH_RESOURCE)
4118 {
4119 if (httpSeparateURI(HTTP_URI_CODING_ALL, value, scheme, sizeof(scheme), userpass, sizeof(userpass), hostname, sizeof(hostname), &port, buffer, (int)bufsize) < HTTP_URI_STATUS_OK)
4120 buffer[0] = '\0';
4121
4122 return (buffer);
4123 }
4124 else if (flags & _CUPS_WITH_SCHEME)
4125 {
4126 if (httpSeparateURI(HTTP_URI_CODING_ALL, value, buffer, (int)bufsize, userpass, sizeof(userpass), hostname, sizeof(hostname), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
4127 buffer[0] = '\0';
4128
4129 return (buffer);
4130 }
4131 else if (ippGetValueTag(attr) == IPP_TAG_URI && (!strncmp(value, "ipp://", 6) || !strncmp(value, "http://", 7) || !strncmp(value, "ipps://", 7) || !strncmp(value, "https://", 8)))
4132 {
4133 http_uri_status_t status = httpSeparateURI(HTTP_URI_CODING_ALL, value, scheme, sizeof(scheme), userpass, sizeof(userpass), hostname, sizeof(hostname), &port, resource, sizeof(resource));
4134
4135 if (status < HTTP_URI_STATUS_OK)
4136 {
4137 /*
4138 * Bad URI...
4139 */
4140
4141 buffer[0] = '\0';
4142 }
4143 else
4144 {
4145 /*
4146 * Normalize URI with no trailing dot...
4147 */
4148
4149 if ((ptr = hostname + strlen(hostname) - 1) >= hostname && *ptr == '.')
4150 *ptr = '\0';
4151
4152 httpAssembleURI(HTTP_URI_CODING_ALL, buffer, (int)bufsize, scheme, userpass, hostname, port, resource);
4153 }
4154
4155 return (buffer);
4156 }
4157 else
4158 return (value);
4159 }
4160
4161
4162 /*
4163 * 'get_token()' - Get a token from a file.
4164 */
4165
4166 static char * /* O - Token from file or NULL on EOF */
4167 get_token(cups_file_t *fp, /* I - File to read from */
4168 char *buf, /* I - Buffer to read into */
4169 int buflen, /* I - Length of buffer */
4170 int *linenum) /* IO - Current line number */
4171 {
4172 int ch, /* Character from file */
4173 quote; /* Quoting character */
4174 char *bufptr, /* Pointer into buffer */
4175 *bufend; /* End of buffer */
4176
4177
4178 for (;;)
4179 {
4180 /*
4181 * Skip whitespace...
4182 */
4183
4184 while (isspace(ch = cupsFileGetChar(fp)))
4185 {
4186 if (ch == '\n')
4187 (*linenum) ++;
4188 }
4189
4190 /*
4191 * Read a token...
4192 */
4193
4194 if (ch == EOF)
4195 return (NULL);
4196 else if (ch == '\'' || ch == '\"')
4197 {
4198 /*
4199 * Quoted text or regular expression...
4200 */
4201
4202 quote = ch;
4203 bufptr = buf;
4204 bufend = buf + buflen - 1;
4205
4206 while ((ch = cupsFileGetChar(fp)) != EOF)
4207 {
4208 if (ch == '\\')
4209 {
4210 /*
4211 * Escape next character...
4212 */
4213
4214 if (bufptr < bufend)
4215 *bufptr++ = (char)ch;
4216
4217 if ((ch = cupsFileGetChar(fp)) != EOF && bufptr < bufend)
4218 *bufptr++ = (char)ch;
4219 }
4220 else if (ch == quote)
4221 break;
4222 else if (bufptr < bufend)
4223 *bufptr++ = (char)ch;
4224 }
4225
4226 *bufptr = '\0';
4227
4228 return (buf);
4229 }
4230 else if (ch == '#')
4231 {
4232 /*
4233 * Comment...
4234 */
4235
4236 while ((ch = cupsFileGetChar(fp)) != EOF)
4237 if (ch == '\n')
4238 break;
4239
4240 (*linenum) ++;
4241 }
4242 else if (ch == '{' || ch == '}' || ch == ',')
4243 {
4244 buf[0] = (char)ch;
4245 buf[1] = '\0';
4246
4247 return (buf);
4248 }
4249 else
4250 {
4251 /*
4252 * Whitespace delimited text...
4253 */
4254
4255 cupsFileSeek(fp, cupsFileTell(fp) - 1);
4256
4257 bufptr = buf;
4258 bufend = buf + buflen - 1;
4259
4260 while ((ch = cupsFileGetChar(fp)) != EOF)
4261 if (isspace(ch) || ch == '#')
4262 break;
4263 else if (bufptr < bufend)
4264 *bufptr++ = (char)ch;
4265
4266 if (ch == '#')
4267 cupsFileSeek(fp, cupsFileTell(fp) - 1);
4268 else if (ch == '\n')
4269 (*linenum) ++;
4270
4271 *bufptr = '\0';
4272
4273 return (buf);
4274 }
4275 }
4276 }
4277
4278
4279 /*
4280 * 'get_variable()' - Get the value of a variable.
4281 */
4282
4283 static char * /* O - Value or NULL */
4284 get_variable(_cups_vars_t *vars, /* I - Variables */
4285 const char *name) /* I - Variable name */
4286 {
4287 _cups_var_t key, /* Search key */
4288 *match; /* Matching variable, if any */
4289
4290
4291 key.name = (char *)name;
4292 match = cupsArrayFind(vars->vars, &key);
4293
4294 return (match ? match->value : NULL);
4295 }
4296
4297
4298 /*
4299 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
4300 * value.
4301 */
4302
4303 static char * /* O - ISO 8601 date/time string */
4304 iso_date(ipp_uchar_t *date) /* I - IPP (RFC 1903) date/time value */
4305 {
4306 time_t utctime; /* UTC time since 1970 */
4307 struct tm *utcdate; /* UTC date/time */
4308 static char buffer[255]; /* String buffer */
4309
4310
4311 utctime = ippDateToTime(date);
4312 utcdate = gmtime(&utctime);
4313
4314 snprintf(buffer, sizeof(buffer), "%04d-%02d-%02dT%02d:%02d:%02dZ",
4315 utcdate->tm_year + 1900, utcdate->tm_mon + 1, utcdate->tm_mday,
4316 utcdate->tm_hour, utcdate->tm_min, utcdate->tm_sec);
4317
4318 return (buffer);
4319 }
4320
4321
4322 /*
4323 * 'password_cb()' - Password callback for authenticated tests.
4324 */
4325
4326 static const char * /* O - Password */
4327 password_cb(const char *prompt) /* I - Prompt (unused) */
4328 {
4329 (void)prompt;
4330
4331 if (PasswordTries < 3)
4332 {
4333 PasswordTries ++;
4334
4335 cupsSetUser(Username);
4336
4337 return (Password);
4338 }
4339 else
4340 return (NULL);
4341 }
4342
4343
4344 /*
4345 * 'pause_message()' - Display the message and pause until the user presses a key.
4346 */
4347
4348 static void
4349 pause_message(const char *message) /* I - Message */
4350 {
4351 #ifdef WIN32
4352 HANDLE tty; /* Console handle */
4353 DWORD mode; /* Console mode */
4354 char key; /* Key press */
4355 DWORD bytes; /* Bytes read for key press */
4356
4357
4358 /*
4359 * Disable input echo and set raw input...
4360 */
4361
4362 if ((tty = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE)
4363 return;
4364
4365 if (!GetConsoleMode(tty, &mode))
4366 return;
4367
4368 if (!SetConsoleMode(tty, 0))
4369 return;
4370
4371 #else
4372 int tty; /* /dev/tty - never read from stdin */
4373 struct termios original, /* Original input mode */
4374 noecho; /* No echo input mode */
4375 char key; /* Current key press */
4376
4377
4378 /*
4379 * Disable input echo and set raw input...
4380 */
4381
4382 if ((tty = open("/dev/tty", O_RDONLY)) < 0)
4383 return;
4384
4385 if (tcgetattr(tty, &original))
4386 {
4387 close(tty);
4388 return;
4389 }
4390
4391 noecho = original;
4392 noecho.c_lflag &= (tcflag_t)~(ICANON | ECHO | ECHOE | ISIG);
4393
4394 if (tcsetattr(tty, TCSAFLUSH, &noecho))
4395 {
4396 close(tty);
4397 return;
4398 }
4399 #endif /* WIN32 */
4400
4401 /*
4402 * Display the prompt...
4403 */
4404
4405 printf("%s\n---- PRESS ANY KEY ----", message);
4406 cupsFileFlush(cupsFileStdout());
4407
4408 #ifdef WIN32
4409 /*
4410 * Read a key...
4411 */
4412
4413 ReadFile(tty, &key, 1, &bytes, NULL);
4414
4415 /*
4416 * Cleanup...
4417 */
4418
4419 SetConsoleMode(tty, mode);
4420
4421 #else
4422 /*
4423 * Read a key...
4424 */
4425
4426 read(tty, &key, 1);
4427
4428 /*
4429 * Cleanup...
4430 */
4431
4432 tcsetattr(tty, TCSAFLUSH, &original);
4433 close(tty);
4434 #endif /* WIN32 */
4435
4436 /*
4437 * Erase the "press any key" prompt...
4438 */
4439
4440 cupsFilePuts(cupsFileStdout(), "\r \r");
4441 cupsFileFlush(cupsFileStdout());
4442 }
4443
4444
4445 /*
4446 * 'print_attr()' - Print an attribute on the screen.
4447 */
4448
4449 static void
4450 print_attr(cups_file_t *outfile, /* I - Output file */
4451 int format, /* I - Output format */
4452 ipp_attribute_t *attr, /* I - Attribute to print */
4453 ipp_tag_t *group) /* IO - Current group */
4454 {
4455 int i; /* Looping var */
4456 ipp_attribute_t *colattr; /* Collection attribute */
4457
4458
4459 if (format == _CUPS_OUTPUT_PLIST)
4460 {
4461 if (!attr->name || (group && *group != attr->group_tag))
4462 {
4463 if (attr->group_tag != IPP_TAG_ZERO)
4464 {
4465 cupsFilePuts(outfile, "</dict>\n");
4466 cupsFilePuts(outfile, "<dict>\n");
4467 }
4468
4469 if (group)
4470 *group = attr->group_tag;
4471 }
4472
4473 if (!attr->name)
4474 return;
4475
4476 print_xml_string(outfile, "key", attr->name);
4477 if (attr->num_values > 1)
4478 cupsFilePuts(outfile, "<array>\n");
4479
4480 switch (attr->value_tag)
4481 {
4482 case IPP_TAG_INTEGER :
4483 case IPP_TAG_ENUM :
4484 for (i = 0; i < attr->num_values; i ++)
4485 cupsFilePrintf(outfile, "<integer>%d</integer>\n", attr->values[i].integer);
4486 break;
4487
4488 case IPP_TAG_BOOLEAN :
4489 for (i = 0; i < attr->num_values; i ++)
4490 cupsFilePuts(outfile, attr->values[i].boolean ? "<true />\n" : "<false />\n");
4491 break;
4492
4493 case IPP_TAG_RANGE :
4494 for (i = 0; i < attr->num_values; i ++)
4495 cupsFilePrintf(outfile, "<dict><key>lower</key><integer>%d</integer>"
4496 "<key>upper</key><integer>%d</integer></dict>\n",
4497 attr->values[i].range.lower, attr->values[i].range.upper);
4498 break;
4499
4500 case IPP_TAG_RESOLUTION :
4501 for (i = 0; i < attr->num_values; i ++)
4502 cupsFilePrintf(outfile, "<dict><key>xres</key><integer>%d</integer>"
4503 "<key>yres</key><integer>%d</integer>"
4504 "<key>units</key><string>%s</string></dict>\n",
4505 attr->values[i].resolution.xres,
4506 attr->values[i].resolution.yres,
4507 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
4508 "dpi" : "dpcm");
4509 break;
4510
4511 case IPP_TAG_DATE :
4512 for (i = 0; i < attr->num_values; i ++)
4513 cupsFilePrintf(outfile, "<date>%s</date>\n", iso_date(attr->values[i].date));
4514 break;
4515
4516 case IPP_TAG_STRING :
4517 for (i = 0; i < attr->num_values; i ++)
4518 {
4519 char buffer[IPP_MAX_LENGTH * 5 / 4 + 1];
4520 /* Output buffer */
4521
4522 cupsFilePrintf(outfile, "<data>%s</data>\n",
4523 httpEncode64_2(buffer, sizeof(buffer),
4524 attr->values[i].unknown.data,
4525 attr->values[i].unknown.length));
4526 }
4527 break;
4528
4529 case IPP_TAG_TEXT :
4530 case IPP_TAG_NAME :
4531 case IPP_TAG_KEYWORD :
4532 case IPP_TAG_URI :
4533 case IPP_TAG_URISCHEME :
4534 case IPP_TAG_CHARSET :
4535 case IPP_TAG_LANGUAGE :
4536 case IPP_TAG_MIMETYPE :
4537 for (i = 0; i < attr->num_values; i ++)
4538 print_xml_string(outfile, "string", attr->values[i].string.text);
4539 break;
4540
4541 case IPP_TAG_TEXTLANG :
4542 case IPP_TAG_NAMELANG :
4543 for (i = 0; i < attr->num_values; i ++)
4544 {
4545 cupsFilePuts(outfile, "<dict><key>language</key><string>");
4546 print_xml_string(outfile, NULL, attr->values[i].string.language);
4547 cupsFilePuts(outfile, "</string><key>string</key><string>");
4548 print_xml_string(outfile, NULL, attr->values[i].string.text);
4549 cupsFilePuts(outfile, "</string></dict>\n");
4550 }
4551 break;
4552
4553 case IPP_TAG_BEGIN_COLLECTION :
4554 for (i = 0; i < attr->num_values; i ++)
4555 {
4556 cupsFilePuts(outfile, "<dict>\n");
4557 for (colattr = attr->values[i].collection->attrs;
4558 colattr;
4559 colattr = colattr->next)
4560 print_attr(outfile, format, colattr, NULL);
4561 cupsFilePuts(outfile, "</dict>\n");
4562 }
4563 break;
4564
4565 default :
4566 cupsFilePrintf(outfile, "<string>&lt;&lt;%s&gt;&gt;</string>\n", ippTagString(attr->value_tag));
4567 break;
4568 }
4569
4570 if (attr->num_values > 1)
4571 cupsFilePuts(outfile, "</array>\n");
4572 }
4573 else
4574 {
4575 char buffer[131072]; /* Value buffer */
4576
4577 if (format == _CUPS_OUTPUT_TEST)
4578 {
4579 if (!attr->name)
4580 {
4581 cupsFilePuts(outfile, " -- separator --\n");
4582 return;
4583 }
4584
4585 cupsFilePrintf(outfile, " %s (%s%s) = ", attr->name, attr->num_values > 1 ? "1setOf " : "", ippTagString(attr->value_tag));
4586 }
4587
4588 ippAttributeString(attr, buffer, sizeof(buffer));
4589 cupsFilePrintf(outfile, "%s\n", buffer);
4590 }
4591 }
4592
4593
4594 /*
4595 * 'print_csv()' - Print a line of CSV text.
4596 */
4597
4598 static void
4599 print_csv(
4600 cups_file_t *outfile, /* I - Output file */
4601 ipp_attribute_t *attr, /* I - First attribute for line */
4602 int num_displayed, /* I - Number of attributes to display */
4603 char **displayed, /* I - Attributes to display */
4604 size_t *widths) /* I - Column widths */
4605 {
4606 int i; /* Looping var */
4607 size_t maxlength; /* Max length of all columns */
4608 char *buffer, /* String buffer */
4609 *bufptr; /* Pointer into buffer */
4610 ipp_attribute_t *current; /* Current attribute */
4611
4612
4613 /*
4614 * Get the maximum string length we have to show and allocate...
4615 */
4616
4617 for (i = 1, maxlength = widths[0]; i < num_displayed; i ++)
4618 if (widths[i] > maxlength)
4619 maxlength = widths[i];
4620
4621 maxlength += 2;
4622
4623 if ((buffer = malloc(maxlength)) == NULL)
4624 return;
4625
4626 /*
4627 * Loop through the attributes to display...
4628 */
4629
4630 if (attr)
4631 {
4632 for (i = 0; i < num_displayed; i ++)
4633 {
4634 if (i)
4635 cupsFilePutChar(outfile, ',');
4636
4637 buffer[0] = '\0';
4638
4639 for (current = attr; current; current = current->next)
4640 {
4641 if (!current->name)
4642 break;
4643 else if (!strcmp(current->name, displayed[i]))
4644 {
4645 ippAttributeString(current, buffer, maxlength);
4646 break;
4647 }
4648 }
4649
4650 if (strchr(buffer, ',') != NULL || strchr(buffer, '\"') != NULL ||
4651 strchr(buffer, '\\') != NULL)
4652 {
4653 cupsFilePutChar(cupsFileStdout(), '\"');
4654 for (bufptr = buffer; *bufptr; bufptr ++)
4655 {
4656 if (*bufptr == '\\' || *bufptr == '\"')
4657 cupsFilePutChar(cupsFileStdout(), '\\');
4658 cupsFilePutChar(cupsFileStdout(), *bufptr);
4659 }
4660 cupsFilePutChar(cupsFileStdout(), '\"');
4661 }
4662 else
4663 cupsFilePuts(outfile, buffer);
4664 }
4665 cupsFilePutChar(cupsFileStdout(), '\n');
4666 }
4667 else
4668 {
4669 for (i = 0; i < num_displayed; i ++)
4670 {
4671 if (i)
4672 cupsFilePutChar(cupsFileStdout(), ',');
4673
4674 cupsFilePuts(outfile, displayed[i]);
4675 }
4676 cupsFilePutChar(cupsFileStdout(), '\n');
4677 }
4678
4679 free(buffer);
4680 }
4681
4682
4683 /*
4684 * 'print_fatal_error()' - Print a fatal error message.
4685 */
4686
4687 static void
4688 print_fatal_error(cups_file_t *outfile, /* I - Output file */
4689 const char *s, /* I - Printf-style format string */
4690 ...) /* I - Additional arguments as needed */
4691 {
4692 char buffer[10240]; /* Format buffer */
4693 va_list ap; /* Pointer to arguments */
4694
4695
4696 /*
4697 * Format the error message...
4698 */
4699
4700 va_start(ap, s);
4701 vsnprintf(buffer, sizeof(buffer), s, ap);
4702 va_end(ap);
4703
4704 /*
4705 * Then output it...
4706 */
4707
4708 if (Output == _CUPS_OUTPUT_PLIST)
4709 {
4710 print_xml_header(outfile);
4711 print_xml_trailer(outfile, 0, buffer);
4712 }
4713
4714 _cupsLangPrintf(stderr, "ipptool: %s", buffer);
4715 }
4716
4717
4718 /*
4719 * 'print_line()' - Print a line of formatted or CSV text.
4720 */
4721
4722 static void
4723 print_line(
4724 cups_file_t *outfile, /* I - Output file */
4725 ipp_attribute_t *attr, /* I - First attribute for line */
4726 int num_displayed, /* I - Number of attributes to display */
4727 char **displayed, /* I - Attributes to display */
4728 size_t *widths) /* I - Column widths */
4729 {
4730 int i; /* Looping var */
4731 size_t maxlength; /* Max length of all columns */
4732 char *buffer; /* String buffer */
4733 ipp_attribute_t *current; /* Current attribute */
4734
4735
4736 /*
4737 * Get the maximum string length we have to show and allocate...
4738 */
4739
4740 for (i = 1, maxlength = widths[0]; i < num_displayed; i ++)
4741 if (widths[i] > maxlength)
4742 maxlength = widths[i];
4743
4744 maxlength += 2;
4745
4746 if ((buffer = malloc(maxlength)) == NULL)
4747 return;
4748
4749 /*
4750 * Loop through the attributes to display...
4751 */
4752
4753 if (attr)
4754 {
4755 for (i = 0; i < num_displayed; i ++)
4756 {
4757 if (i)
4758 cupsFilePutChar(cupsFileStdout(), ' ');
4759
4760 buffer[0] = '\0';
4761
4762 for (current = attr; current; current = current->next)
4763 {
4764 if (!current->name)
4765 break;
4766 else if (!strcmp(current->name, displayed[i]))
4767 {
4768 ippAttributeString(current, buffer, maxlength);
4769 break;
4770 }
4771 }
4772
4773 cupsFilePrintf(outfile, "%*s", (int)-widths[i], buffer);
4774 }
4775 cupsFilePutChar(cupsFileStdout(), '\n');
4776 }
4777 else
4778 {
4779 for (i = 0; i < num_displayed; i ++)
4780 {
4781 if (i)
4782 cupsFilePutChar(cupsFileStdout(), ' ');
4783
4784 cupsFilePrintf(outfile, "%*s", (int)-widths[i], displayed[i]);
4785 }
4786 cupsFilePutChar(cupsFileStdout(), '\n');
4787
4788 for (i = 0; i < num_displayed; i ++)
4789 {
4790 if (i)
4791 cupsFilePutChar(cupsFileStdout(), ' ');
4792
4793 memset(buffer, '-', widths[i]);
4794 buffer[widths[i]] = '\0';
4795 cupsFilePuts(outfile, buffer);
4796 }
4797 cupsFilePutChar(cupsFileStdout(), '\n');
4798 }
4799
4800 free(buffer);
4801 }
4802
4803
4804 /*
4805 * 'print_xml_header()' - Print a standard XML plist header.
4806 */
4807
4808 static void
4809 print_xml_header(cups_file_t *outfile) /* I - Output file */
4810 {
4811 if (!XMLHeader)
4812 {
4813 cupsFilePuts(outfile, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
4814 cupsFilePuts(outfile, "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
4815 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n");
4816 cupsFilePuts(outfile, "<plist version=\"1.0\">\n");
4817 cupsFilePuts(outfile, "<dict>\n");
4818 cupsFilePuts(outfile, "<key>ipptoolVersion</key>\n");
4819 cupsFilePuts(outfile, "<string>" CUPS_SVERSION "</string>\n");
4820 cupsFilePuts(outfile, "<key>Transfer</key>\n");
4821 cupsFilePrintf(outfile, "<string>%s</string>\n",
4822 Transfer == _CUPS_TRANSFER_AUTO ? "auto" :
4823 Transfer == _CUPS_TRANSFER_CHUNKED ? "chunked" : "length");
4824 cupsFilePuts(outfile, "<key>Tests</key>\n");
4825 cupsFilePuts(outfile, "<array>\n");
4826
4827 XMLHeader = 1;
4828 }
4829 }
4830
4831
4832 /*
4833 * 'print_xml_string()' - Print an XML string with escaping.
4834 */
4835
4836 static void
4837 print_xml_string(cups_file_t *outfile, /* I - Output file */
4838 const char *element, /* I - Element name or NULL */
4839 const char *s) /* I - String to print */
4840 {
4841 if (element)
4842 cupsFilePrintf(outfile, "<%s>", element);
4843
4844 while (*s)
4845 {
4846 if (*s == '&')
4847 cupsFilePuts(outfile, "&amp;");
4848 else if (*s == '<')
4849 cupsFilePuts(outfile, "&lt;");
4850 else if (*s == '>')
4851 cupsFilePuts(outfile, "&gt;");
4852 else if ((*s & 0xe0) == 0xc0)
4853 {
4854 /*
4855 * Validate UTF-8 two-byte sequence...
4856 */
4857
4858 if ((s[1] & 0xc0) != 0x80)
4859 {
4860 cupsFilePutChar(cupsFileStdout(), '?');
4861 s ++;
4862 }
4863 else
4864 {
4865 cupsFilePutChar(cupsFileStdout(), *s++);
4866 cupsFilePutChar(cupsFileStdout(), *s);
4867 }
4868 }
4869 else if ((*s & 0xf0) == 0xe0)
4870 {
4871 /*
4872 * Validate UTF-8 three-byte sequence...
4873 */
4874
4875 if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80)
4876 {
4877 cupsFilePutChar(cupsFileStdout(), '?');
4878 s += 2;
4879 }
4880 else
4881 {
4882 cupsFilePutChar(cupsFileStdout(), *s++);
4883 cupsFilePutChar(cupsFileStdout(), *s++);
4884 cupsFilePutChar(cupsFileStdout(), *s);
4885 }
4886 }
4887 else if ((*s & 0xf8) == 0xf0)
4888 {
4889 /*
4890 * Validate UTF-8 four-byte sequence...
4891 */
4892
4893 if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 ||
4894 (s[3] & 0xc0) != 0x80)
4895 {
4896 cupsFilePutChar(cupsFileStdout(), '?');
4897 s += 3;
4898 }
4899 else
4900 {
4901 cupsFilePutChar(cupsFileStdout(), *s++);
4902 cupsFilePutChar(cupsFileStdout(), *s++);
4903 cupsFilePutChar(cupsFileStdout(), *s++);
4904 cupsFilePutChar(cupsFileStdout(), *s);
4905 }
4906 }
4907 else if ((*s & 0x80) || (*s < ' ' && !isspace(*s & 255)))
4908 {
4909 /*
4910 * Invalid control character...
4911 */
4912
4913 cupsFilePutChar(cupsFileStdout(), '?');
4914 }
4915 else
4916 cupsFilePutChar(cupsFileStdout(), *s);
4917
4918 s ++;
4919 }
4920
4921 if (element)
4922 cupsFilePrintf(outfile, "</%s>\n", element);
4923 }
4924
4925
4926 /*
4927 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
4928 */
4929
4930 static void
4931 print_xml_trailer(cups_file_t *outfile, /* I - Output file */
4932 int success, /* I - 1 on success, 0 on failure */
4933 const char *message) /* I - Error message or NULL */
4934 {
4935 if (XMLHeader)
4936 {
4937 cupsFilePuts(outfile, "</array>\n");
4938 cupsFilePuts(outfile, "<key>Successful</key>\n");
4939 cupsFilePuts(outfile, success ? "<true />\n" : "<false />\n");
4940 if (message)
4941 {
4942 cupsFilePuts(outfile, "<key>ErrorMessage</key>\n");
4943 print_xml_string(outfile, "string", message);
4944 }
4945 cupsFilePuts(outfile, "</dict>\n");
4946 cupsFilePuts(outfile, "</plist>\n");
4947
4948 XMLHeader = 0;
4949 }
4950 }
4951
4952
4953 /*
4954 * 'set_variable()' - Set a variable value.
4955 */
4956
4957 static void
4958 set_variable(cups_file_t *outfile, /* I - Output file */
4959 _cups_vars_t *vars, /* I - Variables */
4960 const char *name, /* I - Variable name */
4961 const char *value) /* I - Value string */
4962 {
4963 _cups_var_t key, /* Search key */
4964 *var; /* New variable */
4965
4966
4967 if (!_cups_strcasecmp(name, "filename"))
4968 {
4969 if (vars->filename)
4970 free(vars->filename);
4971
4972 vars->filename = strdup(value);
4973 return;
4974 }
4975
4976 key.name = (char *)name;
4977 if ((var = cupsArrayFind(vars->vars, &key)) != NULL)
4978 {
4979 free(var->value);
4980 var->value = strdup(value);
4981 }
4982 else if ((var = malloc(sizeof(_cups_var_t))) == NULL)
4983 {
4984 print_fatal_error(outfile, "Unable to allocate memory for variable \"%s\".", name);
4985 exit(1);
4986 }
4987 else
4988 {
4989 var->name = strdup(name);
4990 var->value = strdup(value);
4991
4992 cupsArrayAdd(vars->vars, var);
4993 }
4994 }
4995
4996
4997 #ifndef WIN32
4998 /*
4999 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
5000 */
5001
5002 static void
5003 sigterm_handler(int sig) /* I - Signal number (unused) */
5004 {
5005 (void)sig;
5006
5007 Cancel = 1;
5008
5009 signal(SIGINT, SIG_DFL);
5010 signal(SIGTERM, SIG_DFL);
5011 }
5012 #endif /* !WIN32 */
5013
5014
5015 /*
5016 * 'timeout_cb()' - Handle HTTP timeouts.
5017 */
5018
5019 static int /* O - 1 to continue, 0 to cancel */
5020 timeout_cb(http_t *http, /* I - Connection to server */
5021 void *user_data) /* I - User data (unused) */
5022 {
5023 int buffered = 0; /* Bytes buffered but not yet sent */
5024
5025
5026 (void)user_data;
5027
5028 /*
5029 * If the socket still have data waiting to be sent to the printer (as can
5030 * happen if the printer runs out of paper), continue to wait until the output
5031 * buffer is empty...
5032 */
5033
5034 #ifdef SO_NWRITE /* macOS and some versions of Linux */
5035 socklen_t len = sizeof(buffered); /* Size of return value */
5036
5037 if (getsockopt(httpGetFd(http), SOL_SOCKET, SO_NWRITE, &buffered, &len))
5038 buffered = 0;
5039
5040 #elif defined(SIOCOUTQ) /* Others except Windows */
5041 if (ioctl(httpGetFd(http), SIOCOUTQ, &buffered))
5042 buffered = 0;
5043
5044 #else /* Windows (not possible) */
5045 (void)http;
5046 #endif /* SO_NWRITE */
5047
5048 return (buffered > 0);
5049 }
5050
5051
5052 /*
5053 * 'usage()' - Show program usage.
5054 */
5055
5056 static void
5057 usage(void)
5058 {
5059 _cupsLangPuts(stderr, _("Usage: ipptool [options] URI filename [ ... filenameN ]"));
5060 _cupsLangPuts(stderr, _("Options:"));
5061 _cupsLangPuts(stderr, _(" --help Show help."));
5062 _cupsLangPuts(stderr, _(" --stop-after-include-error\n"
5063 " Stop tests after a failed INCLUDE."));
5064 _cupsLangPuts(stderr, _(" --version Show version."));
5065 _cupsLangPuts(stderr, _(" -4 Connect using IPv4."));
5066 _cupsLangPuts(stderr, _(" -6 Connect using IPv6."));
5067 _cupsLangPuts(stderr, _(" -C Send requests using "
5068 "chunking (default)."));
5069 _cupsLangPuts(stderr, _(" -E Test with encryption using HTTP Upgrade to TLS."));
5070 _cupsLangPuts(stderr, _(" -I Ignore errors."));
5071 _cupsLangPuts(stderr, _(" -L Send requests using content-length."));
5072 _cupsLangPuts(stderr, _(" -P filename.plist Produce XML plist to a file and test report to standard output."));
5073 _cupsLangPuts(stderr, _(" -S Test with encryption using HTTPS."));
5074 _cupsLangPuts(stderr, _(" -T seconds Set the receive/send timeout in seconds."));
5075 _cupsLangPuts(stderr, _(" -V version Set default IPP version."));
5076 _cupsLangPuts(stderr, _(" -X Produce XML plist instead of plain text."));
5077 _cupsLangPuts(stderr, _(" -c Produce CSV output."));
5078 _cupsLangPuts(stderr, _(" -d name=value Set named variable to value."));
5079 _cupsLangPuts(stderr, _(" -f filename Set default request filename."));
5080 _cupsLangPuts(stderr, _(" -i seconds Repeat the last file with the given time interval."));
5081 _cupsLangPuts(stderr, _(" -l Produce plain text output."));
5082 _cupsLangPuts(stderr, _(" -n count Repeat the last file the given number of times."));
5083 _cupsLangPuts(stderr, _(" -q Run silently."));
5084 _cupsLangPuts(stderr, _(" -t Produce a test report."));
5085 _cupsLangPuts(stderr, _(" -v Be verbose."));
5086
5087 exit(1);
5088 }
5089
5090
5091 /*
5092 * 'validate_attr()' - Determine whether an attribute is valid.
5093 */
5094
5095 static int /* O - 1 if valid, 0 otherwise */
5096 validate_attr(cups_file_t *outfile, /* I - Output file */
5097 cups_array_t *errors, /* I - Errors array */
5098 ipp_attribute_t *attr) /* I - Attribute to validate */
5099 {
5100 int i; /* Looping var */
5101 char scheme[64], /* Scheme from URI */
5102 userpass[256], /* Username/password from URI */
5103 hostname[256], /* Hostname from URI */
5104 resource[1024]; /* Resource from URI */
5105 int port, /* Port number from URI */
5106 uri_status, /* URI separation status */
5107 valid = 1; /* Is the attribute valid? */
5108 const char *ptr; /* Pointer into string */
5109 ipp_attribute_t *colattr; /* Collection attribute */
5110 regex_t re; /* Regular expression */
5111 ipp_uchar_t *date; /* Current date value */
5112
5113
5114 /*
5115 * Skip separators.
5116 */
5117
5118 if (!attr->name)
5119 return (1);
5120
5121 /*
5122 * Validate the attribute name.
5123 */
5124
5125 for (ptr = attr->name; *ptr; ptr ++)
5126 if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' && *ptr != '_')
5127 break;
5128
5129 if (*ptr || ptr == attr->name)
5130 {
5131 valid = 0;
5132
5133 add_stringf(errors,
5134 "\"%s\": Bad attribute name - invalid character "
5135 "(RFC 2911 section 4.1.3).", attr->name);
5136 }
5137
5138 if ((ptr - attr->name) > 255)
5139 {
5140 valid = 0;
5141
5142 add_stringf(errors,
5143 "\"%s\": Bad attribute name - bad length "
5144 "(RFC 2911 section 4.1.3).", attr->name);
5145 }
5146
5147 switch (attr->value_tag)
5148 {
5149 case IPP_TAG_INTEGER :
5150 break;
5151
5152 case IPP_TAG_BOOLEAN :
5153 for (i = 0; i < attr->num_values; i ++)
5154 {
5155 if (attr->values[i].boolean != 0 &&
5156 attr->values[i].boolean != 1)
5157 {
5158 valid = 0;
5159
5160 add_stringf(errors,
5161 "\"%s\": Bad boolen value %d "
5162 "(RFC 2911 section 4.1.11).", attr->name,
5163 attr->values[i].boolean);
5164 }
5165 }
5166 break;
5167
5168 case IPP_TAG_ENUM :
5169 for (i = 0; i < attr->num_values; i ++)
5170 {
5171 if (attr->values[i].integer < 1)
5172 {
5173 valid = 0;
5174
5175 add_stringf(errors,
5176 "\"%s\": Bad enum value %d - out of range "
5177 "(RFC 2911 section 4.1.4).", attr->name,
5178 attr->values[i].integer);
5179 }
5180 }
5181 break;
5182
5183 case IPP_TAG_STRING :
5184 for (i = 0; i < attr->num_values; i ++)
5185 {
5186 if (attr->values[i].unknown.length > IPP_MAX_OCTETSTRING)
5187 {
5188 valid = 0;
5189
5190 add_stringf(errors,
5191 "\"%s\": Bad octetString value - bad length %d "
5192 "(RFC 2911 section 4.1.10).", attr->name,
5193 attr->values[i].unknown.length);
5194 }
5195 }
5196 break;
5197
5198 case IPP_TAG_DATE :
5199 for (i = 0; i < attr->num_values; i ++)
5200 {
5201 date = attr->values[i].date;
5202
5203 if (date[2] < 1 || date[2] > 12)
5204 {
5205 valid = 0;
5206
5207 add_stringf(errors,
5208 "\"%s\": Bad dateTime month %u "
5209 "(RFC 2911 section 4.1.14).", attr->name, date[2]);
5210 }
5211
5212 if (date[3] < 1 || date[3] > 31)
5213 {
5214 valid = 0;
5215
5216 add_stringf(errors,
5217 "\"%s\": Bad dateTime day %u "
5218 "(RFC 2911 section 4.1.14).", attr->name, date[3]);
5219 }
5220
5221 if (date[4] > 23)
5222 {
5223 valid = 0;
5224
5225 add_stringf(errors,
5226 "\"%s\": Bad dateTime hours %u "
5227 "(RFC 2911 section 4.1.14).", attr->name, date[4]);
5228 }
5229
5230 if (date[5] > 59)
5231 {
5232 valid = 0;
5233
5234 add_stringf(errors,
5235 "\"%s\": Bad dateTime minutes %u "
5236 "(RFC 2911 section 4.1.14).", attr->name, date[5]);
5237 }
5238
5239 if (date[6] > 60)
5240 {
5241 valid = 0;
5242
5243 add_stringf(errors,
5244 "\"%s\": Bad dateTime seconds %u "
5245 "(RFC 2911 section 4.1.14).", attr->name, date[6]);
5246 }
5247
5248 if (date[7] > 9)
5249 {
5250 valid = 0;
5251
5252 add_stringf(errors,
5253 "\"%s\": Bad dateTime deciseconds %u "
5254 "(RFC 2911 section 4.1.14).", attr->name, date[7]);
5255 }
5256
5257 if (date[8] != '-' && date[8] != '+')
5258 {
5259 valid = 0;
5260
5261 add_stringf(errors,
5262 "\"%s\": Bad dateTime UTC sign '%c' "
5263 "(RFC 2911 section 4.1.14).", attr->name, date[8]);
5264 }
5265
5266 if (date[9] > 11)
5267 {
5268 valid = 0;
5269
5270 add_stringf(errors,
5271 "\"%s\": Bad dateTime UTC hours %u "
5272 "(RFC 2911 section 4.1.14).", attr->name, date[9]);
5273 }
5274
5275 if (date[10] > 59)
5276 {
5277 valid = 0;
5278
5279 add_stringf(errors,
5280 "\"%s\": Bad dateTime UTC minutes %u "
5281 "(RFC 2911 section 4.1.14).", attr->name, date[10]);
5282 }
5283 }
5284 break;
5285
5286 case IPP_TAG_RESOLUTION :
5287 for (i = 0; i < attr->num_values; i ++)
5288 {
5289 if (attr->values[i].resolution.xres <= 0)
5290 {
5291 valid = 0;
5292
5293 add_stringf(errors,
5294 "\"%s\": Bad resolution value %dx%d%s - cross "
5295 "feed resolution must be positive "
5296 "(RFC 2911 section 4.1.15).", attr->name,
5297 attr->values[i].resolution.xres,
5298 attr->values[i].resolution.yres,
5299 attr->values[i].resolution.units ==
5300 IPP_RES_PER_INCH ? "dpi" :
5301 attr->values[i].resolution.units ==
5302 IPP_RES_PER_CM ? "dpcm" : "unknown");
5303 }
5304
5305 if (attr->values[i].resolution.yres <= 0)
5306 {
5307 valid = 0;
5308
5309 add_stringf(errors,
5310 "\"%s\": Bad resolution value %dx%d%s - feed "
5311 "resolution must be positive "
5312 "(RFC 2911 section 4.1.15).", attr->name,
5313 attr->values[i].resolution.xres,
5314 attr->values[i].resolution.yres,
5315 attr->values[i].resolution.units ==
5316 IPP_RES_PER_INCH ? "dpi" :
5317 attr->values[i].resolution.units ==
5318 IPP_RES_PER_CM ? "dpcm" : "unknown");
5319 }
5320
5321 if (attr->values[i].resolution.units != IPP_RES_PER_INCH &&
5322 attr->values[i].resolution.units != IPP_RES_PER_CM)
5323 {
5324 valid = 0;
5325
5326 add_stringf(errors,
5327 "\"%s\": Bad resolution value %dx%d%s - bad "
5328 "units value (RFC 2911 section 4.1.15).",
5329 attr->name, attr->values[i].resolution.xres,
5330 attr->values[i].resolution.yres,
5331 attr->values[i].resolution.units ==
5332 IPP_RES_PER_INCH ? "dpi" :
5333 attr->values[i].resolution.units ==
5334 IPP_RES_PER_CM ? "dpcm" : "unknown");
5335 }
5336 }
5337 break;
5338
5339 case IPP_TAG_RANGE :
5340 for (i = 0; i < attr->num_values; i ++)
5341 {
5342 if (attr->values[i].range.lower > attr->values[i].range.upper)
5343 {
5344 valid = 0;
5345
5346 add_stringf(errors,
5347 "\"%s\": Bad rangeOfInteger value %d-%d - lower "
5348 "greater than upper (RFC 2911 section 4.1.13).",
5349 attr->name, attr->values[i].range.lower,
5350 attr->values[i].range.upper);
5351 }
5352 }
5353 break;
5354
5355 case IPP_TAG_BEGIN_COLLECTION :
5356 for (i = 0; i < attr->num_values; i ++)
5357 {
5358 for (colattr = attr->values[i].collection->attrs;
5359 colattr;
5360 colattr = colattr->next)
5361 {
5362 if (!validate_attr(outfile, NULL, colattr))
5363 {
5364 valid = 0;
5365 break;
5366 }
5367 }
5368
5369 if (colattr && errors)
5370 {
5371 add_stringf(errors, "\"%s\": Bad collection value.", attr->name);
5372
5373 while (colattr)
5374 {
5375 validate_attr(outfile, errors, colattr);
5376 colattr = colattr->next;
5377 }
5378 }
5379 }
5380 break;
5381
5382 case IPP_TAG_TEXT :
5383 case IPP_TAG_TEXTLANG :
5384 for (i = 0; i < attr->num_values; i ++)
5385 {
5386 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
5387 {
5388 if ((*ptr & 0xe0) == 0xc0)
5389 {
5390 ptr ++;
5391 if ((*ptr & 0xc0) != 0x80)
5392 break;
5393 }
5394 else if ((*ptr & 0xf0) == 0xe0)
5395 {
5396 ptr ++;
5397 if ((*ptr & 0xc0) != 0x80)
5398 break;
5399 ptr ++;
5400 if ((*ptr & 0xc0) != 0x80)
5401 break;
5402 }
5403 else if ((*ptr & 0xf8) == 0xf0)
5404 {
5405 ptr ++;
5406 if ((*ptr & 0xc0) != 0x80)
5407 break;
5408 ptr ++;
5409 if ((*ptr & 0xc0) != 0x80)
5410 break;
5411 ptr ++;
5412 if ((*ptr & 0xc0) != 0x80)
5413 break;
5414 }
5415 else if (*ptr & 0x80)
5416 break;
5417 }
5418
5419 if (*ptr)
5420 {
5421 valid = 0;
5422
5423 add_stringf(errors,
5424 "\"%s\": Bad text value \"%s\" - bad UTF-8 "
5425 "sequence (RFC 2911 section 4.1.1).", attr->name,
5426 attr->values[i].string.text);
5427 }
5428
5429 if ((ptr - attr->values[i].string.text) > (IPP_MAX_TEXT - 1))
5430 {
5431 valid = 0;
5432
5433 add_stringf(errors,
5434 "\"%s\": Bad text value \"%s\" - bad length %d "
5435 "(RFC 2911 section 4.1.1).", attr->name,
5436 attr->values[i].string.text,
5437 (int)strlen(attr->values[i].string.text));
5438 }
5439 }
5440 break;
5441
5442 case IPP_TAG_NAME :
5443 case IPP_TAG_NAMELANG :
5444 for (i = 0; i < attr->num_values; i ++)
5445 {
5446 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
5447 {
5448 if ((*ptr & 0xe0) == 0xc0)
5449 {
5450 ptr ++;
5451 if ((*ptr & 0xc0) != 0x80)
5452 break;
5453 }
5454 else if ((*ptr & 0xf0) == 0xe0)
5455 {
5456 ptr ++;
5457 if ((*ptr & 0xc0) != 0x80)
5458 break;
5459 ptr ++;
5460 if ((*ptr & 0xc0) != 0x80)
5461 break;
5462 }
5463 else if ((*ptr & 0xf8) == 0xf0)
5464 {
5465 ptr ++;
5466 if ((*ptr & 0xc0) != 0x80)
5467 break;
5468 ptr ++;
5469 if ((*ptr & 0xc0) != 0x80)
5470 break;
5471 ptr ++;
5472 if ((*ptr & 0xc0) != 0x80)
5473 break;
5474 }
5475 else if (*ptr & 0x80)
5476 break;
5477 }
5478
5479 if (*ptr)
5480 {
5481 valid = 0;
5482
5483 add_stringf(errors,
5484 "\"%s\": Bad name value \"%s\" - bad UTF-8 "
5485 "sequence (RFC 2911 section 4.1.2).", attr->name,
5486 attr->values[i].string.text);
5487 }
5488
5489 if ((ptr - attr->values[i].string.text) > (IPP_MAX_NAME - 1))
5490 {
5491 valid = 0;
5492
5493 add_stringf(errors,
5494 "\"%s\": Bad name value \"%s\" - bad length %d "
5495 "(RFC 2911 section 4.1.2).", attr->name,
5496 attr->values[i].string.text,
5497 (int)strlen(attr->values[i].string.text));
5498 }
5499 }
5500 break;
5501
5502 case IPP_TAG_KEYWORD :
5503 for (i = 0; i < attr->num_values; i ++)
5504 {
5505 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
5506 if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' &&
5507 *ptr != '_')
5508 break;
5509
5510 if (*ptr || ptr == attr->values[i].string.text)
5511 {
5512 valid = 0;
5513
5514 add_stringf(errors,
5515 "\"%s\": Bad keyword value \"%s\" - invalid "
5516 "character (RFC 2911 section 4.1.3).",
5517 attr->name, attr->values[i].string.text);
5518 }
5519
5520 if ((ptr - attr->values[i].string.text) > (IPP_MAX_KEYWORD - 1))
5521 {
5522 valid = 0;
5523
5524 add_stringf(errors,
5525 "\"%s\": Bad keyword value \"%s\" - bad "
5526 "length %d (RFC 2911 section 4.1.3).",
5527 attr->name, attr->values[i].string.text,
5528 (int)strlen(attr->values[i].string.text));
5529 }
5530 }
5531 break;
5532
5533 case IPP_TAG_URI :
5534 for (i = 0; i < attr->num_values; i ++)
5535 {
5536 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL,
5537 attr->values[i].string.text,
5538 scheme, sizeof(scheme),
5539 userpass, sizeof(userpass),
5540 hostname, sizeof(hostname),
5541 &port, resource, sizeof(resource));
5542
5543 if (uri_status < HTTP_URI_OK)
5544 {
5545 valid = 0;
5546
5547 add_stringf(errors,
5548 "\"%s\": Bad URI value \"%s\" - %s "
5549 "(RFC 2911 section 4.1.5).", attr->name,
5550 attr->values[i].string.text,
5551 httpURIStatusString(uri_status));
5552 }
5553
5554 if (strlen(attr->values[i].string.text) > (IPP_MAX_URI - 1))
5555 {
5556 valid = 0;
5557
5558 add_stringf(errors,
5559 "\"%s\": Bad URI value \"%s\" - bad length %d "
5560 "(RFC 2911 section 4.1.5).", attr->name,
5561 attr->values[i].string.text,
5562 (int)strlen(attr->values[i].string.text));
5563 }
5564 }
5565 break;
5566
5567 case IPP_TAG_URISCHEME :
5568 for (i = 0; i < attr->num_values; i ++)
5569 {
5570 ptr = attr->values[i].string.text;
5571 if (islower(*ptr & 255))
5572 {
5573 for (ptr ++; *ptr; ptr ++)
5574 if (!islower(*ptr & 255) && !isdigit(*ptr & 255) &&
5575 *ptr != '+' && *ptr != '-' && *ptr != '.')
5576 break;
5577 }
5578
5579 if (*ptr || ptr == attr->values[i].string.text)
5580 {
5581 valid = 0;
5582
5583 add_stringf(errors,
5584 "\"%s\": Bad uriScheme value \"%s\" - bad "
5585 "characters (RFC 2911 section 4.1.6).",
5586 attr->name, attr->values[i].string.text);
5587 }
5588
5589 if ((ptr - attr->values[i].string.text) > (IPP_MAX_URISCHEME - 1))
5590 {
5591 valid = 0;
5592
5593 add_stringf(errors,
5594 "\"%s\": Bad uriScheme value \"%s\" - bad "
5595 "length %d (RFC 2911 section 4.1.6).",
5596 attr->name, attr->values[i].string.text,
5597 (int)strlen(attr->values[i].string.text));
5598 }
5599 }
5600 break;
5601
5602 case IPP_TAG_CHARSET :
5603 for (i = 0; i < attr->num_values; i ++)
5604 {
5605 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
5606 if (!isprint(*ptr & 255) || isupper(*ptr & 255) ||
5607 isspace(*ptr & 255))
5608 break;
5609
5610 if (*ptr || ptr == attr->values[i].string.text)
5611 {
5612 valid = 0;
5613
5614 add_stringf(errors,
5615 "\"%s\": Bad charset value \"%s\" - bad "
5616 "characters (RFC 2911 section 4.1.7).",
5617 attr->name, attr->values[i].string.text);
5618 }
5619
5620 if ((ptr - attr->values[i].string.text) > (IPP_MAX_CHARSET - 1))
5621 {
5622 valid = 0;
5623
5624 add_stringf(errors,
5625 "\"%s\": Bad charset value \"%s\" - bad "
5626 "length %d (RFC 2911 section 4.1.7).",
5627 attr->name, attr->values[i].string.text,
5628 (int)strlen(attr->values[i].string.text));
5629 }
5630 }
5631 break;
5632
5633 case IPP_TAG_LANGUAGE :
5634 /*
5635 * The following regular expression is derived from the ABNF for
5636 * language tags in RFC 4646. All I can say is that this is the
5637 * easiest way to check the values...
5638 */
5639
5640 if ((i = regcomp(&re,
5641 "^("
5642 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
5643 /* language */
5644 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
5645 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
5646 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
5647 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
5648 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
5649 "|"
5650 "x(-[a-z0-9]{1,8})+" /* privateuse */
5651 "|"
5652 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
5653 ")$",
5654 REG_NOSUB | REG_EXTENDED)) != 0)
5655 {
5656 char temp[256]; /* Temporary error string */
5657
5658 regerror(i, &re, temp, sizeof(temp));
5659 print_fatal_error(outfile, "Unable to compile naturalLanguage regular "
5660 "expression: %s.", temp);
5661 break;
5662 }
5663
5664 for (i = 0; i < attr->num_values; i ++)
5665 {
5666 if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
5667 {
5668 valid = 0;
5669
5670 add_stringf(errors,
5671 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5672 "characters (RFC 2911 section 4.1.8).",
5673 attr->name, attr->values[i].string.text);
5674 }
5675
5676 if (strlen(attr->values[i].string.text) > (IPP_MAX_LANGUAGE - 1))
5677 {
5678 valid = 0;
5679
5680 add_stringf(errors,
5681 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5682 "length %d (RFC 2911 section 4.1.8).",
5683 attr->name, attr->values[i].string.text,
5684 (int)strlen(attr->values[i].string.text));
5685 }
5686 }
5687
5688 regfree(&re);
5689 break;
5690
5691 case IPP_TAG_MIMETYPE :
5692 /*
5693 * The following regular expression is derived from the ABNF for
5694 * language tags in RFC 2045 and 4288. All I can say is that this is
5695 * the easiest way to check the values...
5696 */
5697
5698 if ((i = regcomp(&re,
5699 "^"
5700 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
5701 "/"
5702 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
5703 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
5704 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
5705 /* value */
5706 "$",
5707 REG_NOSUB | REG_EXTENDED)) != 0)
5708 {
5709 char temp[256]; /* Temporary error string */
5710
5711 regerror(i, &re, temp, sizeof(temp));
5712 print_fatal_error(outfile, "Unable to compile mimeMediaType regular "
5713 "expression: %s.", temp);
5714 break;
5715 }
5716
5717 for (i = 0; i < attr->num_values; i ++)
5718 {
5719 if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
5720 {
5721 valid = 0;
5722
5723 add_stringf(errors,
5724 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5725 "characters (RFC 2911 section 4.1.9).",
5726 attr->name, attr->values[i].string.text);
5727 }
5728
5729 if (strlen(attr->values[i].string.text) > (IPP_MAX_MIMETYPE - 1))
5730 {
5731 valid = 0;
5732
5733 add_stringf(errors,
5734 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5735 "length %d (RFC 2911 section 4.1.9).",
5736 attr->name, attr->values[i].string.text,
5737 (int)strlen(attr->values[i].string.text));
5738 }
5739 }
5740
5741 regfree(&re);
5742 break;
5743
5744 default :
5745 break;
5746 }
5747
5748 return (valid);
5749 }
5750
5751
5752 /*
5753 * 'with_value()' - Test a WITH-VALUE predicate.
5754 */
5755
5756 static int /* O - 1 on match, 0 on non-match */
5757 with_value(cups_file_t *outfile, /* I - Output file */
5758 cups_array_t *errors, /* I - Errors array */
5759 char *value, /* I - Value string */
5760 int flags, /* I - Flags for match */
5761 ipp_attribute_t *attr, /* I - Attribute to compare */
5762 char *matchbuf, /* I - Buffer to hold matching value */
5763 size_t matchlen) /* I - Length of match buffer */
5764 {
5765 int i, /* Looping var */
5766 match; /* Match? */
5767 char temp[1024], /* Temporary value string */
5768 *valptr; /* Pointer into value */
5769
5770
5771 *matchbuf = '\0';
5772 match = (flags & _CUPS_WITH_ALL) ? 1 : 0;
5773
5774 /*
5775 * NULL matches everything.
5776 */
5777
5778 if (!value || !*value)
5779 return (1);
5780
5781 /*
5782 * Compare the value string to the attribute value.
5783 */
5784
5785 switch (attr->value_tag)
5786 {
5787 case IPP_TAG_INTEGER :
5788 case IPP_TAG_ENUM :
5789 for (i = 0; i < attr->num_values; i ++)
5790 {
5791 char op, /* Comparison operator */
5792 *nextptr; /* Next pointer */
5793 int intvalue, /* Integer value */
5794 valmatch = 0; /* Does the current value match? */
5795
5796 valptr = value;
5797
5798 while (isspace(*valptr & 255) || isdigit(*valptr & 255) ||
5799 *valptr == '-' || *valptr == ',' || *valptr == '<' ||
5800 *valptr == '=' || *valptr == '>')
5801 {
5802 op = '=';
5803 while (*valptr && !isdigit(*valptr & 255) && *valptr != '-')
5804 {
5805 if (*valptr == '<' || *valptr == '>' || *valptr == '=')
5806 op = *valptr;
5807 valptr ++;
5808 }
5809
5810 if (!*valptr)
5811 break;
5812
5813 intvalue = (int)strtol(valptr, &nextptr, 0);
5814 if (nextptr == valptr)
5815 break;
5816 valptr = nextptr;
5817
5818 if ((op == '=' && attr->values[i].integer == intvalue) ||
5819 (op == '<' && attr->values[i].integer < intvalue) ||
5820 (op == '>' && attr->values[i].integer > intvalue))
5821 {
5822 if (!matchbuf[0])
5823 snprintf(matchbuf, matchlen, "%d", attr->values[i].integer);
5824
5825 valmatch = 1;
5826 break;
5827 }
5828 }
5829
5830 if (flags & _CUPS_WITH_ALL)
5831 {
5832 if (!valmatch)
5833 {
5834 match = 0;
5835 break;
5836 }
5837 }
5838 else if (valmatch)
5839 {
5840 match = 1;
5841 break;
5842 }
5843 }
5844
5845 if (!match && errors)
5846 {
5847 for (i = 0; i < attr->num_values; i ++)
5848 add_stringf(errors, "GOT: %s=%d", attr->name,
5849 attr->values[i].integer);
5850 }
5851 break;
5852
5853 case IPP_TAG_RANGE :
5854 for (i = 0; i < attr->num_values; i ++)
5855 {
5856 char op, /* Comparison operator */
5857 *nextptr; /* Next pointer */
5858 int intvalue, /* Integer value */
5859 valmatch = 0; /* Does the current value match? */
5860
5861 valptr = value;
5862
5863 while (isspace(*valptr & 255) || isdigit(*valptr & 255) ||
5864 *valptr == '-' || *valptr == ',' || *valptr == '<' ||
5865 *valptr == '=' || *valptr == '>')
5866 {
5867 op = '=';
5868 while (*valptr && !isdigit(*valptr & 255) && *valptr != '-')
5869 {
5870 if (*valptr == '<' || *valptr == '>' || *valptr == '=')
5871 op = *valptr;
5872 valptr ++;
5873 }
5874
5875 if (!*valptr)
5876 break;
5877
5878 intvalue = (int)strtol(valptr, &nextptr, 0);
5879 if (nextptr == valptr)
5880 break;
5881 valptr = nextptr;
5882
5883 if ((op == '=' && (attr->values[i].range.lower == intvalue ||
5884 attr->values[i].range.upper == intvalue)) ||
5885 (op == '<' && attr->values[i].range.upper < intvalue) ||
5886 (op == '>' && attr->values[i].range.upper > intvalue))
5887 {
5888 if (!matchbuf[0])
5889 snprintf(matchbuf, matchlen, "%d-%d",
5890 attr->values[0].range.lower,
5891 attr->values[0].range.upper);
5892
5893 valmatch = 1;
5894 break;
5895 }
5896 }
5897
5898 if (flags & _CUPS_WITH_ALL)
5899 {
5900 if (!valmatch)
5901 {
5902 match = 0;
5903 break;
5904 }
5905 }
5906 else if (valmatch)
5907 {
5908 match = 1;
5909 break;
5910 }
5911 }
5912
5913 if (!match && errors)
5914 {
5915 for (i = 0; i < attr->num_values; i ++)
5916 add_stringf(errors, "GOT: %s=%d-%d", attr->name,
5917 attr->values[i].range.lower,
5918 attr->values[i].range.upper);
5919 }
5920 break;
5921
5922 case IPP_TAG_BOOLEAN :
5923 for (i = 0; i < attr->num_values; i ++)
5924 {
5925 if ((!strcmp(value, "true")) == attr->values[i].boolean)
5926 {
5927 if (!matchbuf[0])
5928 strlcpy(matchbuf, value, matchlen);
5929
5930 if (!(flags & _CUPS_WITH_ALL))
5931 {
5932 match = 1;
5933 break;
5934 }
5935 }
5936 else if (flags & _CUPS_WITH_ALL)
5937 {
5938 match = 0;
5939 break;
5940 }
5941 }
5942
5943 if (!match && errors)
5944 {
5945 for (i = 0; i < attr->num_values; i ++)
5946 add_stringf(errors, "GOT: %s=%s", attr->name,
5947 attr->values[i].boolean ? "true" : "false");
5948 }
5949 break;
5950
5951 case IPP_TAG_RESOLUTION :
5952 for (i = 0; i < attr->num_values; i ++)
5953 {
5954 if (attr->values[i].resolution.xres ==
5955 attr->values[i].resolution.yres)
5956 snprintf(temp, sizeof(temp), "%d%s",
5957 attr->values[i].resolution.xres,
5958 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
5959 "dpi" : "dpcm");
5960 else
5961 snprintf(temp, sizeof(temp), "%dx%d%s",
5962 attr->values[i].resolution.xres,
5963 attr->values[i].resolution.yres,
5964 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
5965 "dpi" : "dpcm");
5966
5967 if (!strcmp(value, temp))
5968 {
5969 if (!matchbuf[0])
5970 strlcpy(matchbuf, value, matchlen);
5971
5972 if (!(flags & _CUPS_WITH_ALL))
5973 {
5974 match = 1;
5975 break;
5976 }
5977 }
5978 else if (flags & _CUPS_WITH_ALL)
5979 {
5980 match = 0;
5981 break;
5982 }
5983 }
5984
5985 if (!match && errors)
5986 {
5987 for (i = 0; i < attr->num_values; i ++)
5988 {
5989 if (attr->values[i].resolution.xres ==
5990 attr->values[i].resolution.yres)
5991 snprintf(temp, sizeof(temp), "%d%s",
5992 attr->values[i].resolution.xres,
5993 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
5994 "dpi" : "dpcm");
5995 else
5996 snprintf(temp, sizeof(temp), "%dx%d%s",
5997 attr->values[i].resolution.xres,
5998 attr->values[i].resolution.yres,
5999 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
6000 "dpi" : "dpcm");
6001
6002 if (strcmp(value, temp))
6003 add_stringf(errors, "GOT: %s=%s", attr->name, temp);
6004 }
6005 }
6006 break;
6007
6008 case IPP_TAG_NOVALUE :
6009 case IPP_TAG_UNKNOWN :
6010 return (1);
6011
6012 case IPP_TAG_CHARSET :
6013 case IPP_TAG_KEYWORD :
6014 case IPP_TAG_LANGUAGE :
6015 case IPP_TAG_MIMETYPE :
6016 case IPP_TAG_NAME :
6017 case IPP_TAG_NAMELANG :
6018 case IPP_TAG_TEXT :
6019 case IPP_TAG_TEXTLANG :
6020 case IPP_TAG_URI :
6021 case IPP_TAG_URISCHEME :
6022 if (flags & _CUPS_WITH_REGEX)
6023 {
6024 /*
6025 * Value is an extended, case-sensitive POSIX regular expression...
6026 */
6027
6028 regex_t re; /* Regular expression */
6029
6030 if ((i = regcomp(&re, value, REG_EXTENDED | REG_NOSUB)) != 0)
6031 {
6032 regerror(i, &re, temp, sizeof(temp));
6033
6034 print_fatal_error(outfile, "Unable to compile WITH-VALUE regular expression "
6035 "\"%s\" - %s", value, temp);
6036 return (0);
6037 }
6038
6039 /*
6040 * See if ALL of the values match the given regular expression.
6041 */
6042
6043 for (i = 0; i < attr->num_values; i ++)
6044 {
6045 if (!regexec(&re, get_string(attr, i, flags, temp, sizeof(temp)),
6046 0, NULL, 0))
6047 {
6048 if (!matchbuf[0])
6049 strlcpy(matchbuf,
6050 get_string(attr, i, flags, temp, sizeof(temp)),
6051 matchlen);
6052
6053 if (!(flags & _CUPS_WITH_ALL))
6054 {
6055 match = 1;
6056 break;
6057 }
6058 }
6059 else if (flags & _CUPS_WITH_ALL)
6060 {
6061 match = 0;
6062 break;
6063 }
6064 }
6065
6066 regfree(&re);
6067 }
6068 else if (ippGetValueTag(attr) == IPP_TAG_URI)
6069 {
6070 /*
6071 * Value is a literal URI string, see if the value(s) match...
6072 */
6073
6074 for (i = 0; i < attr->num_values; i ++)
6075 {
6076 if (!compare_uris(value, get_string(attr, i, flags, temp, sizeof(temp))))
6077 {
6078 if (!matchbuf[0])
6079 strlcpy(matchbuf,
6080 get_string(attr, i, flags, temp, sizeof(temp)),
6081 matchlen);
6082
6083 if (!(flags & _CUPS_WITH_ALL))
6084 {
6085 match = 1;
6086 break;
6087 }
6088 }
6089 else if (flags & _CUPS_WITH_ALL)
6090 {
6091 match = 0;
6092 break;
6093 }
6094 }
6095 }
6096 else
6097 {
6098 /*
6099 * Value is a literal string, see if the value(s) match...
6100 */
6101
6102 for (i = 0; i < attr->num_values; i ++)
6103 {
6104 if (!strcmp(value, get_string(attr, i, flags, temp, sizeof(temp))))
6105 {
6106 if (!matchbuf[0])
6107 strlcpy(matchbuf,
6108 get_string(attr, i, flags, temp, sizeof(temp)),
6109 matchlen);
6110
6111 if (!(flags & _CUPS_WITH_ALL))
6112 {
6113 match = 1;
6114 break;
6115 }
6116 }
6117 else if (flags & _CUPS_WITH_ALL)
6118 {
6119 match = 0;
6120 break;
6121 }
6122 }
6123 }
6124
6125 if (!match && errors)
6126 {
6127 for (i = 0; i < attr->num_values; i ++)
6128 add_stringf(errors, "GOT: %s=\"%s\"", attr->name,
6129 attr->values[i].string.text);
6130 }
6131 break;
6132
6133 default :
6134 break;
6135 }
6136
6137 return (match);
6138 }
6139
6140
6141 /*
6142 * 'with_value_from()' - Test a WITH-VALUE-FROM predicate.
6143 */
6144
6145 static int /* O - 1 on match, 0 on non-match */
6146 with_value_from(
6147 cups_array_t *errors, /* I - Errors array */
6148 ipp_attribute_t *fromattr, /* I - "From" attribute */
6149 ipp_attribute_t *attr, /* I - Attribute to compare */
6150 char *matchbuf, /* I - Buffer to hold matching value */
6151 size_t matchlen) /* I - Length of match buffer */
6152 {
6153 int i, j, /* Looping vars */
6154 count = ippGetCount(attr), /* Number of attribute values */
6155 match = 1; /* Match? */
6156
6157
6158 *matchbuf = '\0';
6159
6160 /*
6161 * Compare the from value(s) to the attribute value(s)...
6162 */
6163
6164 switch (ippGetValueTag(attr))
6165 {
6166 case IPP_TAG_INTEGER :
6167 if (ippGetValueTag(fromattr) != IPP_TAG_INTEGER && ippGetValueTag(fromattr) != IPP_TAG_RANGE)
6168 goto wrong_value_tag;
6169
6170 for (i = 0; i < count; i ++)
6171 {
6172 int value = ippGetInteger(attr, i);
6173 /* Current integer value */
6174
6175 if (ippContainsInteger(fromattr, value))
6176 {
6177 if (!matchbuf[0])
6178 snprintf(matchbuf, matchlen, "%d", value);
6179 }
6180 else
6181 {
6182 add_stringf(errors, "GOT: %s=%d", ippGetName(attr), value);
6183 match = 0;
6184 }
6185 }
6186 break;
6187
6188 case IPP_TAG_ENUM :
6189 if (ippGetValueTag(fromattr) != IPP_TAG_ENUM)
6190 goto wrong_value_tag;
6191
6192 for (i = 0; i < count; i ++)
6193 {
6194 int value = ippGetInteger(attr, i);
6195 /* Current integer value */
6196
6197 if (ippContainsInteger(fromattr, value))
6198 {
6199 if (!matchbuf[0])
6200 snprintf(matchbuf, matchlen, "%d", value);
6201 }
6202 else
6203 {
6204 add_stringf(errors, "GOT: %s=%d", ippGetName(attr), value);
6205 match = 0;
6206 }
6207 }
6208 break;
6209
6210 case IPP_TAG_RESOLUTION :
6211 if (ippGetValueTag(fromattr) != IPP_TAG_RESOLUTION)
6212 goto wrong_value_tag;
6213
6214 for (i = 0; i < count; i ++)
6215 {
6216 int xres, yres;
6217 ipp_res_t units;
6218 int fromcount = ippGetCount(fromattr);
6219 int fromxres, fromyres;
6220 ipp_res_t fromunits;
6221
6222 xres = ippGetResolution(attr, i, &yres, &units);
6223
6224 for (j = 0; j < fromcount; j ++)
6225 {
6226 fromxres = ippGetResolution(fromattr, j, &fromyres, &fromunits);
6227 if (fromxres == xres && fromyres == yres && fromunits == units)
6228 break;
6229 }
6230
6231 if (j < fromcount)
6232 {
6233 if (!matchbuf[0])
6234 {
6235 if (xres == yres)
6236 snprintf(matchbuf, matchlen, "%d%s", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
6237 else
6238 snprintf(matchbuf, matchlen, "%dx%d%s", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
6239 }
6240 }
6241 else
6242 {
6243 if (xres == yres)
6244 add_stringf(errors, "GOT: %s=%d%s", ippGetName(attr), xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
6245 else
6246 add_stringf(errors, "GOT: %s=%dx%d%s", ippGetName(attr), xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
6247
6248 match = 0;
6249 }
6250 }
6251 break;
6252
6253 case IPP_TAG_NOVALUE :
6254 case IPP_TAG_UNKNOWN :
6255 return (1);
6256
6257 case IPP_TAG_CHARSET :
6258 case IPP_TAG_KEYWORD :
6259 case IPP_TAG_LANGUAGE :
6260 case IPP_TAG_MIMETYPE :
6261 case IPP_TAG_NAME :
6262 case IPP_TAG_NAMELANG :
6263 case IPP_TAG_TEXT :
6264 case IPP_TAG_TEXTLANG :
6265 case IPP_TAG_URI :
6266 case IPP_TAG_URISCHEME :
6267 for (i = 0; i < count; i ++)
6268 {
6269 const char *value = ippGetString(attr, i, NULL);
6270 /* Current string value */
6271
6272 if (ippContainsString(fromattr, value))
6273 {
6274 if (!matchbuf[0])
6275 strlcpy(matchbuf, value, matchlen);
6276 }
6277 else
6278 {
6279 add_stringf(errors, "GOT: %s='%s'", ippGetName(attr), value);
6280 match = 0;
6281 }
6282 }
6283 break;
6284
6285 default :
6286 match = 0;
6287 break;
6288 }
6289
6290 return (match);
6291
6292 /* value tag mismatch between fromattr and attr */
6293 wrong_value_tag :
6294
6295 add_stringf(errors, "GOT: %s OF-TYPE %s", ippGetName(attr), ippTagString(ippGetValueTag(attr)));
6296
6297 return (0);
6298 }