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