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