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