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