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