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