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