]> git.ipfire.org Git - thirdparty/cups.git/blob - test/ipptool.c
Save work on IPP Everywhere updates.
[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
3094 sleep((unsigned)repeat_interval);
3095 repeat_interval = _cupsNextDelay(repeat_interval, &repeat_prev);
3096
3097 if (Output == _CUPS_OUTPUT_TEST || (Output == _CUPS_OUTPUT_PLIST && outfile != stdout))
3098 {
3099 printf(" %-68.68s [", name);
3100 fflush(stdout);
3101 }
3102 }
3103 }
3104 while (repeat_test);
3105
3106 ippDelete(request);
3107
3108 request = NULL;
3109
3110 if (cupsArrayCount(errors) > 0)
3111 prev_pass = pass = 0;
3112
3113 if (prev_pass)
3114 PassCount ++;
3115 else
3116 FailCount ++;
3117
3118 if (Output == _CUPS_OUTPUT_PLIST)
3119 {
3120 fputs("<key>Successful</key>\n", outfile);
3121 fputs(prev_pass ? "<true />\n" : "<false />\n", outfile);
3122 fputs("<key>StatusCode</key>\n", outfile);
3123 print_xml_string(outfile, "string", ippErrorString(cupsLastError()));
3124 fputs("<key>ResponseAttributes</key>\n", outfile);
3125 fputs("<array>\n", outfile);
3126 fputs("<dict>\n", outfile);
3127 for (attrptr = response ? response->attrs : NULL,
3128 group = attrptr ? attrptr->group_tag : IPP_TAG_ZERO;
3129 attrptr;
3130 attrptr = attrptr->next)
3131 print_attr(outfile, Output, attrptr, &group);
3132 fputs("</dict>\n", outfile);
3133 fputs("</array>\n", outfile);
3134 }
3135
3136 if (Output == _CUPS_OUTPUT_TEST || (Output == _CUPS_OUTPUT_PLIST && outfile != stdout))
3137 {
3138 puts(prev_pass ? "PASS]" : "FAIL]");
3139
3140 if (!prev_pass || (Verbosity && response))
3141 {
3142 printf(" RECEIVED: %lu bytes in response\n",
3143 (unsigned long)ippLength(response));
3144 printf(" status-code = %s (%s)\n", ippErrorString(cupsLastError()),
3145 cupsLastErrorString());
3146
3147 if (response)
3148 {
3149 for (attrptr = response->attrs;
3150 attrptr != NULL;
3151 attrptr = attrptr->next)
3152 print_attr(stdout, _CUPS_OUTPUT_TEST, attrptr, NULL);
3153 }
3154 }
3155 }
3156 else if (!prev_pass)
3157 fprintf(stderr, "%s\n", cupsLastErrorString());
3158
3159 if (prev_pass && Output >= _CUPS_OUTPUT_LIST && !Verbosity &&
3160 num_displayed > 0)
3161 {
3162 size_t width; /* Length of value */
3163
3164 for (i = 0; i < num_displayed; i ++)
3165 {
3166 widths[i] = strlen(displayed[i]);
3167
3168 for (attrptr = ippFindAttribute(response, displayed[i], IPP_TAG_ZERO);
3169 attrptr;
3170 attrptr = ippFindNextAttribute(response, displayed[i],
3171 IPP_TAG_ZERO))
3172 {
3173 width = ippAttributeString(attrptr, NULL, 0);
3174 if (width > widths[i])
3175 widths[i] = width;
3176 }
3177 }
3178
3179 if (Output == _CUPS_OUTPUT_CSV)
3180 print_csv(outfile, NULL, num_displayed, displayed, widths);
3181 else
3182 print_line(outfile, NULL, num_displayed, displayed, widths);
3183
3184 attrptr = response->attrs;
3185
3186 while (attrptr)
3187 {
3188 while (attrptr && attrptr->group_tag <= IPP_TAG_OPERATION)
3189 attrptr = attrptr->next;
3190
3191 if (attrptr)
3192 {
3193 if (Output == _CUPS_OUTPUT_CSV)
3194 print_csv(outfile, attrptr, num_displayed, displayed, widths);
3195 else
3196 print_line(outfile, attrptr, num_displayed, displayed, widths);
3197
3198 while (attrptr && attrptr->group_tag > IPP_TAG_OPERATION)
3199 attrptr = attrptr->next;
3200 }
3201 }
3202 }
3203 else if (!prev_pass)
3204 {
3205 if (Output == _CUPS_OUTPUT_PLIST)
3206 {
3207 fputs("<key>Errors</key>\n", outfile);
3208 fputs("<array>\n", outfile);
3209
3210 for (error = (char *)cupsArrayFirst(errors);
3211 error;
3212 error = (char *)cupsArrayNext(errors))
3213 print_xml_string(outfile, "string", error);
3214
3215 fputs("</array>\n", outfile);
3216 }
3217
3218 if (Output == _CUPS_OUTPUT_TEST || (Output == _CUPS_OUTPUT_PLIST && outfile != stdout))
3219 {
3220 for (error = (char *)cupsArrayFirst(errors);
3221 error;
3222 error = (char *)cupsArrayNext(errors))
3223 printf(" %s\n", error);
3224 }
3225 }
3226
3227 if (num_displayed > 0 && !Verbosity && response && Output == _CUPS_OUTPUT_TEST)
3228 {
3229 for (attrptr = response->attrs;
3230 attrptr != NULL;
3231 attrptr = attrptr->next)
3232 {
3233 if (attrptr->name)
3234 {
3235 for (i = 0; i < num_displayed; i ++)
3236 {
3237 if (!strcmp(displayed[i], attrptr->name))
3238 {
3239 print_attr(outfile, Output, attrptr, NULL);
3240 break;
3241 }
3242 }
3243 }
3244 }
3245 }
3246
3247 skip_error:
3248
3249 if (Output == _CUPS_OUTPUT_PLIST)
3250 fputs("</dict>\n", outfile);
3251
3252 fflush(stdout);
3253
3254 ippDelete(response);
3255 response = NULL;
3256
3257 for (i = 0; i < num_statuses; i ++)
3258 {
3259 if (statuses[i].if_defined)
3260 free(statuses[i].if_defined);
3261 if (statuses[i].if_not_defined)
3262 free(statuses[i].if_not_defined);
3263 if (statuses[i].define_match)
3264 free(statuses[i].define_match);
3265 if (statuses[i].define_no_match)
3266 free(statuses[i].define_no_match);
3267 }
3268 num_statuses = 0;
3269
3270 for (i = num_expects, expect = expects; i > 0; i --, expect ++)
3271 {
3272 free(expect->name);
3273 if (expect->of_type)
3274 free(expect->of_type);
3275 if (expect->same_count_as)
3276 free(expect->same_count_as);
3277 if (expect->if_defined)
3278 free(expect->if_defined);
3279 if (expect->if_not_defined)
3280 free(expect->if_not_defined);
3281 if (expect->with_value)
3282 free(expect->with_value);
3283 if (expect->define_match)
3284 free(expect->define_match);
3285 if (expect->define_no_match)
3286 free(expect->define_no_match);
3287 if (expect->define_value)
3288 free(expect->define_value);
3289 }
3290 num_expects = 0;
3291
3292 for (i = 0; i < num_displayed; i ++)
3293 free(displayed[i]);
3294 num_displayed = 0;
3295
3296 if (!ignore_errors && !prev_pass)
3297 break;
3298 }
3299
3300 test_exit:
3301
3302 cupsArrayDelete(errors);
3303
3304 if (fp)
3305 fclose(fp);
3306
3307 httpClose(http);
3308 ippDelete(request);
3309 ippDelete(response);
3310
3311 for (i = 0; i < num_statuses; i ++)
3312 {
3313 if (statuses[i].if_defined)
3314 free(statuses[i].if_defined);
3315 if (statuses[i].if_not_defined)
3316 free(statuses[i].if_not_defined);
3317 if (statuses[i].define_match)
3318 free(statuses[i].define_match);
3319 if (statuses[i].define_no_match)
3320 free(statuses[i].define_no_match);
3321 }
3322
3323 for (i = num_expects, expect = expects; i > 0; i --, expect ++)
3324 {
3325 free(expect->name);
3326 if (expect->of_type)
3327 free(expect->of_type);
3328 if (expect->same_count_as)
3329 free(expect->same_count_as);
3330 if (expect->if_defined)
3331 free(expect->if_defined);
3332 if (expect->if_not_defined)
3333 free(expect->if_not_defined);
3334 if (expect->with_value)
3335 free(expect->with_value);
3336 if (expect->define_match)
3337 free(expect->define_match);
3338 if (expect->define_no_match)
3339 free(expect->define_no_match);
3340 if (expect->define_value)
3341 free(expect->define_value);
3342 }
3343
3344 for (i = 0; i < num_displayed; i ++)
3345 free(displayed[i]);
3346
3347 return (pass);
3348 }
3349
3350
3351 /*
3352 * 'expand_variables()' - Expand variables in a string.
3353 */
3354
3355 static void
3356 expand_variables(_cups_vars_t *vars, /* I - Variables */
3357 char *dst, /* I - Destination string buffer */
3358 const char *src, /* I - Source string */
3359 size_t dstsize) /* I - Size of destination buffer */
3360 {
3361 char *dstptr, /* Pointer into destination */
3362 *dstend, /* End of destination */
3363 temp[256], /* Temporary string */
3364 *tempptr; /* Pointer into temporary string */
3365 const char *value; /* Value to substitute */
3366
3367
3368 dstptr = dst;
3369 dstend = dst + dstsize - 1;
3370
3371 while (*src && dstptr < dstend)
3372 {
3373 if (*src == '$')
3374 {
3375 /*
3376 * Substitute a string/number...
3377 */
3378
3379 if (!strncmp(src, "$$", 2))
3380 {
3381 value = "$";
3382 src += 2;
3383 }
3384 else if (!strncmp(src, "$ENV[", 5))
3385 {
3386 strlcpy(temp, src + 5, sizeof(temp));
3387
3388 for (tempptr = temp; *tempptr; tempptr ++)
3389 if (*tempptr == ']')
3390 break;
3391
3392 if (*tempptr)
3393 *tempptr++ = '\0';
3394
3395 value = getenv(temp);
3396 src += tempptr - temp + 5;
3397 }
3398 else if (vars)
3399 {
3400 strlcpy(temp, src + 1, sizeof(temp));
3401
3402 for (tempptr = temp; *tempptr; tempptr ++)
3403 if (!isalnum(*tempptr & 255) && *tempptr != '-' && *tempptr != '_')
3404 break;
3405
3406 if (*tempptr)
3407 *tempptr = '\0';
3408
3409 if (!strcmp(temp, "uri"))
3410 value = vars->uri;
3411 else if (!strcmp(temp, "filename"))
3412 value = vars->filename;
3413 else if (!strcmp(temp, "scheme") || !strcmp(temp, "method"))
3414 value = vars->scheme;
3415 else if (!strcmp(temp, "username"))
3416 value = vars->userpass;
3417 else if (!strcmp(temp, "hostname"))
3418 value = vars->hostname;
3419 else if (!strcmp(temp, "port"))
3420 {
3421 snprintf(temp, sizeof(temp), "%d", vars->port);
3422 value = temp;
3423 }
3424 else if (!strcmp(temp, "resource"))
3425 value = vars->resource;
3426 else if (!strcmp(temp, "user"))
3427 value = cupsUser();
3428 else
3429 value = get_variable(vars, temp);
3430
3431 src += tempptr - temp + 1;
3432 }
3433 else
3434 {
3435 value = "$";
3436 src ++;
3437 }
3438
3439 if (value)
3440 {
3441 strlcpy(dstptr, value, (size_t)(dstend - dstptr + 1));
3442 dstptr += strlen(dstptr);
3443 }
3444 }
3445 else
3446 *dstptr++ = *src++;
3447 }
3448
3449 *dstptr = '\0';
3450 }
3451
3452
3453 /*
3454 * 'expect_matches()' - Return true if the tag matches the specification.
3455 */
3456
3457 static int /* O - 1 if matches, 0 otherwise */
3458 expect_matches(
3459 _cups_expect_t *expect, /* I - Expected attribute */
3460 ipp_tag_t value_tag) /* I - Value tag for attribute */
3461 {
3462 int match; /* Match? */
3463 char *of_type, /* Type name to match */
3464 *next, /* Next name to match */
3465 sep; /* Separator character */
3466
3467
3468 /*
3469 * If we don't expect a particular type, return immediately...
3470 */
3471
3472 if (!expect->of_type)
3473 return (1);
3474
3475 /*
3476 * Parse the "of_type" value since the string can contain multiple attribute
3477 * types separated by "," or "|"...
3478 */
3479
3480 for (of_type = expect->of_type, match = 0; !match && *of_type; of_type = next)
3481 {
3482 /*
3483 * Find the next separator, and set it (temporarily) to nul if present.
3484 */
3485
3486 for (next = of_type; *next && *next != '|' && *next != ','; next ++);
3487
3488 if ((sep = *next) != '\0')
3489 *next = '\0';
3490
3491 /*
3492 * Support some meta-types to make it easier to write the test file.
3493 */
3494
3495 if (!strcmp(of_type, "text"))
3496 match = value_tag == IPP_TAG_TEXTLANG || value_tag == IPP_TAG_TEXT;
3497 else if (!strcmp(of_type, "name"))
3498 match = value_tag == IPP_TAG_NAMELANG || value_tag == IPP_TAG_NAME;
3499 else if (!strcmp(of_type, "collection"))
3500 match = value_tag == IPP_TAG_BEGIN_COLLECTION;
3501 else
3502 match = value_tag == ippTagValue(of_type);
3503
3504 /*
3505 * Restore the separator if we have one...
3506 */
3507
3508 if (sep)
3509 *next++ = sep;
3510 }
3511
3512 return (match);
3513 }
3514
3515
3516 /*
3517 * 'get_collection()' - Get a collection value from the current test file.
3518 */
3519
3520 static ipp_t * /* O - Collection value */
3521 get_collection(FILE *outfile, /* I - Output file */
3522 _cups_vars_t *vars, /* I - Variables */
3523 FILE *fp, /* I - File to read from */
3524 int *linenum) /* IO - Line number */
3525 {
3526 char token[1024], /* Token from file */
3527 temp[1024], /* Temporary string */
3528 attr[128]; /* Attribute name */
3529 ipp_tag_t value; /* Current value type */
3530 ipp_t *col = ippNew(); /* Collection value */
3531 ipp_attribute_t *lastcol = NULL; /* Last collection attribute */
3532
3533
3534 while (get_token(fp, token, sizeof(token), linenum) != NULL)
3535 {
3536 if (!strcmp(token, "}"))
3537 break;
3538 else if (!strcmp(token, "{") && lastcol)
3539 {
3540 /*
3541 * Another collection value
3542 */
3543
3544 ipp_t *subcol = get_collection(outfile, vars, fp, linenum);
3545 /* Collection value */
3546
3547 if (subcol)
3548 ippSetCollection(col, &lastcol, ippGetCount(lastcol), subcol);
3549 else
3550 goto col_error;
3551 }
3552 else if (!_cups_strcasecmp(token, "MEMBER"))
3553 {
3554 /*
3555 * Attribute...
3556 */
3557
3558 lastcol = NULL;
3559
3560 if (!get_token(fp, token, sizeof(token), linenum))
3561 {
3562 print_fatal_error(outfile, "Missing MEMBER value tag on line %d.", *linenum);
3563 goto col_error;
3564 }
3565
3566 if ((value = ippTagValue(token)) == IPP_TAG_ZERO)
3567 {
3568 print_fatal_error(outfile, "Bad MEMBER value tag \"%s\" on line %d.", token,
3569 *linenum);
3570 goto col_error;
3571 }
3572
3573 if (!get_token(fp, attr, sizeof(attr), linenum))
3574 {
3575 print_fatal_error(outfile, "Missing MEMBER name on line %d.", *linenum);
3576 goto col_error;
3577 }
3578
3579 if (!get_token(fp, temp, sizeof(temp), linenum))
3580 {
3581 print_fatal_error(outfile, "Missing MEMBER value on line %d.", *linenum);
3582 goto col_error;
3583 }
3584
3585 expand_variables(vars, token, temp, sizeof(token));
3586
3587 switch (value)
3588 {
3589 case IPP_TAG_BOOLEAN :
3590 if (!_cups_strcasecmp(token, "true"))
3591 ippAddBoolean(col, IPP_TAG_ZERO, attr, 1);
3592 else
3593 ippAddBoolean(col, IPP_TAG_ZERO, attr, (char)atoi(token));
3594 break;
3595
3596 case IPP_TAG_INTEGER :
3597 case IPP_TAG_ENUM :
3598 ippAddInteger(col, IPP_TAG_ZERO, value, attr, atoi(token));
3599 break;
3600
3601 case IPP_TAG_RESOLUTION :
3602 {
3603 int xres, /* X resolution */
3604 yres; /* Y resolution */
3605 char units[6]; /* Units */
3606
3607 if (sscanf(token, "%dx%d%5s", &xres, &yres, units) != 3 ||
3608 (_cups_strcasecmp(units, "dpi") &&
3609 _cups_strcasecmp(units, "dpc") &&
3610 _cups_strcasecmp(units, "dpcm") &&
3611 _cups_strcasecmp(units, "other")))
3612 {
3613 print_fatal_error(outfile, "Bad resolution value \"%s\" on line %d.",
3614 token, *linenum);
3615 goto col_error;
3616 }
3617
3618 if (!_cups_strcasecmp(units, "dpi"))
3619 ippAddResolution(col, IPP_TAG_ZERO, attr, IPP_RES_PER_INCH, xres, yres);
3620 else if (!_cups_strcasecmp(units, "dpc") ||
3621 !_cups_strcasecmp(units, "dpcm"))
3622 ippAddResolution(col, IPP_TAG_ZERO, attr, IPP_RES_PER_CM, xres, yres);
3623 else
3624 ippAddResolution(col, IPP_TAG_ZERO, attr, (ipp_res_t)0, xres, yres);
3625 }
3626 break;
3627
3628 case IPP_TAG_RANGE :
3629 {
3630 int lowers[4], /* Lower value */
3631 uppers[4], /* Upper values */
3632 num_vals; /* Number of values */
3633
3634
3635 num_vals = sscanf(token, "%d-%d,%d-%d,%d-%d,%d-%d",
3636 lowers + 0, uppers + 0,
3637 lowers + 1, uppers + 1,
3638 lowers + 2, uppers + 2,
3639 lowers + 3, uppers + 3);
3640
3641 if ((num_vals & 1) || num_vals == 0)
3642 {
3643 print_fatal_error(outfile, "Bad rangeOfInteger value \"%s\" on line %d.",
3644 token, *linenum);
3645 goto col_error;
3646 }
3647
3648 ippAddRanges(col, IPP_TAG_ZERO, attr, num_vals / 2, lowers,
3649 uppers);
3650 }
3651 break;
3652
3653 case IPP_TAG_BEGIN_COLLECTION :
3654 if (!strcmp(token, "{"))
3655 {
3656 ipp_t *subcol = get_collection(outfile, vars, fp, linenum);
3657 /* Collection value */
3658
3659 if (subcol)
3660 {
3661 lastcol = ippAddCollection(col, IPP_TAG_ZERO, attr, subcol);
3662 ippDelete(subcol);
3663 }
3664 else
3665 goto col_error;
3666 }
3667 else
3668 {
3669 print_fatal_error(outfile, "Bad collection value on line %d.", *linenum);
3670 goto col_error;
3671 }
3672 break;
3673 case IPP_TAG_STRING :
3674 ippAddOctetString(col, IPP_TAG_ZERO, attr, token, (int)strlen(token));
3675 break;
3676
3677 default :
3678 if (!strchr(token, ','))
3679 ippAddString(col, IPP_TAG_ZERO, value, attr, NULL, token);
3680 else
3681 {
3682 /*
3683 * Multiple string values...
3684 */
3685
3686 int num_values; /* Number of values */
3687 char *values[100], /* Values */
3688 *ptr; /* Pointer to next value */
3689
3690
3691 values[0] = token;
3692 num_values = 1;
3693
3694 for (ptr = strchr(token, ','); ptr; ptr = strchr(ptr, ','))
3695 {
3696 *ptr++ = '\0';
3697 values[num_values] = ptr;
3698 num_values ++;
3699 }
3700
3701 ippAddStrings(col, IPP_TAG_ZERO, value, attr, num_values,
3702 NULL, (const char **)values);
3703 }
3704 break;
3705 }
3706 }
3707 }
3708
3709 return (col);
3710
3711 /*
3712 * If we get here there was a parse error; free memory and return.
3713 */
3714
3715 col_error:
3716
3717 ippDelete(col);
3718
3719 return (NULL);
3720 }
3721
3722
3723 /*
3724 * 'get_filename()' - Get a filename based on the current test file.
3725 */
3726
3727 static char * /* O - Filename */
3728 get_filename(const char *testfile, /* I - Current test file */
3729 char *dst, /* I - Destination filename */
3730 const char *src, /* I - Source filename */
3731 size_t dstsize) /* I - Size of destination buffer */
3732 {
3733 char *dstptr; /* Pointer into destination */
3734 _cups_globals_t *cg = _cupsGlobals();
3735 /* Global data */
3736
3737
3738 if (*src == '<' && src[strlen(src) - 1] == '>')
3739 {
3740 /*
3741 * Map <filename> to CUPS_DATADIR/ipptool/filename...
3742 */
3743
3744 snprintf(dst, dstsize, "%s/ipptool/%s", cg->cups_datadir, src + 1);
3745 dstptr = dst + strlen(dst) - 1;
3746 if (*dstptr == '>')
3747 *dstptr = '\0';
3748 }
3749 else if (*src == '/' || !strchr(testfile, '/')
3750 #ifdef WIN32
3751 || (isalpha(*src & 255) && src[1] == ':')
3752 #endif /* WIN32 */
3753 )
3754 {
3755 /*
3756 * Use the path as-is...
3757 */
3758
3759 strlcpy(dst, src, dstsize);
3760 }
3761 else
3762 {
3763 /*
3764 * Make path relative to testfile...
3765 */
3766
3767 strlcpy(dst, testfile, dstsize);
3768 if ((dstptr = strrchr(dst, '/')) != NULL)
3769 dstptr ++;
3770 else
3771 dstptr = dst; /* Should never happen */
3772
3773 strlcpy(dstptr, src, dstsize - (size_t)(dstptr - dst));
3774 }
3775
3776 return (dst);
3777 }
3778
3779
3780 /*
3781 * 'get_string()' - Get a pointer to a string value or the portion of interest.
3782 */
3783
3784 static char * /* O - Pointer to string */
3785 get_string(ipp_attribute_t *attr, /* I - IPP attribute */
3786 int element, /* I - Element to fetch */
3787 int flags, /* I - Value ("with") flags */
3788 char *buffer, /* I - Temporary buffer */
3789 size_t bufsize) /* I - Size of temporary buffer */
3790 {
3791 char *ptr, /* Value */
3792 scheme[256], /* URI scheme */
3793 userpass[256], /* Username/password */
3794 hostname[256], /* Hostname */
3795 resource[1024]; /* Resource */
3796 int port; /* Port number */
3797
3798
3799 ptr = attr->values[element].string.text;
3800
3801 if (flags & _CUPS_WITH_HOSTNAME)
3802 {
3803 if (httpSeparateURI(HTTP_URI_CODING_ALL, ptr, scheme, sizeof(scheme), userpass, sizeof(userpass), buffer, (int)bufsize, &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
3804 buffer[0] = '\0';
3805
3806 return (buffer);
3807 }
3808 else if (flags & _CUPS_WITH_RESOURCE)
3809 {
3810 if (httpSeparateURI(HTTP_URI_CODING_ALL, ptr, scheme, sizeof(scheme), userpass, sizeof(userpass), hostname, sizeof(hostname), &port, buffer, (int)bufsize) < HTTP_URI_STATUS_OK)
3811 buffer[0] = '\0';
3812
3813 return (buffer);
3814 }
3815 else if (flags & _CUPS_WITH_SCHEME)
3816 {
3817 if (httpSeparateURI(HTTP_URI_CODING_ALL, ptr, buffer, (int)bufsize, userpass, sizeof(userpass), hostname, sizeof(hostname), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
3818 buffer[0] = '\0';
3819
3820 return (buffer);
3821 }
3822 else
3823 return (ptr);
3824 }
3825
3826
3827 /*
3828 * 'get_token()' - Get a token from a file.
3829 */
3830
3831 static char * /* O - Token from file or NULL on EOF */
3832 get_token(FILE *fp, /* I - File to read from */
3833 char *buf, /* I - Buffer to read into */
3834 int buflen, /* I - Length of buffer */
3835 int *linenum) /* IO - Current line number */
3836 {
3837 int ch, /* Character from file */
3838 quote; /* Quoting character */
3839 char *bufptr, /* Pointer into buffer */
3840 *bufend; /* End of buffer */
3841
3842
3843 for (;;)
3844 {
3845 /*
3846 * Skip whitespace...
3847 */
3848
3849 while (isspace(ch = getc(fp)))
3850 {
3851 if (ch == '\n')
3852 (*linenum) ++;
3853 }
3854
3855 /*
3856 * Read a token...
3857 */
3858
3859 if (ch == EOF)
3860 return (NULL);
3861 else if (ch == '\'' || ch == '\"')
3862 {
3863 /*
3864 * Quoted text or regular expression...
3865 */
3866
3867 quote = ch;
3868 bufptr = buf;
3869 bufend = buf + buflen - 1;
3870
3871 while ((ch = getc(fp)) != EOF)
3872 {
3873 if (ch == '\\')
3874 {
3875 /*
3876 * Escape next character...
3877 */
3878
3879 if (bufptr < bufend)
3880 *bufptr++ = (char)ch;
3881
3882 if ((ch = getc(fp)) != EOF && bufptr < bufend)
3883 *bufptr++ = (char)ch;
3884 }
3885 else if (ch == quote)
3886 break;
3887 else if (bufptr < bufend)
3888 *bufptr++ = (char)ch;
3889 }
3890
3891 *bufptr = '\0';
3892
3893 return (buf);
3894 }
3895 else if (ch == '#')
3896 {
3897 /*
3898 * Comment...
3899 */
3900
3901 while ((ch = getc(fp)) != EOF)
3902 if (ch == '\n')
3903 break;
3904
3905 (*linenum) ++;
3906 }
3907 else if (ch == '{' || ch == '}' || ch == ',')
3908 {
3909 buf[0] = (char)ch;
3910 buf[1] = '\0';
3911
3912 return (buf);
3913 }
3914 else
3915 {
3916 /*
3917 * Whitespace delimited text...
3918 */
3919
3920 ungetc(ch, fp);
3921
3922 bufptr = buf;
3923 bufend = buf + buflen - 1;
3924
3925 while ((ch = getc(fp)) != EOF)
3926 if (isspace(ch) || ch == '#')
3927 break;
3928 else if (bufptr < bufend)
3929 *bufptr++ = (char)ch;
3930
3931 if (ch == '#')
3932 ungetc(ch, fp);
3933 else if (ch == '\n')
3934 (*linenum) ++;
3935
3936 *bufptr = '\0';
3937
3938 return (buf);
3939 }
3940 }
3941 }
3942
3943
3944 /*
3945 * 'get_variable()' - Get the value of a variable.
3946 */
3947
3948 static char * /* O - Value or NULL */
3949 get_variable(_cups_vars_t *vars, /* I - Variables */
3950 const char *name) /* I - Variable name */
3951 {
3952 _cups_var_t key, /* Search key */
3953 *match; /* Matching variable, if any */
3954
3955
3956 key.name = (char *)name;
3957 match = cupsArrayFind(vars->vars, &key);
3958
3959 return (match ? match->value : NULL);
3960 }
3961
3962
3963 /*
3964 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
3965 * value.
3966 */
3967
3968 static char * /* O - ISO 8601 date/time string */
3969 iso_date(ipp_uchar_t *date) /* I - IPP (RFC 1903) date/time value */
3970 {
3971 time_t utctime; /* UTC time since 1970 */
3972 struct tm *utcdate; /* UTC date/time */
3973 static char buffer[255]; /* String buffer */
3974
3975
3976 utctime = ippDateToTime(date);
3977 utcdate = gmtime(&utctime);
3978
3979 snprintf(buffer, sizeof(buffer), "%04d-%02d-%02dT%02d:%02d:%02dZ",
3980 utcdate->tm_year + 1900, utcdate->tm_mon + 1, utcdate->tm_mday,
3981 utcdate->tm_hour, utcdate->tm_min, utcdate->tm_sec);
3982
3983 return (buffer);
3984 }
3985
3986
3987 /*
3988 * 'password_cb()' - Password callback for authenticated tests.
3989 */
3990
3991 static const char * /* O - Password */
3992 password_cb(const char *prompt) /* I - Prompt (unused) */
3993 {
3994 (void)prompt;
3995
3996 if (PasswordTries < 3)
3997 {
3998 PasswordTries ++;
3999
4000 cupsSetUser(Username);
4001
4002 return (Password);
4003 }
4004 else
4005 return (NULL);
4006 }
4007
4008
4009 /*
4010 * 'pause_message()' - Display the message and pause until the user presses a key.
4011 */
4012
4013 static void
4014 pause_message(const char *message) /* I - Message */
4015 {
4016 #ifdef WIN32
4017 HANDLE tty; /* Console handle */
4018 DWORD mode; /* Console mode */
4019 char key; /* Key press */
4020 DWORD bytes; /* Bytes read for key press */
4021
4022
4023 /*
4024 * Disable input echo and set raw input...
4025 */
4026
4027 if ((tty = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE)
4028 return;
4029
4030 if (!GetConsoleMode(tty, &mode))
4031 return;
4032
4033 if (!SetConsoleMode(tty, 0))
4034 return;
4035
4036 #else
4037 int tty; /* /dev/tty - never read from stdin */
4038 struct termios original, /* Original input mode */
4039 noecho; /* No echo input mode */
4040 char key; /* Current key press */
4041
4042
4043 /*
4044 * Disable input echo and set raw input...
4045 */
4046
4047 if ((tty = open("/dev/tty", O_RDONLY)) < 0)
4048 return;
4049
4050 if (tcgetattr(tty, &original))
4051 {
4052 close(tty);
4053 return;
4054 }
4055
4056 noecho = original;
4057 noecho.c_lflag &= (tcflag_t)~(ICANON | ECHO | ECHOE | ISIG);
4058
4059 if (tcsetattr(tty, TCSAFLUSH, &noecho))
4060 {
4061 close(tty);
4062 return;
4063 }
4064 #endif /* WIN32 */
4065
4066 /*
4067 * Display the prompt...
4068 */
4069
4070 printf("%s\n---- PRESS ANY KEY ----", message);
4071 fflush(stdout);
4072
4073 #ifdef WIN32
4074 /*
4075 * Read a key...
4076 */
4077
4078 ReadFile(tty, &key, 1, &bytes, NULL);
4079
4080 /*
4081 * Cleanup...
4082 */
4083
4084 SetConsoleMode(tty, mode);
4085
4086 #else
4087 /*
4088 * Read a key...
4089 */
4090
4091 read(tty, &key, 1);
4092
4093 /*
4094 * Cleanup...
4095 */
4096
4097 tcsetattr(tty, TCSAFLUSH, &original);
4098 close(tty);
4099 #endif /* WIN32 */
4100
4101 /*
4102 * Erase the "press any key" prompt...
4103 */
4104
4105 fputs("\r \r", stdout);
4106 fflush(stdout);
4107 }
4108
4109
4110 /*
4111 * 'print_attr()' - Print an attribute on the screen.
4112 */
4113
4114 static void
4115 print_attr(FILE *outfile, /* I - Output file */
4116 int format, /* I - Output format */
4117 ipp_attribute_t *attr, /* I - Attribute to print */
4118 ipp_tag_t *group) /* IO - Current group */
4119 {
4120 int i; /* Looping var */
4121 ipp_attribute_t *colattr; /* Collection attribute */
4122
4123
4124 if (format == _CUPS_OUTPUT_PLIST)
4125 {
4126 if (!attr->name || (group && *group != attr->group_tag))
4127 {
4128 if (attr->group_tag != IPP_TAG_ZERO)
4129 {
4130 fputs("</dict>\n", outfile);
4131 fputs("<dict>\n", outfile);
4132 }
4133
4134 if (group)
4135 *group = attr->group_tag;
4136 }
4137
4138 if (!attr->name)
4139 return;
4140
4141 print_xml_string(outfile, "key", attr->name);
4142 if (attr->num_values > 1)
4143 fputs("<array>\n", outfile);
4144
4145 switch (attr->value_tag)
4146 {
4147 case IPP_TAG_INTEGER :
4148 case IPP_TAG_ENUM :
4149 for (i = 0; i < attr->num_values; i ++)
4150 fprintf(outfile, "<integer>%d</integer>\n", attr->values[i].integer);
4151 break;
4152
4153 case IPP_TAG_BOOLEAN :
4154 for (i = 0; i < attr->num_values; i ++)
4155 fputs(attr->values[i].boolean ? "<true />\n" : "<false />\n", outfile);
4156 break;
4157
4158 case IPP_TAG_RANGE :
4159 for (i = 0; i < attr->num_values; i ++)
4160 fprintf(outfile, "<dict><key>lower</key><integer>%d</integer>"
4161 "<key>upper</key><integer>%d</integer></dict>\n",
4162 attr->values[i].range.lower, attr->values[i].range.upper);
4163 break;
4164
4165 case IPP_TAG_RESOLUTION :
4166 for (i = 0; i < attr->num_values; i ++)
4167 fprintf(outfile, "<dict><key>xres</key><integer>%d</integer>"
4168 "<key>yres</key><integer>%d</integer>"
4169 "<key>units</key><string>%s</string></dict>\n",
4170 attr->values[i].resolution.xres,
4171 attr->values[i].resolution.yres,
4172 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
4173 "dpi" : "dpcm");
4174 break;
4175
4176 case IPP_TAG_DATE :
4177 for (i = 0; i < attr->num_values; i ++)
4178 fprintf(outfile, "<date>%s</date>\n", iso_date(attr->values[i].date));
4179 break;
4180
4181 case IPP_TAG_STRING :
4182 for (i = 0; i < attr->num_values; i ++)
4183 {
4184 char buffer[IPP_MAX_LENGTH * 5 / 4 + 1];
4185 /* Output buffer */
4186
4187 fprintf(outfile, "<data>%s</data>\n",
4188 httpEncode64_2(buffer, sizeof(buffer),
4189 attr->values[i].unknown.data,
4190 attr->values[i].unknown.length));
4191 }
4192 break;
4193
4194 case IPP_TAG_TEXT :
4195 case IPP_TAG_NAME :
4196 case IPP_TAG_KEYWORD :
4197 case IPP_TAG_CHARSET :
4198 case IPP_TAG_URI :
4199 case IPP_TAG_MIMETYPE :
4200 case IPP_TAG_LANGUAGE :
4201 for (i = 0; i < attr->num_values; i ++)
4202 print_xml_string(outfile, "string", attr->values[i].string.text);
4203 break;
4204
4205 case IPP_TAG_TEXTLANG :
4206 case IPP_TAG_NAMELANG :
4207 for (i = 0; i < attr->num_values; i ++)
4208 {
4209 fputs("<dict><key>language</key><string>", outfile);
4210 print_xml_string(outfile, NULL, attr->values[i].string.language);
4211 fputs("</string><key>string</key><string>", outfile);
4212 print_xml_string(outfile, NULL, attr->values[i].string.text);
4213 fputs("</string></dict>\n", outfile);
4214 }
4215 break;
4216
4217 case IPP_TAG_BEGIN_COLLECTION :
4218 for (i = 0; i < attr->num_values; i ++)
4219 {
4220 fputs("<dict>\n", outfile);
4221 for (colattr = attr->values[i].collection->attrs;
4222 colattr;
4223 colattr = colattr->next)
4224 print_attr(outfile, format, colattr, NULL);
4225 fputs("</dict>\n", outfile);
4226 }
4227 break;
4228
4229 default :
4230 fprintf(outfile, "<string>&lt;&lt;%s&gt;&gt;</string>\n", ippTagString(attr->value_tag));
4231 break;
4232 }
4233
4234 if (attr->num_values > 1)
4235 fputs("</array>\n", outfile);
4236 }
4237 else
4238 {
4239 char buffer[8192]; /* Value buffer */
4240
4241 if (format == _CUPS_OUTPUT_TEST)
4242 {
4243 if (!attr->name)
4244 {
4245 fputs(" -- separator --\n", outfile);
4246 return;
4247 }
4248
4249 fprintf(outfile, " %s (%s%s) = ", attr->name, attr->num_values > 1 ? "1setOf " : "", ippTagString(attr->value_tag));
4250 }
4251
4252 ippAttributeString(attr, buffer, sizeof(buffer));
4253 fprintf(outfile, "%s\n", buffer);
4254 }
4255 }
4256
4257
4258 /*
4259 * 'print_csv()' - Print a line of CSV text.
4260 */
4261
4262 static void
4263 print_csv(
4264 FILE *outfile, /* I - Output file */
4265 ipp_attribute_t *attr, /* I - First attribute for line */
4266 int num_displayed, /* I - Number of attributes to display */
4267 char **displayed, /* I - Attributes to display */
4268 size_t *widths) /* I - Column widths */
4269 {
4270 int i; /* Looping var */
4271 size_t maxlength; /* Max length of all columns */
4272 char *buffer, /* String buffer */
4273 *bufptr; /* Pointer into buffer */
4274 ipp_attribute_t *current; /* Current attribute */
4275
4276
4277 /*
4278 * Get the maximum string length we have to show and allocate...
4279 */
4280
4281 for (i = 1, maxlength = widths[0]; i < num_displayed; i ++)
4282 if (widths[i] > maxlength)
4283 maxlength = widths[i];
4284
4285 maxlength += 2;
4286
4287 if ((buffer = malloc(maxlength)) == NULL)
4288 return;
4289
4290 /*
4291 * Loop through the attributes to display...
4292 */
4293
4294 if (attr)
4295 {
4296 for (i = 0; i < num_displayed; i ++)
4297 {
4298 if (i)
4299 fputc(',', outfile);
4300
4301 buffer[0] = '\0';
4302
4303 for (current = attr; current; current = current->next)
4304 {
4305 if (!current->name)
4306 break;
4307 else if (!strcmp(current->name, displayed[i]))
4308 {
4309 ippAttributeString(current, buffer, maxlength);
4310 break;
4311 }
4312 }
4313
4314 if (strchr(buffer, ',') != NULL || strchr(buffer, '\"') != NULL ||
4315 strchr(buffer, '\\') != NULL)
4316 {
4317 putc('\"', outfile);
4318 for (bufptr = buffer; *bufptr; bufptr ++)
4319 {
4320 if (*bufptr == '\\' || *bufptr == '\"')
4321 putc('\\', outfile);
4322 putc(*bufptr, outfile);
4323 }
4324 putc('\"', outfile);
4325 }
4326 else
4327 fputs(buffer, outfile);
4328 }
4329 putc('\n', outfile);
4330 }
4331 else
4332 {
4333 for (i = 0; i < num_displayed; i ++)
4334 {
4335 if (i)
4336 putc(',', outfile);
4337
4338 fputs(displayed[i], outfile);
4339 }
4340 putc('\n', outfile);
4341 }
4342
4343 free(buffer);
4344 }
4345
4346
4347 /*
4348 * 'print_fatal_error()' - Print a fatal error message.
4349 */
4350
4351 static void
4352 print_fatal_error(FILE *outfile, /* I - Output file */
4353 const char *s, /* I - Printf-style format string */
4354 ...) /* I - Additional arguments as needed */
4355 {
4356 char buffer[10240]; /* Format buffer */
4357 va_list ap; /* Pointer to arguments */
4358
4359
4360 /*
4361 * Format the error message...
4362 */
4363
4364 va_start(ap, s);
4365 vsnprintf(buffer, sizeof(buffer), s, ap);
4366 va_end(ap);
4367
4368 /*
4369 * Then output it...
4370 */
4371
4372 if (Output == _CUPS_OUTPUT_PLIST)
4373 {
4374 print_xml_header(outfile);
4375 print_xml_trailer(outfile, 0, buffer);
4376 }
4377
4378 _cupsLangPrintf(stderr, "ipptool: %s", buffer);
4379 }
4380
4381
4382 /*
4383 * 'print_line()' - Print a line of formatted or CSV text.
4384 */
4385
4386 static void
4387 print_line(
4388 FILE *outfile, /* I - Output file */
4389 ipp_attribute_t *attr, /* I - First attribute for line */
4390 int num_displayed, /* I - Number of attributes to display */
4391 char **displayed, /* I - Attributes to display */
4392 size_t *widths) /* I - Column widths */
4393 {
4394 int i; /* Looping var */
4395 size_t maxlength; /* Max length of all columns */
4396 char *buffer; /* String buffer */
4397 ipp_attribute_t *current; /* Current attribute */
4398
4399
4400 /*
4401 * Get the maximum string length we have to show and allocate...
4402 */
4403
4404 for (i = 1, maxlength = widths[0]; i < num_displayed; i ++)
4405 if (widths[i] > maxlength)
4406 maxlength = widths[i];
4407
4408 maxlength += 2;
4409
4410 if ((buffer = malloc(maxlength)) == NULL)
4411 return;
4412
4413 /*
4414 * Loop through the attributes to display...
4415 */
4416
4417 if (attr)
4418 {
4419 for (i = 0; i < num_displayed; i ++)
4420 {
4421 if (i)
4422 putc(' ', outfile);
4423
4424 buffer[0] = '\0';
4425
4426 for (current = attr; current; current = current->next)
4427 {
4428 if (!current->name)
4429 break;
4430 else if (!strcmp(current->name, displayed[i]))
4431 {
4432 ippAttributeString(current, buffer, maxlength);
4433 break;
4434 }
4435 }
4436
4437 fprintf(outfile, "%*s", (int)-widths[i], buffer);
4438 }
4439 putc('\n', outfile);
4440 }
4441 else
4442 {
4443 for (i = 0; i < num_displayed; i ++)
4444 {
4445 if (i)
4446 putc(' ', outfile);
4447
4448 fprintf(outfile, "%*s", (int)-widths[i], displayed[i]);
4449 }
4450 putc('\n', outfile);
4451
4452 for (i = 0; i < num_displayed; i ++)
4453 {
4454 if (i)
4455 putc(' ', outfile);
4456
4457 memset(buffer, '-', widths[i]);
4458 buffer[widths[i]] = '\0';
4459 fputs(buffer, outfile);
4460 }
4461 putc('\n', outfile);
4462 }
4463
4464 free(buffer);
4465 }
4466
4467
4468 /*
4469 * 'print_xml_header()' - Print a standard XML plist header.
4470 */
4471
4472 static void
4473 print_xml_header(FILE *outfile) /* I - Output file */
4474 {
4475 if (!XMLHeader)
4476 {
4477 fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", outfile);
4478 fputs("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
4479 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n", outfile);
4480 fputs("<plist version=\"1.0\">\n", outfile);
4481 fputs("<dict>\n", outfile);
4482 fputs("<key>ipptoolVersion</key>\n", outfile);
4483 fputs("<string>" CUPS_SVERSION "</string>\n", outfile);
4484 fputs("<key>Transfer</key>\n", outfile);
4485 fprintf(outfile, "<string>%s</string>\n",
4486 Transfer == _CUPS_TRANSFER_AUTO ? "auto" :
4487 Transfer == _CUPS_TRANSFER_CHUNKED ? "chunked" : "length");
4488 fputs("<key>Tests</key>\n", outfile);
4489 fputs("<array>\n", outfile);
4490
4491 XMLHeader = 1;
4492 }
4493 }
4494
4495
4496 /*
4497 * 'print_xml_string()' - Print an XML string with escaping.
4498 */
4499
4500 static void
4501 print_xml_string(FILE *outfile, /* I - Output file */
4502 const char *element, /* I - Element name or NULL */
4503 const char *s) /* I - String to print */
4504 {
4505 if (element)
4506 fprintf(outfile, "<%s>", element);
4507
4508 while (*s)
4509 {
4510 if (*s == '&')
4511 fputs("&amp;", outfile);
4512 else if (*s == '<')
4513 fputs("&lt;", outfile);
4514 else if (*s == '>')
4515 fputs("&gt;", outfile);
4516 else if ((*s & 0xe0) == 0xc0)
4517 {
4518 /*
4519 * Validate UTF-8 two-byte sequence...
4520 */
4521
4522 if ((s[1] & 0xc0) != 0x80)
4523 {
4524 putc('?', outfile);
4525 s ++;
4526 }
4527 else
4528 {
4529 putc(*s++, outfile);
4530 putc(*s, outfile);
4531 }
4532 }
4533 else if ((*s & 0xf0) == 0xe0)
4534 {
4535 /*
4536 * Validate UTF-8 three-byte sequence...
4537 */
4538
4539 if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80)
4540 {
4541 putc('?', outfile);
4542 s += 2;
4543 }
4544 else
4545 {
4546 putc(*s++, outfile);
4547 putc(*s++, outfile);
4548 putc(*s, outfile);
4549 }
4550 }
4551 else if ((*s & 0xf8) == 0xf0)
4552 {
4553 /*
4554 * Validate UTF-8 four-byte sequence...
4555 */
4556
4557 if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 ||
4558 (s[3] & 0xc0) != 0x80)
4559 {
4560 putc('?', outfile);
4561 s += 3;
4562 }
4563 else
4564 {
4565 putc(*s++, outfile);
4566 putc(*s++, outfile);
4567 putc(*s++, outfile);
4568 putc(*s, outfile);
4569 }
4570 }
4571 else if ((*s & 0x80) || (*s < ' ' && !isspace(*s & 255)))
4572 {
4573 /*
4574 * Invalid control character...
4575 */
4576
4577 putc('?', outfile);
4578 }
4579 else
4580 putc(*s, outfile);
4581
4582 s ++;
4583 }
4584
4585 if (element)
4586 fprintf(outfile, "</%s>\n", element);
4587 }
4588
4589
4590 /*
4591 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
4592 */
4593
4594 static void
4595 print_xml_trailer(FILE *outfile, /* I - Output file */
4596 int success, /* I - 1 on success, 0 on failure */
4597 const char *message) /* I - Error message or NULL */
4598 {
4599 if (XMLHeader)
4600 {
4601 fputs("</array>\n", outfile);
4602 fputs("<key>Successful</key>\n", outfile);
4603 fputs(success ? "<true />\n" : "<false />\n", outfile);
4604 if (message)
4605 {
4606 fputs("<key>ErrorMessage</key>\n", outfile);
4607 print_xml_string(outfile, "string", message);
4608 }
4609 fputs("</dict>\n", outfile);
4610 fputs("</plist>\n", outfile);
4611
4612 XMLHeader = 0;
4613 }
4614 }
4615
4616
4617 /*
4618 * 'set_variable()' - Set a variable value.
4619 */
4620
4621 static void
4622 set_variable(FILE *outfile, /* I - Output file */
4623 _cups_vars_t *vars, /* I - Variables */
4624 const char *name, /* I - Variable name */
4625 const char *value) /* I - Value string */
4626 {
4627 _cups_var_t key, /* Search key */
4628 *var; /* New variable */
4629
4630
4631 if (!_cups_strcasecmp(name, "filename"))
4632 {
4633 if (vars->filename)
4634 free(vars->filename);
4635
4636 vars->filename = strdup(value);
4637 return;
4638 }
4639
4640 key.name = (char *)name;
4641 if ((var = cupsArrayFind(vars->vars, &key)) != NULL)
4642 {
4643 free(var->value);
4644 var->value = strdup(value);
4645 }
4646 else if ((var = malloc(sizeof(_cups_var_t))) == NULL)
4647 {
4648 print_fatal_error(outfile, "Unable to allocate memory for variable \"%s\".", name);
4649 exit(1);
4650 }
4651 else
4652 {
4653 var->name = strdup(name);
4654 var->value = strdup(value);
4655
4656 cupsArrayAdd(vars->vars, var);
4657 }
4658 }
4659
4660
4661 #ifndef WIN32
4662 /*
4663 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
4664 */
4665
4666 static void
4667 sigterm_handler(int sig) /* I - Signal number (unused) */
4668 {
4669 (void)sig;
4670
4671 Cancel = 1;
4672
4673 signal(SIGINT, SIG_DFL);
4674 signal(SIGTERM, SIG_DFL);
4675 }
4676 #endif /* !WIN32 */
4677
4678
4679 /*
4680 * 'timeout_cb()' - Handle HTTP timeouts.
4681 */
4682
4683 static int /* O - 1 to continue, 0 to cancel */
4684 timeout_cb(http_t *http, /* I - Connection to server */
4685 void *user_data) /* I - User data (unused) */
4686 {
4687 int buffered = 0; /* Bytes buffered but not yet sent */
4688
4689
4690 (void)user_data;
4691
4692 /*
4693 * If the socket still have data waiting to be sent to the printer (as can
4694 * happen if the printer runs out of paper), continue to wait until the output
4695 * buffer is empty...
4696 */
4697
4698 #ifdef SO_NWRITE /* OS X and some versions of Linux */
4699 socklen_t len = sizeof(buffered); /* Size of return value */
4700
4701 if (getsockopt(httpGetFd(http), SOL_SOCKET, SO_NWRITE, &buffered, &len))
4702 buffered = 0;
4703
4704 #elif defined(SIOCOUTQ) /* Others except Windows */
4705 if (ioctl(httpGetFd(http), SIOCOUTQ, &buffered))
4706 buffered = 0;
4707
4708 #else /* Windows (not possible) */
4709 (void)http;
4710 #endif /* SO_NWRITE */
4711
4712 return (buffered > 0);
4713 }
4714
4715
4716 /*
4717 * 'usage()' - Show program usage.
4718 */
4719
4720 static void
4721 usage(void)
4722 {
4723 _cupsLangPuts(stderr, _("Usage: ipptool [options] URI filename [ ... "
4724 "filenameN ]"));
4725 _cupsLangPuts(stderr, _("Options:"));
4726 _cupsLangPuts(stderr, _(" -4 Connect using IPv4."));
4727 _cupsLangPuts(stderr, _(" -6 Connect using IPv6."));
4728 _cupsLangPuts(stderr, _(" -C Send requests using "
4729 "chunking (default)."));
4730 _cupsLangPuts(stdout, _(" -E Test with HTTP Upgrade to "
4731 "TLS."));
4732 _cupsLangPuts(stderr, _(" -I Ignore errors."));
4733 _cupsLangPuts(stderr, _(" -L Send requests using "
4734 "content-length."));
4735 _cupsLangPuts(stderr, _(" -S Test with SSL "
4736 "encryption."));
4737 _cupsLangPuts(stderr, _(" -T seconds Set the receive/send "
4738 "timeout in seconds."));
4739 _cupsLangPuts(stderr, _(" -V version Set default IPP "
4740 "version."));
4741 _cupsLangPuts(stderr, _(" -X Produce XML plist instead "
4742 "of plain text."));
4743 _cupsLangPuts(stderr, _(" -d name=value Set named variable to "
4744 "value."));
4745 _cupsLangPuts(stderr, _(" -f filename Set default request "
4746 "filename."));
4747 _cupsLangPuts(stderr, _(" -i seconds Repeat the last file with "
4748 "the given time interval."));
4749 _cupsLangPuts(stderr, _(" -n count Repeat the last file the "
4750 "given number of times."));
4751 _cupsLangPuts(stderr, _(" -q Run silently."));
4752 _cupsLangPuts(stderr, _(" -t Produce a test report."));
4753 _cupsLangPuts(stderr, _(" -v Be verbose."));
4754
4755 exit(1);
4756 }
4757
4758
4759 /*
4760 * 'validate_attr()' - Determine whether an attribute is valid.
4761 */
4762
4763 static int /* O - 1 if valid, 0 otherwise */
4764 validate_attr(FILE *outfile, /* I - Output file */
4765 cups_array_t *errors, /* I - Errors array */
4766 ipp_attribute_t *attr) /* I - Attribute to validate */
4767 {
4768 int i; /* Looping var */
4769 char scheme[64], /* Scheme from URI */
4770 userpass[256], /* Username/password from URI */
4771 hostname[256], /* Hostname from URI */
4772 resource[1024]; /* Resource from URI */
4773 int port, /* Port number from URI */
4774 uri_status, /* URI separation status */
4775 valid = 1; /* Is the attribute valid? */
4776 const char *ptr; /* Pointer into string */
4777 ipp_attribute_t *colattr; /* Collection attribute */
4778 regex_t re; /* Regular expression */
4779 ipp_uchar_t *date; /* Current date value */
4780
4781
4782 /*
4783 * Skip separators.
4784 */
4785
4786 if (!attr->name)
4787 return (1);
4788
4789 /*
4790 * Validate the attribute name.
4791 */
4792
4793 for (ptr = attr->name; *ptr; ptr ++)
4794 if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' && *ptr != '_')
4795 break;
4796
4797 if (*ptr || ptr == attr->name)
4798 {
4799 valid = 0;
4800
4801 add_stringf(errors,
4802 "\"%s\": Bad attribute name - invalid character "
4803 "(RFC 2911 section 4.1.3).", attr->name);
4804 }
4805
4806 if ((ptr - attr->name) > 255)
4807 {
4808 valid = 0;
4809
4810 add_stringf(errors,
4811 "\"%s\": Bad attribute name - bad length "
4812 "(RFC 2911 section 4.1.3).", attr->name);
4813 }
4814
4815 switch (attr->value_tag)
4816 {
4817 case IPP_TAG_INTEGER :
4818 break;
4819
4820 case IPP_TAG_BOOLEAN :
4821 for (i = 0; i < attr->num_values; i ++)
4822 {
4823 if (attr->values[i].boolean != 0 &&
4824 attr->values[i].boolean != 1)
4825 {
4826 valid = 0;
4827
4828 add_stringf(errors,
4829 "\"%s\": Bad boolen value %d "
4830 "(RFC 2911 section 4.1.11).", attr->name,
4831 attr->values[i].boolean);
4832 }
4833 }
4834 break;
4835
4836 case IPP_TAG_ENUM :
4837 for (i = 0; i < attr->num_values; i ++)
4838 {
4839 if (attr->values[i].integer < 1)
4840 {
4841 valid = 0;
4842
4843 add_stringf(errors,
4844 "\"%s\": Bad enum value %d - out of range "
4845 "(RFC 2911 section 4.1.4).", attr->name,
4846 attr->values[i].integer);
4847 }
4848 }
4849 break;
4850
4851 case IPP_TAG_STRING :
4852 for (i = 0; i < attr->num_values; i ++)
4853 {
4854 if (attr->values[i].unknown.length > IPP_MAX_OCTETSTRING)
4855 {
4856 valid = 0;
4857
4858 add_stringf(errors,
4859 "\"%s\": Bad octetString value - bad length %d "
4860 "(RFC 2911 section 4.1.10).", attr->name,
4861 attr->values[i].unknown.length);
4862 }
4863 }
4864 break;
4865
4866 case IPP_TAG_DATE :
4867 for (i = 0; i < attr->num_values; i ++)
4868 {
4869 date = attr->values[i].date;
4870
4871 if (date[2] < 1 || date[2] > 12)
4872 {
4873 valid = 0;
4874
4875 add_stringf(errors,
4876 "\"%s\": Bad dateTime month %u "
4877 "(RFC 2911 section 4.1.14).", attr->name, date[2]);
4878 }
4879
4880 if (date[3] < 1 || date[3] > 31)
4881 {
4882 valid = 0;
4883
4884 add_stringf(errors,
4885 "\"%s\": Bad dateTime day %u "
4886 "(RFC 2911 section 4.1.14).", attr->name, date[3]);
4887 }
4888
4889 if (date[4] > 23)
4890 {
4891 valid = 0;
4892
4893 add_stringf(errors,
4894 "\"%s\": Bad dateTime hours %u "
4895 "(RFC 2911 section 4.1.14).", attr->name, date[4]);
4896 }
4897
4898 if (date[5] > 59)
4899 {
4900 valid = 0;
4901
4902 add_stringf(errors,
4903 "\"%s\": Bad dateTime minutes %u "
4904 "(RFC 2911 section 4.1.14).", attr->name, date[5]);
4905 }
4906
4907 if (date[6] > 60)
4908 {
4909 valid = 0;
4910
4911 add_stringf(errors,
4912 "\"%s\": Bad dateTime seconds %u "
4913 "(RFC 2911 section 4.1.14).", attr->name, date[6]);
4914 }
4915
4916 if (date[7] > 9)
4917 {
4918 valid = 0;
4919
4920 add_stringf(errors,
4921 "\"%s\": Bad dateTime deciseconds %u "
4922 "(RFC 2911 section 4.1.14).", attr->name, date[7]);
4923 }
4924
4925 if (date[8] != '-' && date[8] != '+')
4926 {
4927 valid = 0;
4928
4929 add_stringf(errors,
4930 "\"%s\": Bad dateTime UTC sign '%c' "
4931 "(RFC 2911 section 4.1.14).", attr->name, date[8]);
4932 }
4933
4934 if (date[9] > 11)
4935 {
4936 valid = 0;
4937
4938 add_stringf(errors,
4939 "\"%s\": Bad dateTime UTC hours %u "
4940 "(RFC 2911 section 4.1.14).", attr->name, date[9]);
4941 }
4942
4943 if (date[10] > 59)
4944 {
4945 valid = 0;
4946
4947 add_stringf(errors,
4948 "\"%s\": Bad dateTime UTC minutes %u "
4949 "(RFC 2911 section 4.1.14).", attr->name, date[10]);
4950 }
4951 }
4952 break;
4953
4954 case IPP_TAG_RESOLUTION :
4955 for (i = 0; i < attr->num_values; i ++)
4956 {
4957 if (attr->values[i].resolution.xres <= 0)
4958 {
4959 valid = 0;
4960
4961 add_stringf(errors,
4962 "\"%s\": Bad resolution value %dx%d%s - cross "
4963 "feed resolution must be positive "
4964 "(RFC 2911 section 4.1.15).", attr->name,
4965 attr->values[i].resolution.xres,
4966 attr->values[i].resolution.yres,
4967 attr->values[i].resolution.units ==
4968 IPP_RES_PER_INCH ? "dpi" :
4969 attr->values[i].resolution.units ==
4970 IPP_RES_PER_CM ? "dpcm" : "unknown");
4971 }
4972
4973 if (attr->values[i].resolution.yres <= 0)
4974 {
4975 valid = 0;
4976
4977 add_stringf(errors,
4978 "\"%s\": Bad resolution value %dx%d%s - feed "
4979 "resolution must be positive "
4980 "(RFC 2911 section 4.1.15).", attr->name,
4981 attr->values[i].resolution.xres,
4982 attr->values[i].resolution.yres,
4983 attr->values[i].resolution.units ==
4984 IPP_RES_PER_INCH ? "dpi" :
4985 attr->values[i].resolution.units ==
4986 IPP_RES_PER_CM ? "dpcm" : "unknown");
4987 }
4988
4989 if (attr->values[i].resolution.units != IPP_RES_PER_INCH &&
4990 attr->values[i].resolution.units != IPP_RES_PER_CM)
4991 {
4992 valid = 0;
4993
4994 add_stringf(errors,
4995 "\"%s\": Bad resolution value %dx%d%s - bad "
4996 "units value (RFC 2911 section 4.1.15).",
4997 attr->name, attr->values[i].resolution.xres,
4998 attr->values[i].resolution.yres,
4999 attr->values[i].resolution.units ==
5000 IPP_RES_PER_INCH ? "dpi" :
5001 attr->values[i].resolution.units ==
5002 IPP_RES_PER_CM ? "dpcm" : "unknown");
5003 }
5004 }
5005 break;
5006
5007 case IPP_TAG_RANGE :
5008 for (i = 0; i < attr->num_values; i ++)
5009 {
5010 if (attr->values[i].range.lower > attr->values[i].range.upper)
5011 {
5012 valid = 0;
5013
5014 add_stringf(errors,
5015 "\"%s\": Bad rangeOfInteger value %d-%d - lower "
5016 "greater than upper (RFC 2911 section 4.1.13).",
5017 attr->name, attr->values[i].range.lower,
5018 attr->values[i].range.upper);
5019 }
5020 }
5021 break;
5022
5023 case IPP_TAG_BEGIN_COLLECTION :
5024 for (i = 0; i < attr->num_values; i ++)
5025 {
5026 for (colattr = attr->values[i].collection->attrs;
5027 colattr;
5028 colattr = colattr->next)
5029 {
5030 if (!validate_attr(outfile, NULL, colattr))
5031 {
5032 valid = 0;
5033 break;
5034 }
5035 }
5036
5037 if (colattr && errors)
5038 {
5039 add_stringf(errors, "\"%s\": Bad collection value.", attr->name);
5040
5041 while (colattr)
5042 {
5043 validate_attr(outfile, errors, colattr);
5044 colattr = colattr->next;
5045 }
5046 }
5047 }
5048 break;
5049
5050 case IPP_TAG_TEXT :
5051 case IPP_TAG_TEXTLANG :
5052 for (i = 0; i < attr->num_values; i ++)
5053 {
5054 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
5055 {
5056 if ((*ptr & 0xe0) == 0xc0)
5057 {
5058 ptr ++;
5059 if ((*ptr & 0xc0) != 0x80)
5060 break;
5061 }
5062 else if ((*ptr & 0xf0) == 0xe0)
5063 {
5064 ptr ++;
5065 if ((*ptr & 0xc0) != 0x80)
5066 break;
5067 ptr ++;
5068 if ((*ptr & 0xc0) != 0x80)
5069 break;
5070 }
5071 else if ((*ptr & 0xf8) == 0xf0)
5072 {
5073 ptr ++;
5074 if ((*ptr & 0xc0) != 0x80)
5075 break;
5076 ptr ++;
5077 if ((*ptr & 0xc0) != 0x80)
5078 break;
5079 ptr ++;
5080 if ((*ptr & 0xc0) != 0x80)
5081 break;
5082 }
5083 else if (*ptr & 0x80)
5084 break;
5085 }
5086
5087 if (*ptr)
5088 {
5089 valid = 0;
5090
5091 add_stringf(errors,
5092 "\"%s\": Bad text value \"%s\" - bad UTF-8 "
5093 "sequence (RFC 2911 section 4.1.1).", attr->name,
5094 attr->values[i].string.text);
5095 }
5096
5097 if ((ptr - attr->values[i].string.text) > (IPP_MAX_TEXT - 1))
5098 {
5099 valid = 0;
5100
5101 add_stringf(errors,
5102 "\"%s\": Bad text value \"%s\" - bad length %d "
5103 "(RFC 2911 section 4.1.1).", attr->name,
5104 attr->values[i].string.text,
5105 (int)strlen(attr->values[i].string.text));
5106 }
5107 }
5108 break;
5109
5110 case IPP_TAG_NAME :
5111 case IPP_TAG_NAMELANG :
5112 for (i = 0; i < attr->num_values; i ++)
5113 {
5114 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
5115 {
5116 if ((*ptr & 0xe0) == 0xc0)
5117 {
5118 ptr ++;
5119 if ((*ptr & 0xc0) != 0x80)
5120 break;
5121 }
5122 else if ((*ptr & 0xf0) == 0xe0)
5123 {
5124 ptr ++;
5125 if ((*ptr & 0xc0) != 0x80)
5126 break;
5127 ptr ++;
5128 if ((*ptr & 0xc0) != 0x80)
5129 break;
5130 }
5131 else if ((*ptr & 0xf8) == 0xf0)
5132 {
5133 ptr ++;
5134 if ((*ptr & 0xc0) != 0x80)
5135 break;
5136 ptr ++;
5137 if ((*ptr & 0xc0) != 0x80)
5138 break;
5139 ptr ++;
5140 if ((*ptr & 0xc0) != 0x80)
5141 break;
5142 }
5143 else if (*ptr & 0x80)
5144 break;
5145 }
5146
5147 if (*ptr)
5148 {
5149 valid = 0;
5150
5151 add_stringf(errors,
5152 "\"%s\": Bad name value \"%s\" - bad UTF-8 "
5153 "sequence (RFC 2911 section 4.1.2).", attr->name,
5154 attr->values[i].string.text);
5155 }
5156
5157 if ((ptr - attr->values[i].string.text) > (IPP_MAX_NAME - 1))
5158 {
5159 valid = 0;
5160
5161 add_stringf(errors,
5162 "\"%s\": Bad name value \"%s\" - bad length %d "
5163 "(RFC 2911 section 4.1.2).", attr->name,
5164 attr->values[i].string.text,
5165 (int)strlen(attr->values[i].string.text));
5166 }
5167 }
5168 break;
5169
5170 case IPP_TAG_KEYWORD :
5171 for (i = 0; i < attr->num_values; i ++)
5172 {
5173 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
5174 if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' &&
5175 *ptr != '_')
5176 break;
5177
5178 if (*ptr || ptr == attr->values[i].string.text)
5179 {
5180 valid = 0;
5181
5182 add_stringf(errors,
5183 "\"%s\": Bad keyword value \"%s\" - invalid "
5184 "character (RFC 2911 section 4.1.3).",
5185 attr->name, attr->values[i].string.text);
5186 }
5187
5188 if ((ptr - attr->values[i].string.text) > (IPP_MAX_KEYWORD - 1))
5189 {
5190 valid = 0;
5191
5192 add_stringf(errors,
5193 "\"%s\": Bad keyword value \"%s\" - bad "
5194 "length %d (RFC 2911 section 4.1.3).",
5195 attr->name, attr->values[i].string.text,
5196 (int)strlen(attr->values[i].string.text));
5197 }
5198 }
5199 break;
5200
5201 case IPP_TAG_URI :
5202 for (i = 0; i < attr->num_values; i ++)
5203 {
5204 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL,
5205 attr->values[i].string.text,
5206 scheme, sizeof(scheme),
5207 userpass, sizeof(userpass),
5208 hostname, sizeof(hostname),
5209 &port, resource, sizeof(resource));
5210
5211 if (uri_status < HTTP_URI_OK)
5212 {
5213 valid = 0;
5214
5215 add_stringf(errors,
5216 "\"%s\": Bad URI value \"%s\" - %s "
5217 "(RFC 2911 section 4.1.5).", attr->name,
5218 attr->values[i].string.text,
5219 httpURIStatusString(uri_status));
5220 }
5221
5222 if (strlen(attr->values[i].string.text) > (IPP_MAX_URI - 1))
5223 {
5224 valid = 0;
5225
5226 add_stringf(errors,
5227 "\"%s\": Bad URI value \"%s\" - bad length %d "
5228 "(RFC 2911 section 4.1.5).", attr->name,
5229 attr->values[i].string.text,
5230 (int)strlen(attr->values[i].string.text));
5231 }
5232 }
5233 break;
5234
5235 case IPP_TAG_URISCHEME :
5236 for (i = 0; i < attr->num_values; i ++)
5237 {
5238 ptr = attr->values[i].string.text;
5239 if (islower(*ptr & 255))
5240 {
5241 for (ptr ++; *ptr; ptr ++)
5242 if (!islower(*ptr & 255) && !isdigit(*ptr & 255) &&
5243 *ptr != '+' && *ptr != '-' && *ptr != '.')
5244 break;
5245 }
5246
5247 if (*ptr || ptr == attr->values[i].string.text)
5248 {
5249 valid = 0;
5250
5251 add_stringf(errors,
5252 "\"%s\": Bad uriScheme value \"%s\" - bad "
5253 "characters (RFC 2911 section 4.1.6).",
5254 attr->name, attr->values[i].string.text);
5255 }
5256
5257 if ((ptr - attr->values[i].string.text) > (IPP_MAX_URISCHEME - 1))
5258 {
5259 valid = 0;
5260
5261 add_stringf(errors,
5262 "\"%s\": Bad uriScheme value \"%s\" - bad "
5263 "length %d (RFC 2911 section 4.1.6).",
5264 attr->name, attr->values[i].string.text,
5265 (int)strlen(attr->values[i].string.text));
5266 }
5267 }
5268 break;
5269
5270 case IPP_TAG_CHARSET :
5271 for (i = 0; i < attr->num_values; i ++)
5272 {
5273 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
5274 if (!isprint(*ptr & 255) || isupper(*ptr & 255) ||
5275 isspace(*ptr & 255))
5276 break;
5277
5278 if (*ptr || ptr == attr->values[i].string.text)
5279 {
5280 valid = 0;
5281
5282 add_stringf(errors,
5283 "\"%s\": Bad charset value \"%s\" - bad "
5284 "characters (RFC 2911 section 4.1.7).",
5285 attr->name, attr->values[i].string.text);
5286 }
5287
5288 if ((ptr - attr->values[i].string.text) > (IPP_MAX_CHARSET - 1))
5289 {
5290 valid = 0;
5291
5292 add_stringf(errors,
5293 "\"%s\": Bad charset value \"%s\" - bad "
5294 "length %d (RFC 2911 section 4.1.7).",
5295 attr->name, attr->values[i].string.text,
5296 (int)strlen(attr->values[i].string.text));
5297 }
5298 }
5299 break;
5300
5301 case IPP_TAG_LANGUAGE :
5302 /*
5303 * The following regular expression is derived from the ABNF for
5304 * language tags in RFC 4646. All I can say is that this is the
5305 * easiest way to check the values...
5306 */
5307
5308 if ((i = regcomp(&re,
5309 "^("
5310 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
5311 /* language */
5312 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
5313 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
5314 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
5315 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
5316 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
5317 "|"
5318 "x(-[a-z0-9]{1,8})+" /* privateuse */
5319 "|"
5320 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
5321 ")$",
5322 REG_NOSUB | REG_EXTENDED)) != 0)
5323 {
5324 char temp[256]; /* Temporary error string */
5325
5326 regerror(i, &re, temp, sizeof(temp));
5327 print_fatal_error(outfile, "Unable to compile naturalLanguage regular "
5328 "expression: %s.", temp);
5329 break;
5330 }
5331
5332 for (i = 0; i < attr->num_values; i ++)
5333 {
5334 if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
5335 {
5336 valid = 0;
5337
5338 add_stringf(errors,
5339 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5340 "characters (RFC 2911 section 4.1.8).",
5341 attr->name, attr->values[i].string.text);
5342 }
5343
5344 if (strlen(attr->values[i].string.text) > (IPP_MAX_LANGUAGE - 1))
5345 {
5346 valid = 0;
5347
5348 add_stringf(errors,
5349 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5350 "length %d (RFC 2911 section 4.1.8).",
5351 attr->name, attr->values[i].string.text,
5352 (int)strlen(attr->values[i].string.text));
5353 }
5354 }
5355
5356 regfree(&re);
5357 break;
5358
5359 case IPP_TAG_MIMETYPE :
5360 /*
5361 * The following regular expression is derived from the ABNF for
5362 * language tags in RFC 2045 and 4288. All I can say is that this is
5363 * the easiest way to check the values...
5364 */
5365
5366 if ((i = regcomp(&re,
5367 "^"
5368 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
5369 "/"
5370 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
5371 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
5372 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
5373 /* value */
5374 "$",
5375 REG_NOSUB | REG_EXTENDED)) != 0)
5376 {
5377 char temp[256]; /* Temporary error string */
5378
5379 regerror(i, &re, temp, sizeof(temp));
5380 print_fatal_error(outfile, "Unable to compile mimeMediaType regular "
5381 "expression: %s.", temp);
5382 break;
5383 }
5384
5385 for (i = 0; i < attr->num_values; i ++)
5386 {
5387 if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
5388 {
5389 valid = 0;
5390
5391 add_stringf(errors,
5392 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5393 "characters (RFC 2911 section 4.1.9).",
5394 attr->name, attr->values[i].string.text);
5395 }
5396
5397 if (strlen(attr->values[i].string.text) > (IPP_MAX_MIMETYPE - 1))
5398 {
5399 valid = 0;
5400
5401 add_stringf(errors,
5402 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5403 "length %d (RFC 2911 section 4.1.9).",
5404 attr->name, attr->values[i].string.text,
5405 (int)strlen(attr->values[i].string.text));
5406 }
5407 }
5408
5409 regfree(&re);
5410 break;
5411
5412 default :
5413 break;
5414 }
5415
5416 return (valid);
5417 }
5418
5419
5420 /*
5421 * 'with_value()' - Test a WITH-VALUE predicate.
5422 */
5423
5424 static int /* O - 1 on match, 0 on non-match */
5425 with_value(FILE *outfile, /* I - Output file */
5426 cups_array_t *errors, /* I - Errors array */
5427 char *value, /* I - Value string */
5428 int flags, /* I - Flags for match */
5429 ipp_attribute_t *attr, /* I - Attribute to compare */
5430 char *matchbuf, /* I - Buffer to hold matching value */
5431 size_t matchlen) /* I - Length of match buffer */
5432 {
5433 int i, /* Looping var */
5434 match; /* Match? */
5435 char temp[1024], /* Temporary value string */
5436 *valptr; /* Pointer into value */
5437
5438
5439 *matchbuf = '\0';
5440 match = (flags & _CUPS_WITH_ALL) ? 1 : 0;
5441
5442 /*
5443 * NULL matches everything.
5444 */
5445
5446 if (!value || !*value)
5447 return (1);
5448
5449 /*
5450 * Compare the value string to the attribute value.
5451 */
5452
5453 switch (attr->value_tag)
5454 {
5455 case IPP_TAG_INTEGER :
5456 case IPP_TAG_ENUM :
5457 for (i = 0; i < attr->num_values; i ++)
5458 {
5459 char op, /* Comparison operator */
5460 *nextptr; /* Next pointer */
5461 int intvalue, /* Integer value */
5462 valmatch = 0; /* Does the current value match? */
5463
5464 valptr = value;
5465
5466 while (isspace(*valptr & 255) || isdigit(*valptr & 255) ||
5467 *valptr == '-' || *valptr == ',' || *valptr == '<' ||
5468 *valptr == '=' || *valptr == '>')
5469 {
5470 op = '=';
5471 while (*valptr && !isdigit(*valptr & 255) && *valptr != '-')
5472 {
5473 if (*valptr == '<' || *valptr == '>' || *valptr == '=')
5474 op = *valptr;
5475 valptr ++;
5476 }
5477
5478 if (!*valptr)
5479 break;
5480
5481 intvalue = (int)strtol(valptr, &nextptr, 0);
5482 if (nextptr == valptr)
5483 break;
5484 valptr = nextptr;
5485
5486 if ((op == '=' && attr->values[i].integer == intvalue) ||
5487 (op == '<' && attr->values[i].integer < intvalue) ||
5488 (op == '>' && attr->values[i].integer > intvalue))
5489 {
5490 if (!matchbuf[0])
5491 snprintf(matchbuf, matchlen, "%d", attr->values[i].integer);
5492
5493 valmatch = 1;
5494 break;
5495 }
5496 }
5497
5498 if (flags & _CUPS_WITH_ALL)
5499 {
5500 if (!valmatch)
5501 {
5502 match = 0;
5503 break;
5504 }
5505 }
5506 else if (valmatch)
5507 {
5508 match = 1;
5509 break;
5510 }
5511 }
5512
5513 if (!match && errors)
5514 {
5515 for (i = 0; i < attr->num_values; i ++)
5516 add_stringf(errors, "GOT: %s=%d", attr->name,
5517 attr->values[i].integer);
5518 }
5519 break;
5520
5521 case IPP_TAG_RANGE :
5522 for (i = 0; i < attr->num_values; i ++)
5523 {
5524 char op, /* Comparison operator */
5525 *nextptr; /* Next pointer */
5526 int intvalue, /* Integer value */
5527 valmatch = 0; /* Does the current value match? */
5528
5529 valptr = value;
5530
5531 while (isspace(*valptr & 255) || isdigit(*valptr & 255) ||
5532 *valptr == '-' || *valptr == ',' || *valptr == '<' ||
5533 *valptr == '=' || *valptr == '>')
5534 {
5535 op = '=';
5536 while (*valptr && !isdigit(*valptr & 255) && *valptr != '-')
5537 {
5538 if (*valptr == '<' || *valptr == '>' || *valptr == '=')
5539 op = *valptr;
5540 valptr ++;
5541 }
5542
5543 if (!*valptr)
5544 break;
5545
5546 intvalue = (int)strtol(valptr, &nextptr, 0);
5547 if (nextptr == valptr)
5548 break;
5549 valptr = nextptr;
5550
5551 if ((op == '=' && (attr->values[i].range.lower == intvalue ||
5552 attr->values[i].range.upper == intvalue)) ||
5553 (op == '<' && attr->values[i].range.upper < intvalue) ||
5554 (op == '>' && attr->values[i].range.upper > intvalue))
5555 {
5556 if (!matchbuf[0])
5557 snprintf(matchbuf, matchlen, "%d-%d",
5558 attr->values[0].range.lower,
5559 attr->values[0].range.upper);
5560
5561 valmatch = 1;
5562 break;
5563 }
5564 }
5565
5566 if (flags & _CUPS_WITH_ALL)
5567 {
5568 if (!valmatch)
5569 {
5570 match = 0;
5571 break;
5572 }
5573 }
5574 else if (valmatch)
5575 {
5576 match = 1;
5577 break;
5578 }
5579 }
5580
5581 if (!match && errors)
5582 {
5583 for (i = 0; i < attr->num_values; i ++)
5584 add_stringf(errors, "GOT: %s=%d-%d", attr->name,
5585 attr->values[i].range.lower,
5586 attr->values[i].range.upper);
5587 }
5588 break;
5589
5590 case IPP_TAG_BOOLEAN :
5591 for (i = 0; i < attr->num_values; i ++)
5592 {
5593 if (!strcmp(value, "true") == attr->values[i].boolean)
5594 {
5595 if (!matchbuf[0])
5596 strlcpy(matchbuf, value, matchlen);
5597
5598 if (!(flags & _CUPS_WITH_ALL))
5599 {
5600 match = 1;
5601 break;
5602 }
5603 }
5604 else if (flags & _CUPS_WITH_ALL)
5605 {
5606 match = 0;
5607 break;
5608 }
5609 }
5610
5611 if (!match && errors)
5612 {
5613 for (i = 0; i < attr->num_values; i ++)
5614 add_stringf(errors, "GOT: %s=%s", attr->name,
5615 attr->values[i].boolean ? "true" : "false");
5616 }
5617 break;
5618
5619 case IPP_TAG_RESOLUTION :
5620 for (i = 0; i < attr->num_values; i ++)
5621 {
5622 if (attr->values[i].resolution.xres ==
5623 attr->values[i].resolution.yres)
5624 snprintf(temp, sizeof(temp), "%d%s",
5625 attr->values[i].resolution.xres,
5626 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
5627 "dpi" : "dpcm");
5628 else
5629 snprintf(temp, sizeof(temp), "%dx%d%s",
5630 attr->values[i].resolution.xres,
5631 attr->values[i].resolution.yres,
5632 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
5633 "dpi" : "dpcm");
5634
5635 if (!strcmp(value, temp))
5636 {
5637 if (!matchbuf[0])
5638 strlcpy(matchbuf, value, matchlen);
5639
5640 if (!(flags & _CUPS_WITH_ALL))
5641 {
5642 match = 1;
5643 break;
5644 }
5645 }
5646 else if (flags & _CUPS_WITH_ALL)
5647 {
5648 match = 0;
5649 break;
5650 }
5651 }
5652
5653 if (!match && errors)
5654 {
5655 for (i = 0; i < attr->num_values; i ++)
5656 {
5657 if (attr->values[i].resolution.xres ==
5658 attr->values[i].resolution.yres)
5659 snprintf(temp, sizeof(temp), "%d%s",
5660 attr->values[i].resolution.xres,
5661 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
5662 "dpi" : "dpcm");
5663 else
5664 snprintf(temp, sizeof(temp), "%dx%d%s",
5665 attr->values[i].resolution.xres,
5666 attr->values[i].resolution.yres,
5667 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
5668 "dpi" : "dpcm");
5669
5670 if (strcmp(value, temp))
5671 add_stringf(errors, "GOT: %s=%s", attr->name, temp);
5672 }
5673 }
5674 break;
5675
5676 case IPP_TAG_NOVALUE :
5677 case IPP_TAG_UNKNOWN :
5678 return (1);
5679
5680 case IPP_TAG_CHARSET :
5681 case IPP_TAG_KEYWORD :
5682 case IPP_TAG_LANGUAGE :
5683 case IPP_TAG_MIMETYPE :
5684 case IPP_TAG_NAME :
5685 case IPP_TAG_NAMELANG :
5686 case IPP_TAG_TEXT :
5687 case IPP_TAG_TEXTLANG :
5688 case IPP_TAG_URI :
5689 case IPP_TAG_URISCHEME :
5690 if (flags & _CUPS_WITH_REGEX)
5691 {
5692 /*
5693 * Value is an extended, case-sensitive POSIX regular expression...
5694 */
5695
5696 regex_t re; /* Regular expression */
5697
5698 if ((i = regcomp(&re, value, REG_EXTENDED | REG_NOSUB)) != 0)
5699 {
5700 regerror(i, &re, temp, sizeof(temp));
5701
5702 print_fatal_error(outfile, "Unable to compile WITH-VALUE regular expression "
5703 "\"%s\" - %s", value, temp);
5704 return (0);
5705 }
5706
5707 /*
5708 * See if ALL of the values match the given regular expression.
5709 */
5710
5711 for (i = 0; i < attr->num_values; i ++)
5712 {
5713 if (!regexec(&re, get_string(attr, i, flags, temp, sizeof(temp)),
5714 0, NULL, 0))
5715 {
5716 if (!matchbuf[0])
5717 strlcpy(matchbuf,
5718 get_string(attr, i, flags, temp, sizeof(temp)),
5719 matchlen);
5720
5721 if (!(flags & _CUPS_WITH_ALL))
5722 {
5723 match = 1;
5724 break;
5725 }
5726 }
5727 else if (flags & _CUPS_WITH_ALL)
5728 {
5729 match = 0;
5730 break;
5731 }
5732 }
5733
5734 regfree(&re);
5735 }
5736 else
5737 {
5738 /*
5739 * Value is a literal string, see if the value(s) match...
5740 */
5741
5742 for (i = 0; i < attr->num_values; i ++)
5743 {
5744 if (!strcmp(value, get_string(attr, i, flags, temp, sizeof(temp))))
5745 {
5746 if (!matchbuf[0])
5747 strlcpy(matchbuf,
5748 get_string(attr, i, flags, temp, sizeof(temp)),
5749 matchlen);
5750
5751 if (!(flags & _CUPS_WITH_ALL))
5752 {
5753 match = 1;
5754 break;
5755 }
5756 }
5757 else if (flags & _CUPS_WITH_ALL)
5758 {
5759 match = 0;
5760 break;
5761 }
5762 }
5763 }
5764
5765 if (!match && errors)
5766 {
5767 for (i = 0; i < attr->num_values; i ++)
5768 add_stringf(errors, "GOT: %s=\"%s\"", attr->name,
5769 attr->values[i].string.text);
5770 }
5771 break;
5772
5773 default :
5774 break;
5775 }
5776
5777 return (match);
5778 }
5779
5780
5781 /*
5782 * End of "$Id$".
5783 */