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