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