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