]> git.ipfire.org Git - thirdparty/cups.git/blob - test/ipptool.c
6d56f1c54f95b9d5ac17042fa5687085e788cc6b
[thirdparty/cups.git] / test / ipptool.c
1 /*
2 * "$Id$"
3 *
4 * ipptool command for CUPS.
5 *
6 * Copyright 2007-2010 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 * Contents:
16 *
17 * main() - Parse options and do tests.
18 * compare_vars() - Compare two variables.
19 * do_tests() - Do tests as specified in the test file.
20 * expand_variables() - Expand variables in a string.
21 * expect_matches() - Return true if the tag matches the specification.
22 * get_collection() - Get a collection value from the current test file.
23 * get_filename() - Get a filename based on the current test file.
24 * get_token() - Get a token from a file.
25 * get_variable() - Get the value of a variable.
26 * iso_date() - Return an ISO 8601 date/time string for the given IPP
27 * dateTime value.
28 * print_attr() - Print an attribute on the screen.
29 * print_col() - Print a collection attribute on the screen.
30 * print_csv() - Print a line of CSV text.
31 * print_fatal_error() - Print a fatal error message.
32 * print_line() - Print a line of formatted text.
33 * print_test_error() - Print a test error message.
34 * print_xml_header() - Print a standard XML plist header.
35 * print_xml_string() - Print an XML string with escaping.
36 * print_xml_trailer() - Print the XML trailer with success/fail value.
37 * set_variable() - Set a variable value.
38 * usage() - Show program usage.
39 * validate_attr() - Determine whether an attribute is valid.
40 * with_value() - Test a WITH-VALUE predicate.
41 */
42
43 /*
44 * Include necessary headers...
45 */
46
47 #include <cups/cups-private.h>
48 #include <cups/file-private.h>
49 #include <regex.h>
50
51 #ifndef O_BINARY
52 # define O_BINARY 0
53 #endif /* !O_BINARY */
54
55
56 /*
57 * Types...
58 */
59
60 typedef enum _cups_transfer_e /**** How to send request data ****/
61 {
62 _CUPS_TRANSFER_AUTO, /* Chunk for files, length for static */
63 _CUPS_TRANSFER_CHUNKED, /* Chunk always */
64 _CUPS_TRANSFER_LENGTH /* Length always */
65 } _cups_transfer_t;
66
67 typedef enum _cups_output_e /**** Output mode ****/
68 {
69 _CUPS_OUTPUT_QUIET, /* No output */
70 _CUPS_OUTPUT_TEST, /* Traditional CUPS test output */
71 _CUPS_OUTPUT_PLIST, /* XML plist test output */
72 _CUPS_OUTPUT_LIST, /* Tabular list output */
73 _CUPS_OUTPUT_CSV /* Comma-separated values output */
74 } _cups_output_t;
75
76 typedef struct _cups_expect_s /**** Expected attribute info ****/
77 {
78 int optional, /* Optional attribute? */
79 not_expect; /* Don't expect attribute? */
80 char *name, /* Attribute name */
81 *of_type, /* Type name */
82 *same_count_as, /* Parallel attribute name */
83 *if_defined, /* Only required if variable defined */
84 *if_undefined, /* Only required if variable is not defined */
85 *with_value; /* Attribute must include this value */
86 int with_regex, /* WITH-VALUE is a regular expression */
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_undefined; /* Only if variable is not defined */
96 } _cups_status_t;
97
98 typedef struct _cups_var_s /**** Variable ****/
99 {
100 char *name, /* Name of variable */
101 *value; /* Value of variable */
102 } _cups_var_t;
103
104 typedef struct _cups_vars_s /**** Set of variables ****/
105 {
106 const char *uri, /* URI for printer */
107 *filename; /* Filename */
108 char scheme[64], /* Scheme from URI */
109 userpass[256], /* Username/password from URI */
110 hostname[256], /* Hostname from URI */
111 resource[1024]; /* Resource path from URI */
112 int port; /* Port number from URI */
113 http_encryption_t encryption; /* Encryption for connection? */
114 cups_array_t *vars; /* Array of variables */
115 } _cups_vars_t;
116
117
118 /*
119 * Globals...
120 */
121
122 _cups_transfer_t Transfer = _CUPS_TRANSFER_AUTO;
123 /* How to transfer requests */
124 _cups_output_t Output = _CUPS_OUTPUT_LIST;
125 /* Output mode */
126 int Verbosity = 0, /* Show all attributes? */
127 Version = 11, /* Default IPP version */
128 XMLHeader = 0; /* 1 if header is written */
129 const char * const URIStatusStrings[] = /* URI status strings */
130 {
131 "URI too large",
132 "Bad arguments to function",
133 "Bad resource in URI",
134 "Bad port number in URI",
135 "Bad hostname/address in URI",
136 "Bad username in URI",
137 "Bad scheme in URI",
138 "Bad/empty URI",
139 "OK",
140 "Missing scheme in URI",
141 "Unknown scheme in URI",
142 "Missing resource in URI"
143 };
144
145
146 /*
147 * Local functions...
148 */
149
150 static int compare_vars(_cups_var_t *a, _cups_var_t *b);
151 static int do_tests(_cups_vars_t *vars, const char *testfile);
152 static void expand_variables(_cups_vars_t *vars, char *dst, const char *src,
153 size_t dstsize)
154 #ifdef __GNUC__
155 __attribute((nonnull(1,2,3)))
156 #endif /* __GNUC__ */
157 ;
158 static int expect_matches(_cups_expect_t *expect, ipp_tag_t value_tag);
159 static ipp_t *get_collection(_cups_vars_t *vars, FILE *fp, int *linenum);
160 static char *get_filename(const char *testfile, char *dst, const char *src,
161 size_t dstsize);
162 static char *get_token(FILE *fp, char *buf, int buflen,
163 int *linenum);
164 static char *get_variable(_cups_vars_t *vars, const char *name);
165 static char *iso_date(ipp_uchar_t *date);
166 static void print_attr(ipp_attribute_t *attr);
167 static void print_col(ipp_t *col);
168 static void print_csv(ipp_attribute_t *attr, int num_displayed,
169 char **displayed, size_t *widths);
170 static void print_fatal_error(const char *s, ...)
171 #ifdef __GNUC__
172 __attribute__ ((__format__ (__printf__, 1, 2)))
173 #endif /* __GNUC__ */
174 ;
175 static void print_line(ipp_attribute_t *attr, int num_displayed,
176 char **displayed, size_t *widths);
177 static void print_test_error(const char *s, ...)
178 #ifdef __GNUC__
179 __attribute__ ((__format__ (__printf__, 1, 2)))
180 #endif /* __GNUC__ */
181 ;
182 static void print_xml_header(void);
183 static void print_xml_string(const char *element, const char *s);
184 static void print_xml_trailer(int success, const char *message);
185 static void set_variable(_cups_vars_t *vars, const char *name,
186 const char *value);
187 static void usage(void);
188 static int validate_attr(ipp_attribute_t *attr, int print);
189 static int with_value(char *value, int regex, ipp_attribute_t *attr);
190
191
192 /*
193 * 'main()' - Parse options and do tests.
194 */
195
196 int /* O - Exit status */
197 main(int argc, /* I - Number of command-line args */
198 char *argv[]) /* I - Command-line arguments */
199 {
200 int i; /* Looping var */
201 int status; /* Status of tests... */
202 char *opt, /* Current option */
203 name[1024], /* Name/value buffer */
204 *value, /* Pointer to value */
205 filename[1024], /* Real filename */
206 testname[1024]; /* Real test filename */
207 const char *testfile; /* Test file to use */
208 int interval, /* Test interval */
209 repeat; /* Repeat count */
210 _cups_vars_t vars; /* Variables */
211 http_uri_status_t uri_status; /* URI separation status */
212 _cups_globals_t *cg = _cupsGlobals();
213 /* Global data */
214
215
216
217 /*
218 * Initialize the locale and variables...
219 */
220
221 _cupsSetLocale(argv);
222
223 memset(&vars, 0, sizeof(vars));
224 vars.vars = cupsArrayNew((cups_array_func_t)compare_vars, NULL);
225
226 /*
227 * We need at least:
228 *
229 * ipptool URI testfile
230 */
231
232 interval = 0;
233 repeat = 0;
234 status = 0;
235 testfile = NULL;
236
237 for (i = 1; i < argc; i ++)
238 {
239 if (argv[i][0] == '-')
240 {
241 for (opt = argv[i] + 1; *opt; opt ++)
242 {
243 switch (*opt)
244 {
245 case 'C' : /* Enable HTTP chunking */
246 Transfer = _CUPS_TRANSFER_CHUNKED;
247 break;
248
249 case 'E' : /* Encrypt with TLS */
250 #ifdef HAVE_SSL
251 vars.encryption = HTTP_ENCRYPT_REQUIRED;
252 #else
253 _cupsLangPrintf(stderr,
254 _("%s: Sorry, no encryption support compiled in\n"),
255 argv[0]);
256 #endif /* HAVE_SSL */
257 break;
258
259 case 'L' : /* Disable HTTP chunking */
260 Transfer = _CUPS_TRANSFER_LENGTH;
261 break;
262
263 case 'S' : /* Encrypt with SSL */
264 #ifdef HAVE_SSL
265 vars.encryption = HTTP_ENCRYPT_ALWAYS;
266 #else
267 _cupsLangPrintf(stderr,
268 _("%s: Sorry, no encryption support compiled in\n"),
269 argv[0]);
270 #endif /* HAVE_SSL */
271 break;
272
273 case 'V' : /* Set IPP version */
274 i ++;
275
276 if (i >= argc)
277 {
278 _cupsLangPuts(stderr,
279 _("ipptool: Missing version for \"-V\".\n"));
280 usage();
281 }
282
283 if (!strcmp(argv[i], "1.0"))
284 Version = 10;
285 else if (!strcmp(argv[i], "1.1"))
286 Version = 11;
287 else if (!strcmp(argv[i], "2.0"))
288 Version = 20;
289 else if (!strcmp(argv[i], "2.1"))
290 Version = 21;
291 else if (!strcmp(argv[i], "2.2"))
292 Version = 22;
293 else
294 {
295 _cupsLangPrintf(stderr,
296 _("ipptool: Bad version %s for \"-V\".\n"),
297 argv[i]);
298 usage();
299 }
300 break;
301
302 case 'X' : /* Produce XML output */
303 Output = _CUPS_OUTPUT_PLIST;
304
305 if (interval || repeat)
306 {
307 _cupsLangPuts(stderr, _("ipptool: \"-i\" and \"-n\" are "
308 "incompatible with -X\".\n"));
309 usage();
310 }
311 break;
312
313 case 'c' : /* CSV output */
314 Output = _CUPS_OUTPUT_CSV;
315 break;
316
317 case 'd' : /* Define a variable */
318 i ++;
319
320 if (i >= argc)
321 {
322 _cupsLangPuts(stderr,
323 _("ipptool: Missing name=value for \"-d\".\n"));
324 usage();
325 }
326
327 strlcpy(name, argv[i], sizeof(name));
328 if ((value = strchr(name, '=')) != NULL)
329 *value++ = '\0';
330 else
331 value = name + strlen(name);
332
333 set_variable(&vars, name, value);
334 break;
335
336 case 'f' : /* Set the default test filename */
337 i ++;
338
339 if (i >= argc)
340 {
341 _cupsLangPuts(stderr,
342 _("ipptool: Missing filename for \"-f\".\n"));
343 usage();
344 }
345
346 if (access(argv[i], 0) && argv[i][0] != '/')
347 {
348 snprintf(filename, sizeof(filename), "%s/ipptool/%s",
349 cg->cups_datadir, argv[i]);
350 if (access(argv[i], 0))
351 vars.filename = argv[i];
352 else
353 vars.filename = filename;
354 }
355 else
356 vars.filename = argv[i];
357 break;
358
359 case 'i' : /* Test every N seconds */
360 i ++;
361
362 if (i >= argc)
363 {
364 _cupsLangPuts(stderr,
365 _("ipptool: Missing seconds for \"-i\".\n"));
366 usage();
367 }
368 else
369 interval = atoi(argv[i]);
370
371 if (Output == _CUPS_OUTPUT_PLIST && interval)
372 {
373 _cupsLangPuts(stderr, _("ipptool: \"-i\" is incompatible with "
374 "\"-X\".\n"));
375 usage();
376 }
377 break;
378
379 case 'l' : /* List as a table */
380 Output = _CUPS_OUTPUT_LIST;
381 break;
382
383 case 'n' : /* Repeat count */
384 i ++;
385
386 if (i >= argc)
387 {
388 _cupsLangPuts(stderr,
389 _("ipptool: Missing count for \"-n\".\n"));
390 usage();
391 }
392 else
393 repeat = atoi(argv[i]);
394
395 if (Output == _CUPS_OUTPUT_PLIST && repeat)
396 {
397 _cupsLangPuts(stderr, _("ipptool: \"-n\" is incompatible with "
398 "\"-X\".\n"));
399 usage();
400 }
401 break;
402
403 case 'q' : /* Be quiet */
404 Output = _CUPS_OUTPUT_QUIET;
405 break;
406
407 case 't' : /* CUPS test output */
408 Output = _CUPS_OUTPUT_TEST;
409 break;
410
411 case 'v' : /* Be verbose */
412 Verbosity ++;
413 break;
414
415 default :
416 _cupsLangPrintf(stderr, _("ipptool: Unknown option \"-%c\".\n"),
417 *opt);
418 usage();
419 break;
420 }
421 }
422 }
423 else if (!strncmp(argv[i], "ipp://", 6) ||
424 !strncmp(argv[i], "http://", 7) ||
425 !strncmp(argv[i], "https://", 8))
426 {
427 /*
428 * Set URI...
429 */
430
431 if (vars.uri)
432 {
433 _cupsLangPuts(stderr, _("ipptool: May only specify a single URI.\n"));
434 usage();
435 }
436
437 vars.uri = argv[i];
438 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, vars.uri,
439 vars.scheme, sizeof(vars.scheme),
440 vars.userpass, sizeof(vars.userpass),
441 vars.hostname, sizeof(vars.hostname),
442 &(vars.port),
443 vars.resource, sizeof(vars.resource));
444
445 if (uri_status != HTTP_URI_OK)
446 {
447 _cupsLangPrintf(stderr, _("ipptool: Bad URI - %s.\n"),
448 URIStatusStrings[uri_status - HTTP_URI_OVERFLOW]);
449 return (1);
450 }
451
452 if (strcmp(vars.scheme, "http") && strcmp(vars.scheme, "https") &&
453 strcmp(vars.scheme, "ipp"))
454 {
455 _cupsLangPuts(stderr, _("ipptool: Only http, https, and ipp URIs are "
456 "supported."));
457 return (1);
458 }
459 }
460 else
461 {
462 /*
463 * Run test...
464 */
465
466 if (!vars.uri)
467 {
468 _cupsLangPuts(stderr, _("ipptool: URI required before test file."));
469 usage();
470 }
471
472 if (access(argv[i], 0) && argv[i][0] != '/')
473 {
474 snprintf(testname, sizeof(testname), "%s/ipptool/%s", cg->cups_datadir,
475 argv[i]);
476 if (access(testname, 0))
477 testfile = argv[i];
478 else
479 testfile = testname;
480 }
481 else
482 testfile = argv[i];
483
484 if (!do_tests(&vars, testfile))
485 status = 1;
486 }
487 }
488
489 if (!vars.uri || !testfile)
490 usage();
491
492 /*
493 * Loop if the interval is set...
494 */
495
496 if (Output == _CUPS_OUTPUT_PLIST)
497 print_xml_trailer(!status, NULL);
498 else if (interval && repeat > 0)
499 {
500 while (repeat > 1)
501 {
502 sleep(interval);
503 do_tests(&vars, testfile);
504 repeat --;
505 }
506 }
507 else if (interval)
508 {
509 for (;;)
510 {
511 sleep(interval);
512 do_tests(&vars, testfile);
513 }
514 }
515
516 /*
517 * Exit...
518 */
519
520 return (status);
521 }
522
523
524 /*
525 * 'compare_vars()' - Compare two variables.
526 */
527
528 static int /* O - Result of comparison */
529 compare_vars(_cups_var_t *a, /* I - First variable */
530 _cups_var_t *b) /* I - Second variable */
531 {
532 return (strcasecmp(a->name, b->name));
533 }
534
535
536 /*
537 * 'do_tests()' - Do tests as specified in the test file.
538 */
539
540 static int /* 1 = success, 0 = failure */
541 do_tests(_cups_vars_t *vars, /* I - Variables */
542 const char *testfile) /* I - Test file to use */
543 {
544 int i, /* Looping var */
545 linenum, /* Current line number */
546 pass, /* Did we pass the test? */
547 request_id; /* Current request ID */
548 http_t *http = NULL; /* HTTP connection to server */
549 FILE *fp = NULL; /* Test file */
550 char resource[512], /* Resource for request */
551 token[1024], /* Token from file */
552 *tokenptr, /* Pointer into token */
553 temp[1024]; /* Temporary string */
554 ipp_t *request = NULL; /* IPP request */
555 ipp_t *response = NULL; /* IPP response */
556 char attr[128]; /* Attribute name */
557 ipp_op_t op; /* Operation */
558 ipp_tag_t group; /* Current group */
559 ipp_tag_t value; /* Current value type */
560 ipp_attribute_t *attrptr, /* Attribute pointer */
561 *found, /* Found attribute */
562 *lastcol = NULL; /* Last collection attribute */
563 char name[1024]; /* Name of test */
564 char filename[1024]; /* Filename */
565 _cups_transfer_t transfer; /* To chunk or not to chunk */
566 int version; /* IPP version number to use */
567 int num_statuses = 0; /* Number of valid status codes */
568 _cups_status_t statuses[100], /* Valid status codes */
569 *last_status; /* Last STATUS (for predicates) */
570 int num_expects = 0; /* Number of expected attributes */
571 _cups_expect_t expects[200], /* Expected attributes */
572 *expect, /* Current expected attribute */
573 *last_expect; /* Last EXPECT (for predicates) */
574 int num_displayed = 0; /* Number of displayed attributes */
575 char *displayed[200]; /* Displayed attributes */
576 size_t widths[200]; /* Width of columns */
577
578
579 /*
580 * Open the test file...
581 */
582
583 if ((fp = fopen(testfile, "r")) == NULL)
584 {
585 print_fatal_error("Unable to open test file %s - %s", testfile,
586 strerror(errno));
587 goto test_error;
588 }
589
590 /*
591 * Connect to the server...
592 */
593
594 if ((http = httpConnectEncrypt(vars->hostname, vars->port,
595 vars->encryption)) == NULL)
596 {
597 print_fatal_error("Unable to connect to %s on port %d - %s", vars->hostname,
598 vars->port, strerror(errno));
599 goto test_error;
600 }
601
602 /*
603 * Loop on tests...
604 */
605
606 if (Output == _CUPS_OUTPUT_PLIST)
607 print_xml_header();
608 else if (Output == _CUPS_OUTPUT_TEST)
609 printf("\"%s\":\n", testfile);
610
611 CUPS_SRAND(time(NULL));
612
613 pass = 1;
614 linenum = 1;
615 request_id = (CUPS_RAND() % 1000) * 137 + 1;
616
617 while (get_token(fp, token, sizeof(token), &linenum) != NULL)
618 {
619 /*
620 * Expect an open brace...
621 */
622
623 if (!strcmp(token, "DEFINE"))
624 {
625 /*
626 * DEFINE name value
627 */
628
629 if (get_token(fp, attr, sizeof(attr), &linenum) &&
630 get_token(fp, temp, sizeof(temp), &linenum))
631 {
632 expand_variables(vars, token, temp, sizeof(token));
633 set_variable(vars, attr, token);
634 }
635 else
636 {
637 print_fatal_error("Missing DEFINE name and/or value on line %d.",
638 linenum);
639 goto test_error;
640 }
641
642 continue;
643 }
644 else if (!strcmp(token, "INCLUDE"))
645 {
646 /*
647 * INCLUDE "filename"
648 * INCLUDE <filename>
649 */
650
651 if (get_token(fp, temp, sizeof(temp), &linenum))
652 {
653 /*
654 * Map the filename to and then run the tests...
655 */
656
657 if (!do_tests(vars, get_filename(testfile, filename, temp,
658 sizeof(filename))))
659 goto test_error;
660 }
661 else
662 {
663 print_fatal_error("Missing INCLUDE filename on line %d.", linenum);
664 goto test_error;
665 }
666
667 continue;
668 }
669 else if (!strcmp(token, "TRANSFER"))
670 {
671 /*
672 * TRANSFER auto
673 * TRANSFER chunked
674 * TRANSFER length
675 */
676
677 if (get_token(fp, temp, sizeof(temp), &linenum))
678 {
679 if (!strcmp(temp, "auto"))
680 Transfer = _CUPS_TRANSFER_AUTO;
681 else if (!strcmp(temp, "chunked"))
682 Transfer = _CUPS_TRANSFER_CHUNKED;
683 else if (!strcmp(temp, "length"))
684 Transfer = _CUPS_TRANSFER_LENGTH;
685 else
686 {
687 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp,
688 linenum);
689 goto test_error;
690 }
691 }
692 else
693 {
694 print_fatal_error("Missing TRANSFER value on line %d.", linenum);
695 goto test_error;
696 }
697
698 continue;
699 }
700 else if (!strcmp(token, "VERSION"))
701 {
702 if (get_token(fp, temp, sizeof(temp), &linenum))
703 {
704 if (!strcmp(temp, "1.0"))
705 Version = 10;
706 else if (!strcmp(temp, "1.1"))
707 Version = 11;
708 else if (!strcmp(temp, "2.0"))
709 Version = 20;
710 else if (!strcmp(temp, "2.1"))
711 Version = 21;
712 else if (!strcmp(temp, "2.2"))
713 Version = 22;
714 else
715 {
716 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp, linenum);
717 goto test_error;
718 }
719 }
720 else
721 {
722 print_fatal_error("Missing VERSION number on line %d.", linenum);
723 goto test_error;
724 }
725
726 continue;
727 }
728 else if (strcmp(token, "{"))
729 {
730 print_fatal_error("Unexpected token %s seen on line %d.", token, linenum);
731 goto test_error;
732 }
733
734 /*
735 * Initialize things...
736 */
737
738 strlcpy(resource, vars->resource, sizeof(resource));
739
740 request_id ++;
741 request = ippNew();
742 op = (ipp_op_t)0;
743 group = IPP_TAG_ZERO;
744 last_expect = NULL;
745 last_status = NULL;
746 filename[0] = '\0';
747 version = Version;
748 transfer = Transfer;
749
750 strlcpy(name, testfile, sizeof(name));
751 if (strrchr(name, '.') != NULL)
752 *strrchr(name, '.') = '\0';
753
754 /*
755 * Parse until we see a close brace...
756 */
757
758 while (get_token(fp, token, sizeof(token), &linenum) != NULL)
759 {
760 if (strcasecmp(token, "COUNT") &&
761 strcasecmp(token, "IF-DEFINED") &&
762 strcasecmp(token, "IF-UNDEFINED") &&
763 strcasecmp(token, "IN-GROUP") &&
764 strcasecmp(token, "OF-TYPE") &&
765 strcasecmp(token, "SAME-COUNT-AS") &&
766 strcasecmp(token, "WITH-VALUE"))
767 last_expect = NULL;
768
769 if (strcasecmp(token, "IF-DEFINED") &&
770 strcasecmp(token, "IF-UNDEFINED"))
771 last_status = NULL;
772
773 if (!strcmp(token, "}"))
774 break;
775 else if (!strcmp(token, "{") && lastcol)
776 {
777 /*
778 * Another collection value
779 */
780
781 ipp_t *col = get_collection(vars, fp, &linenum);
782 /* Collection value */
783
784 if (col)
785 {
786 ipp_attribute_t *tempcol; /* Pointer to new buffer */
787
788
789 /*
790 * Reallocate memory...
791 */
792
793 if ((tempcol = realloc(lastcol, sizeof(ipp_attribute_t) +
794 (lastcol->num_values + 1) *
795 sizeof(ipp_value_t))) == NULL)
796 {
797 print_fatal_error("Unable to allocate memory on line %d.", linenum);
798 goto test_error;
799 }
800
801 if (tempcol != lastcol)
802 {
803 /*
804 * Reset pointers in the list...
805 */
806
807 if (request->prev)
808 request->prev->next = tempcol;
809 else
810 request->attrs = tempcol;
811
812 lastcol = request->current = request->last = tempcol;
813 }
814
815 lastcol->values[lastcol->num_values].collection = col;
816 lastcol->num_values ++;
817 }
818 else
819 goto test_error;
820 }
821 else if (!strcmp(token, "DEFINE"))
822 {
823 /*
824 * DEFINE name value
825 */
826
827 if (get_token(fp, attr, sizeof(attr), &linenum) &&
828 get_token(fp, temp, sizeof(temp), &linenum))
829 {
830 expand_variables(vars, token, temp, sizeof(token));
831 set_variable(vars, attr, token);
832 }
833 else
834 {
835 print_fatal_error("Missing DEFINE name and/or value on line %d.",
836 linenum);
837 goto test_error;
838 }
839 }
840 else if (!strcasecmp(token, "NAME"))
841 {
842 /*
843 * Name of test...
844 */
845
846 get_token(fp, name, sizeof(name), &linenum);
847 }
848 else if (!strcmp(token, "REQUEST-ID"))
849 {
850 /*
851 * REQUEST-ID #
852 * REQUEST-ID random
853 */
854
855 if (get_token(fp, temp, sizeof(temp), &linenum))
856 {
857 if (isdigit(temp[0] & 255))
858 request_id = atoi(temp);
859 else if (!strcasecmp(temp, "random"))
860 request_id = (CUPS_RAND() % 1000) * 137 + 1;
861 else
862 {
863 print_fatal_error("Bad REQUEST-ID value \"%s\" on line %d.", temp,
864 linenum);
865 goto test_error;
866 }
867 }
868 else
869 {
870 print_fatal_error("Missing REQUEST-ID value on line %d.", linenum);
871 goto test_error;
872 }
873 }
874 else if (!strcmp(token, "TRANSFER"))
875 {
876 /*
877 * TRANSFER auto
878 * TRANSFER chunked
879 * TRANSFER length
880 */
881
882 if (get_token(fp, temp, sizeof(temp), &linenum))
883 {
884 if (!strcmp(temp, "auto"))
885 transfer = _CUPS_TRANSFER_AUTO;
886 else if (!strcmp(temp, "chunked"))
887 transfer = _CUPS_TRANSFER_CHUNKED;
888 else if (!strcmp(temp, "length"))
889 transfer = _CUPS_TRANSFER_LENGTH;
890 else
891 {
892 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp,
893 linenum);
894 goto test_error;
895 }
896 }
897 else
898 {
899 print_fatal_error("Missing TRANSFER value on line %d.", linenum);
900 goto test_error;
901 }
902 }
903 else if (!strcasecmp(token, "VERSION"))
904 {
905 if (get_token(fp, temp, sizeof(temp), &linenum))
906 {
907 if (!strcmp(temp, "0.0"))
908 version = 0;
909 else if (!strcmp(temp, "1.0"))
910 version = 10;
911 else if (!strcmp(temp, "1.1"))
912 version = 11;
913 else if (!strcmp(temp, "2.0"))
914 version = 20;
915 else if (!strcmp(temp, "2.1"))
916 version = 21;
917 else if (!strcmp(temp, "2.2"))
918 version = 22;
919 else
920 {
921 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp, linenum);
922 goto test_error;
923 }
924 }
925 else
926 {
927 print_fatal_error("Missing VERSION number on line %d.", linenum);
928 goto test_error;
929 }
930 }
931 else if (!strcasecmp(token, "RESOURCE"))
932 {
933 /*
934 * Resource name...
935 */
936
937 if (!get_token(fp, resource, sizeof(resource), &linenum))
938 {
939 print_fatal_error("Missing RESOURCE path on line %d.", linenum);
940 goto test_error;
941 }
942 }
943 else if (!strcasecmp(token, "OPERATION"))
944 {
945 /*
946 * Operation...
947 */
948
949 if (!get_token(fp, token, sizeof(token), &linenum))
950 {
951 print_fatal_error("Missing OPERATION code on line %d.", linenum);
952 goto test_error;
953 }
954
955 if ((op = ippOpValue(token)) < 0 && (op = strtol(token, NULL, 0)) == 0)
956 {
957 print_fatal_error("Bad OPERATION code \"%s\" on line %d.", token,
958 linenum);
959 goto test_error;
960 }
961 }
962 else if (!strcasecmp(token, "GROUP"))
963 {
964 /*
965 * Attribute group...
966 */
967
968 if (!get_token(fp, token, sizeof(token), &linenum))
969 {
970 print_fatal_error("Missing GROUP tag on line %d.", linenum);
971 goto test_error;
972 }
973
974 if ((value = ippTagValue(token)) < 0)
975 {
976 print_fatal_error("Bad GROUP tag \"%s\" on line %d.", token, linenum);
977 goto test_error;
978 }
979
980 if (value == group)
981 ippAddSeparator(request);
982
983 group = value;
984 }
985 else if (!strcasecmp(token, "DELAY"))
986 {
987 /*
988 * Delay before operation...
989 */
990
991 int delay;
992
993 if (!get_token(fp, token, sizeof(token), &linenum))
994 {
995 print_fatal_error("Missing DELAY value on line %d.", linenum);
996 goto test_error;
997 }
998
999 if ((delay = atoi(token)) <= 0)
1000 {
1001 print_fatal_error("Bad DELAY value \"%s\" on line %d.", token,
1002 linenum);
1003 goto test_error;
1004 }
1005 else
1006 sleep(delay);
1007 }
1008 else if (!strcasecmp(token, "ATTR"))
1009 {
1010 /*
1011 * Attribute...
1012 */
1013
1014 if (!get_token(fp, token, sizeof(token), &linenum))
1015 {
1016 print_fatal_error("Missing ATTR value tag on line %d.", linenum);
1017 goto test_error;
1018 }
1019
1020 if ((value = ippTagValue(token)) == IPP_TAG_ZERO)
1021 {
1022 print_fatal_error("Bad ATTR value tag \"%s\" on line %d.", token,
1023 linenum);
1024 goto test_error;
1025 }
1026
1027 if (!get_token(fp, attr, sizeof(attr), &linenum))
1028 {
1029 print_fatal_error("Missing ATTR name on line %d.", linenum);
1030 goto test_error;
1031 }
1032
1033 if (!get_token(fp, temp, sizeof(temp), &linenum))
1034 {
1035 print_fatal_error("Missing ATTR value on line %d.", linenum);
1036 goto test_error;
1037 }
1038
1039 expand_variables(vars, token, temp, sizeof(token));
1040
1041 switch (value)
1042 {
1043 case IPP_TAG_BOOLEAN :
1044 if (!strcasecmp(token, "true"))
1045 ippAddBoolean(request, group, attr, 1);
1046 else
1047 ippAddBoolean(request, group, attr, atoi(token));
1048 break;
1049
1050 case IPP_TAG_INTEGER :
1051 case IPP_TAG_ENUM :
1052 ippAddInteger(request, group, value, attr, atoi(token));
1053 break;
1054
1055 case IPP_TAG_RESOLUTION :
1056 {
1057 int xres, /* X resolution */
1058 yres; /* Y resolution */
1059 char units[6]; /* Units */
1060
1061 if (sscanf(token, "%dx%d%5s", &xres, &yres, units) != 3 ||
1062 (strcasecmp(units, "dpi") && strcasecmp(units, "dpc") &&
1063 strcasecmp(units, "other")))
1064 {
1065 print_fatal_error("Bad resolution value \"%s\" on line %d.",
1066 token, linenum);
1067 goto test_error;
1068 }
1069
1070 if (!strcasecmp(units, "dpi"))
1071 ippAddResolution(request, group, attr, xres, yres,
1072 IPP_RES_PER_INCH);
1073 else if (!strcasecmp(units, "dpc"))
1074 ippAddResolution(request, group, attr, xres, yres,
1075 IPP_RES_PER_CM);
1076 else
1077 ippAddResolution(request, group, attr, xres, yres,
1078 (ipp_res_t)0);
1079 }
1080 break;
1081
1082 case IPP_TAG_RANGE :
1083 {
1084 int lowers[4], /* Lower value */
1085 uppers[4], /* Upper values */
1086 num_vals; /* Number of values */
1087
1088
1089 num_vals = sscanf(token, "%d-%d,%d-%d,%d-%d,%d-%d",
1090 lowers + 0, uppers + 0,
1091 lowers + 1, uppers + 1,
1092 lowers + 2, uppers + 2,
1093 lowers + 3, uppers + 3);
1094
1095 if ((num_vals & 1) || num_vals == 0)
1096 {
1097 print_fatal_error("Bad rangeOfInteger value \"%s\" on line "
1098 "%d.", token, linenum);
1099 goto test_error;
1100 }
1101
1102 ippAddRanges(request, group, attr, num_vals / 2, lowers,
1103 uppers);
1104 }
1105 break;
1106
1107 case IPP_TAG_BEGIN_COLLECTION :
1108 if (!strcmp(token, "{"))
1109 {
1110 ipp_t *col = get_collection(vars, fp, &linenum);
1111 /* Collection value */
1112
1113 if (col)
1114 {
1115 lastcol = ippAddCollection(request, group, attr, col);
1116 ippDelete(col);
1117 }
1118 else
1119 goto test_error;
1120 }
1121 else
1122 {
1123 print_fatal_error("Bad ATTR collection value on line %d.",
1124 linenum);
1125 goto test_error;
1126 }
1127 break;
1128
1129 default :
1130 print_fatal_error("Unsupported ATTR value tag %s on line %d.",
1131 ippTagString(value), linenum);
1132 goto test_error;
1133
1134 case IPP_TAG_TEXTLANG :
1135 case IPP_TAG_NAMELANG :
1136 case IPP_TAG_TEXT :
1137 case IPP_TAG_NAME :
1138 case IPP_TAG_KEYWORD :
1139 case IPP_TAG_URI :
1140 case IPP_TAG_URISCHEME :
1141 case IPP_TAG_CHARSET :
1142 case IPP_TAG_LANGUAGE :
1143 case IPP_TAG_MIMETYPE :
1144 if (!strchr(token, ','))
1145 ippAddString(request, group, value, attr, NULL, token);
1146 else
1147 {
1148 /*
1149 * Multiple string values...
1150 */
1151
1152 int num_values; /* Number of values */
1153 char *values[100], /* Values */
1154 *ptr; /* Pointer to next value */
1155
1156
1157 values[0] = token;
1158 num_values = 1;
1159
1160 for (ptr = strchr(token, ','); ptr; ptr = strchr(ptr, ','))
1161 {
1162 *ptr++ = '\0';
1163 values[num_values] = ptr;
1164 num_values ++;
1165 }
1166
1167 ippAddStrings(request, group, value, attr, num_values,
1168 NULL, (const char **)values);
1169 }
1170 break;
1171 }
1172 }
1173 else if (!strcasecmp(token, "FILE"))
1174 {
1175 /*
1176 * File...
1177 */
1178
1179 if (!get_token(fp, temp, sizeof(temp), &linenum))
1180 {
1181 print_fatal_error("Missing FILE filename on line %d.", linenum);
1182 goto test_error;
1183 }
1184
1185 expand_variables(vars, token, temp, sizeof(token));
1186 get_filename(testfile, filename, token, sizeof(filename));
1187 }
1188 else if (!strcasecmp(token, "STATUS"))
1189 {
1190 /*
1191 * Status...
1192 */
1193
1194 if (num_statuses >= (int)(sizeof(statuses) / sizeof(statuses[0])))
1195 {
1196 print_fatal_error("Too many STATUS's on line %d.", linenum);
1197 goto test_error;
1198 }
1199
1200 if (!get_token(fp, token, sizeof(token), &linenum))
1201 {
1202 print_fatal_error("Missing STATUS code on line %d.", linenum);
1203 goto test_error;
1204 }
1205
1206 if ((statuses[num_statuses].status = ippErrorValue(token)) < 0)
1207 {
1208 print_fatal_error("Bad STATUS code \"%s\" on line %d.", token,
1209 linenum);
1210 goto test_error;
1211 }
1212
1213 last_status = statuses + num_statuses;
1214 num_statuses ++;
1215
1216 last_status->if_defined = NULL;
1217 last_status->if_undefined = NULL;
1218 }
1219 else if (!strcasecmp(token, "EXPECT"))
1220 {
1221 /*
1222 * Expected attributes...
1223 */
1224
1225 if (num_expects >= (int)(sizeof(expects) / sizeof(expects[0])))
1226 {
1227 print_fatal_error("Too many EXPECT's on line %d.", linenum);
1228 goto test_error;
1229 }
1230
1231 if (!get_token(fp, token, sizeof(token), &linenum))
1232 {
1233 print_fatal_error("Missing EXPECT name on line %d.", linenum);
1234 goto test_error;
1235 }
1236
1237 last_expect = expects + num_expects;
1238 num_expects ++;
1239
1240 memset(last_expect, 0, sizeof(_cups_expect_t));
1241
1242 if (token[0] == '!')
1243 {
1244 last_expect->not_expect = 1;
1245 last_expect->name = strdup(token + 1);
1246 }
1247 else if (token[0] == '?')
1248 {
1249 last_expect->optional = 1;
1250 last_expect->name = strdup(token + 1);
1251 }
1252 else
1253 last_expect->name = strdup(token);
1254 }
1255 else if (!strcasecmp(token, "COUNT"))
1256 {
1257 if (!get_token(fp, token, sizeof(token), &linenum))
1258 {
1259 print_fatal_error("Missing COUNT number on line %d.", linenum);
1260 goto test_error;
1261 }
1262
1263 if ((i = atoi(token)) <= 0)
1264 {
1265 print_fatal_error("Bad COUNT \"%s\" on line %d.", token, linenum);
1266 goto test_error;
1267 }
1268
1269 if (last_expect)
1270 last_expect->count = i;
1271 else
1272 {
1273 print_fatal_error("COUNT without a preceding EXPECT on line %d.",
1274 linenum);
1275 goto test_error;
1276 }
1277 }
1278 else if (!strcasecmp(token, "OF-TYPE"))
1279 {
1280 if (!get_token(fp, token, sizeof(token), &linenum))
1281 {
1282 print_fatal_error("Missing OF-TYPE value tag(s) on line %d.",
1283 linenum);
1284 goto test_error;
1285 }
1286
1287 if (last_expect)
1288 last_expect->of_type = strdup(token);
1289 else
1290 {
1291 print_fatal_error("OF-TYPE without a preceding EXPECT on line %d.",
1292 linenum);
1293 goto test_error;
1294 }
1295 }
1296 else if (!strcasecmp(token, "IN-GROUP"))
1297 {
1298 ipp_tag_t in_group; /* IN-GROUP value */
1299
1300
1301 if (!get_token(fp, token, sizeof(token), &linenum))
1302 {
1303 print_fatal_error("Missing IN-GROUP group tag on line %d.", linenum);
1304 goto test_error;
1305 }
1306
1307 if ((in_group = ippTagValue(token)) == (ipp_tag_t)-1)
1308 {
1309 }
1310 else if (last_expect)
1311 last_expect->in_group = in_group;
1312 else
1313 {
1314 print_fatal_error("IN-GROUP without a preceding EXPECT on line %d.",
1315 linenum);
1316 goto test_error;
1317 }
1318 }
1319 else if (!strcasecmp(token, "SAME-COUNT-AS"))
1320 {
1321 if (!get_token(fp, token, sizeof(token), &linenum))
1322 {
1323 print_fatal_error("Missing SAME-COUNT-AS name on line %d.", linenum);
1324 goto test_error;
1325 }
1326
1327 if (last_expect)
1328 last_expect->same_count_as = strdup(token);
1329 else
1330 {
1331 print_fatal_error("SAME-COUNT-AS without a preceding EXPECT on line "
1332 "%d.", linenum);
1333 goto test_error;
1334 }
1335 }
1336 else if (!strcasecmp(token, "IF-DEFINED"))
1337 {
1338 if (!get_token(fp, token, sizeof(token), &linenum))
1339 {
1340 print_fatal_error("Missing IF-DEFINED name on line %d.", linenum);
1341 goto test_error;
1342 }
1343
1344 if (last_expect)
1345 last_expect->if_defined = strdup(token);
1346 else if (last_status)
1347 last_status->if_defined = strdup(token);
1348 else
1349 {
1350 print_fatal_error("IF-DEFINED without a preceding EXPECT or STATUS "
1351 "on line %d.", linenum);
1352 goto test_error;
1353 }
1354 }
1355 else if (!strcasecmp(token, "IF-UNDEFINED"))
1356 {
1357 if (!get_token(fp, token, sizeof(token), &linenum))
1358 {
1359 print_fatal_error("Missing IF-UNDEFINED name on line %d.", linenum);
1360 goto test_error;
1361 }
1362
1363 if (last_expect)
1364 last_expect->if_undefined = strdup(token);
1365 else if (last_status)
1366 last_status->if_undefined = strdup(token);
1367 else
1368 {
1369 print_fatal_error("IF-UNDEFINED without a preceding EXPECT or STATUS "
1370 "on line %d.", linenum);
1371 goto test_error;
1372 }
1373 }
1374 else if (!strcasecmp(token, "WITH-VALUE"))
1375 {
1376 if (!get_token(fp, token, sizeof(token), &linenum))
1377 {
1378 print_fatal_error("Missing WITH-VALUE value on line %d.", linenum);
1379 goto test_error;
1380 }
1381
1382 if (last_expect)
1383 {
1384 tokenptr = token + strlen(token) - 1;
1385 if (token[0] == '/' && tokenptr > token && *tokenptr == '/')
1386 {
1387 /*
1388 * WITH-VALUE is a POSIX extended regular expression.
1389 */
1390
1391 last_expect->with_value = calloc(1, tokenptr - token);
1392 last_expect->with_regex = 1;
1393
1394 if (last_expect->with_value)
1395 memcpy(last_expect->with_value, token + 1, tokenptr - token - 1);
1396 }
1397 else
1398 {
1399 /*
1400 * WITH-VALUE is a literal value...
1401 */
1402
1403 last_expect->with_value = strdup(token);
1404 }
1405 }
1406 else
1407 {
1408 print_fatal_error("WITH-VALUE without a preceding EXPECT on line %d.",
1409 linenum);
1410 goto test_error;
1411 }
1412 }
1413 else if (!strcasecmp(token, "DISPLAY"))
1414 {
1415 /*
1416 * Display attributes...
1417 */
1418
1419 if (num_displayed >= (int)(sizeof(displayed) / sizeof(displayed[0])))
1420 {
1421 print_fatal_error("Too many DISPLAY's on line %d", linenum);
1422 goto test_error;
1423 }
1424
1425 if (!get_token(fp, token, sizeof(token), &linenum))
1426 {
1427 print_fatal_error("Missing DISPLAY name on line %d.", linenum);
1428 goto test_error;
1429 }
1430
1431 displayed[num_displayed] = strdup(token);
1432 num_displayed ++;
1433 }
1434 else
1435 {
1436 print_fatal_error("Unexpected token %s seen on line %d.", token,
1437 linenum);
1438 goto test_error;
1439 }
1440 }
1441
1442 /*
1443 * Submit the IPP request...
1444 */
1445
1446 request->request.op.version[0] = version / 10;
1447 request->request.op.version[1] = version % 10;
1448 request->request.op.operation_id = op;
1449 request->request.op.request_id = request_id;
1450
1451 if (Output == _CUPS_OUTPUT_PLIST)
1452 {
1453 puts("<dict>");
1454 puts("<key>Name</key>");
1455 print_xml_string("string", name);
1456 puts("<key>Operation</key>");
1457 print_xml_string("string", ippOpString(op));
1458 puts("<key>RequestAttributes</key>");
1459 puts("<dict>");
1460 for (attrptr = request->attrs; attrptr; attrptr = attrptr->next)
1461 print_attr(attrptr);
1462 puts("</dict>");
1463 }
1464 else if (Output == _CUPS_OUTPUT_TEST)
1465 {
1466 if (Verbosity)
1467 {
1468 printf(" %s:\n", ippOpString(op));
1469
1470 for (attrptr = request->attrs; attrptr; attrptr = attrptr->next)
1471 print_attr(attrptr);
1472 }
1473
1474 printf(" %-69.69s [", name);
1475 fflush(stdout);
1476 }
1477
1478 if (transfer == _CUPS_TRANSFER_CHUNKED ||
1479 (transfer == _CUPS_TRANSFER_AUTO && filename[0]))
1480 {
1481 /*
1482 * Send request using chunking...
1483 */
1484
1485 http_status_t status = cupsSendRequest(http, request, resource, 0);
1486
1487 if (status == HTTP_CONTINUE && filename[0])
1488 {
1489 int fd; /* File to send */
1490 char buffer[8192]; /* Copy buffer */
1491 ssize_t bytes; /* Bytes read/written */
1492
1493 if ((fd = open(filename, O_RDONLY | O_BINARY)) >= 0)
1494 {
1495 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
1496 if ((status = cupsWriteRequestData(http, buffer,
1497 bytes)) != HTTP_CONTINUE)
1498 break;
1499 }
1500 else
1501 {
1502 snprintf(buffer, sizeof(buffer), "%s: %s", filename, strerror(errno));
1503 _cupsSetError(IPP_INTERNAL_ERROR, buffer, 0);
1504
1505 status = HTTP_ERROR;
1506 }
1507 }
1508
1509 ippDelete(request);
1510
1511 if (status == HTTP_CONTINUE)
1512 response = cupsGetResponse(http, resource);
1513 else
1514 response = NULL;
1515 }
1516 else if (filename[0])
1517 response = cupsDoFileRequest(http, request, resource, filename);
1518 else
1519 response = cupsDoRequest(http, request, resource);
1520
1521 request = NULL;
1522
1523 if (!response)
1524 pass = 0;
1525 else
1526 {
1527 if (http->version != HTTP_1_1)
1528 pass = 0;
1529
1530 if (response->request.status.version[0] != (version / 10) ||
1531 response->request.status.version[1] != (version % 10) ||
1532 response->request.status.request_id != request_id)
1533 pass = 0;
1534
1535 if ((attrptr = ippFindAttribute(response, "job-id",
1536 IPP_TAG_INTEGER)) != NULL)
1537 {
1538 snprintf(temp, sizeof(temp), "%d", attrptr->values[0].integer);
1539 set_variable(vars, "job-id", temp);
1540 }
1541
1542 if ((attrptr = ippFindAttribute(response, "job-uri",
1543 IPP_TAG_URI)) != NULL)
1544 set_variable(vars, "job-uri", attrptr->values[0].string.text);
1545
1546 if ((attrptr = ippFindAttribute(response, "notify-subscription-id",
1547 IPP_TAG_INTEGER)) != NULL)
1548 {
1549 snprintf(temp, sizeof(temp), "%d", attrptr->values[0].integer);
1550 set_variable(vars, "notify-subscription-id", temp);
1551 }
1552
1553 attrptr = response->attrs;
1554 if (!attrptr || !attrptr->name ||
1555 attrptr->value_tag != IPP_TAG_CHARSET ||
1556 attrptr->group_tag != IPP_TAG_OPERATION ||
1557 attrptr->num_values != 1 ||
1558 strcmp(attrptr->name, "attributes-charset"))
1559 pass = 0;
1560
1561 if (attrptr)
1562 {
1563 attrptr = attrptr->next;
1564 if (!attrptr || !attrptr->name ||
1565 attrptr->value_tag != IPP_TAG_LANGUAGE ||
1566 attrptr->group_tag != IPP_TAG_OPERATION ||
1567 attrptr->num_values != 1 ||
1568 strcmp(attrptr->name, "attributes-natural-language"))
1569 pass = 0;
1570 }
1571
1572 if ((attrptr = ippFindAttribute(response, "status-message",
1573 IPP_TAG_ZERO)) != NULL &&
1574 (attrptr->value_tag != IPP_TAG_TEXT ||
1575 attrptr->group_tag != IPP_TAG_OPERATION ||
1576 attrptr->num_values != 1 ||
1577 (attrptr->value_tag == IPP_TAG_TEXT &&
1578 strlen(attrptr->values[0].string.text) > 255)))
1579 pass = 0;
1580
1581 if ((attrptr = ippFindAttribute(response, "detailed-status-message",
1582 IPP_TAG_ZERO)) != NULL &&
1583 (attrptr->value_tag != IPP_TAG_TEXT ||
1584 attrptr->group_tag != IPP_TAG_OPERATION ||
1585 attrptr->num_values != 1 ||
1586 (attrptr->value_tag == IPP_TAG_TEXT &&
1587 strlen(attrptr->values[0].string.text) > 1023)))
1588 pass = 0;
1589
1590 for (attrptr = response->attrs, group = attrptr->group_tag;
1591 attrptr;
1592 attrptr = attrptr->next)
1593 {
1594 if (attrptr->group_tag < group && attrptr->group_tag != IPP_TAG_ZERO)
1595 {
1596 pass = 0;
1597 break;
1598 }
1599
1600 if (!validate_attr(attrptr, 0))
1601 {
1602 pass = 0;
1603 break;
1604 }
1605 }
1606
1607 for (i = 0; i < num_statuses; i ++)
1608 {
1609 if (statuses[i].if_defined &&
1610 !get_variable(vars, statuses[i].if_defined))
1611 continue;
1612
1613 if (statuses[i].if_undefined &&
1614 get_variable(vars, statuses[i].if_undefined))
1615 continue;
1616
1617 if (response->request.status.status_code == statuses[i].status)
1618 break;
1619 }
1620
1621 if (i == num_statuses && num_statuses > 0)
1622 pass = 0;
1623 else
1624 {
1625 for (i = num_expects, expect = expects; i > 0; i --, expect ++)
1626 {
1627 if (expect->if_defined && !get_variable(vars, expect->if_defined))
1628 continue;
1629
1630 if (expect->if_undefined && get_variable(vars, expect->if_undefined))
1631 continue;
1632
1633 found = ippFindAttribute(response, expect->name, IPP_TAG_ZERO);
1634
1635 if ((found && expect->not_expect) ||
1636 (!found && !(expect->not_expect || expect->optional)) ||
1637 (found && !expect_matches(expect, found->value_tag)) ||
1638 (found && expect->in_group &&
1639 found->group_tag != expect->in_group))
1640 {
1641 pass = 0;
1642 break;
1643 }
1644
1645 if (found &&
1646 !with_value(expect->with_value, expect->with_regex, found))
1647 {
1648 pass = 0;
1649 break;
1650 }
1651
1652 if (found && expect->count > 0 && found->num_values != expect->count)
1653 {
1654 pass = 0;
1655 break;
1656 }
1657
1658 if (found && expect->same_count_as)
1659 {
1660 attrptr = ippFindAttribute(response, expect->same_count_as,
1661 IPP_TAG_ZERO);
1662
1663 if (!attrptr || attrptr->num_values != found->num_values)
1664 {
1665 pass = 0;
1666 break;
1667 }
1668 }
1669 }
1670 }
1671 }
1672
1673 if (Output == _CUPS_OUTPUT_PLIST)
1674 {
1675 puts("<key>Successful</key>");
1676 puts(pass ? "<true />" : "<false />");
1677 puts("<key>StatusCode</key>");
1678 print_xml_string("string", ippErrorString(cupsLastError()));
1679 puts("<key>ResponseAttributes</key>");
1680 puts("<dict>");
1681 for (attrptr = response ? response->attrs : NULL;
1682 attrptr;
1683 attrptr = attrptr->next)
1684 print_attr(attrptr);
1685 puts("</dict>");
1686 }
1687 else if (Output == _CUPS_OUTPUT_TEST)
1688 {
1689 puts(pass ? "PASS]" : "FAIL]");
1690
1691 if (Verbosity && response)
1692 {
1693 printf(" RECEIVED: %lu bytes in response\n",
1694 (unsigned long)ippLength(response));
1695 printf(" status-code = %x (%s)\n", cupsLastError(),
1696 ippErrorString(cupsLastError()));
1697
1698 for (attrptr = response->attrs;
1699 attrptr != NULL;
1700 attrptr = attrptr->next)
1701 {
1702 print_attr(attrptr);
1703 }
1704 }
1705 }
1706 else if (!pass)
1707 fprintf(stderr, "%s\n", cupsLastErrorString());
1708
1709 if (pass && Output != _CUPS_OUTPUT_PLIST && Output != _CUPS_OUTPUT_QUIET &&
1710 !Verbosity && num_displayed > 0)
1711 {
1712 if (Output >= _CUPS_OUTPUT_LIST)
1713 {
1714 size_t width; /* Length of value */
1715
1716
1717 for (i = 0; i < num_displayed; i ++)
1718 {
1719 widths[i] = strlen(displayed[i]);
1720
1721 for (attrptr = ippFindAttribute(response, displayed[i], IPP_TAG_ZERO);
1722 attrptr;
1723 attrptr = ippFindNextAttribute(response, displayed[i],
1724 IPP_TAG_ZERO))
1725 {
1726 width = _ippAttrString(attrptr, NULL, 0);
1727 if (width > widths[i])
1728 widths[i] = width;
1729 }
1730 }
1731
1732 if (Output == _CUPS_OUTPUT_CSV)
1733 print_csv(NULL, num_displayed, displayed, widths);
1734 else
1735 print_line(NULL, num_displayed, displayed, widths);
1736
1737 attrptr = response->attrs;
1738
1739 while (attrptr)
1740 {
1741 while (attrptr && attrptr->group_tag <= IPP_TAG_OPERATION)
1742 attrptr = attrptr->next;
1743
1744 if (attrptr)
1745 {
1746 if (Output == _CUPS_OUTPUT_CSV)
1747 print_csv(attrptr, num_displayed, displayed, widths);
1748 else
1749 print_line(attrptr, num_displayed, displayed, widths);
1750
1751 while (attrptr && attrptr->group_tag > IPP_TAG_OPERATION)
1752 attrptr = attrptr->next;
1753 }
1754 }
1755 }
1756 else
1757 {
1758 for (attrptr = response->attrs;
1759 attrptr != NULL;
1760 attrptr = attrptr->next)
1761 {
1762 if (attrptr->name)
1763 {
1764 for (i = 0; i < num_displayed; i ++)
1765 {
1766 if (!strcmp(displayed[i], attrptr->name))
1767 {
1768 print_attr(attrptr);
1769 break;
1770 }
1771 }
1772 }
1773 }
1774 }
1775 }
1776 else if (!pass)
1777 {
1778 if (Output == _CUPS_OUTPUT_PLIST)
1779 {
1780 puts("<key>Errors</key>");
1781 puts("<array>");
1782 }
1783
1784 if (http->version != HTTP_1_1)
1785 print_test_error("Bad HTTP version (%d.%d)", http->version / 100,
1786 http->version % 100);
1787
1788 if (!response)
1789 print_test_error("IPP request failed with status %s (%s)",
1790 ippErrorString(cupsLastError()),
1791 cupsLastErrorString());
1792 else
1793 {
1794 if (response->request.status.version[0] != (version / 10) ||
1795 response->request.status.version[1] != (version % 10))
1796 print_test_error("Bad version %d.%d in response - expected %d.%d "
1797 "(RFC 2911 section 3.1.8).",
1798 response->request.status.version[0],
1799 response->request.status.version[1],
1800 version / 10, version % 10);
1801
1802 if (response->request.status.request_id != request_id)
1803 print_test_error("Bad request ID %d in response - expected %d "
1804 "(RFC 2911 section 3.1.1)",
1805 response->request.status.request_id, request_id);
1806
1807 attrptr = response->attrs;
1808 if (!attrptr)
1809 print_test_error("Missing first attribute \"attributes-charset "
1810 "(charset)\" in group operation-attributes-tag "
1811 "(RFC 2911 section 3.1.4).");
1812 else
1813 {
1814 if (!attrptr->name ||
1815 attrptr->value_tag != IPP_TAG_CHARSET ||
1816 attrptr->group_tag != IPP_TAG_OPERATION ||
1817 attrptr->num_values != 1 ||
1818 strcmp(attrptr->name, "attributes-charset"))
1819 print_test_error("Bad first attribute \"%s (%s%s)\" in group %s, "
1820 "expected \"attributes-charset (charset)\" in "
1821 "group operation-attributes-tag (RFC 2911 section "
1822 "3.1.4).",
1823 attrptr->name ? attrptr->name : "(null)",
1824 attrptr->num_values > 1 ? "1setOf " : "",
1825 ippTagString(attrptr->value_tag),
1826 ippTagString(attrptr->group_tag));
1827
1828 attrptr = attrptr->next;
1829 if (!attrptr)
1830 print_test_error("Missing second attribute \"attributes-natural-"
1831 "language (naturalLanguage)\" in group "
1832 "operation-attributes-tag (RFC 2911 section "
1833 "3.1.4).");
1834 else if (!attrptr->name ||
1835 attrptr->value_tag != IPP_TAG_LANGUAGE ||
1836 attrptr->group_tag != IPP_TAG_OPERATION ||
1837 attrptr->num_values != 1 ||
1838 strcmp(attrptr->name, "attributes-natural-language"))
1839 print_test_error("Bad first attribute \"%s (%s%s)\" in group %s, "
1840 "expected \"attributes-natural-language "
1841 "(naturalLanguage)\" in group "
1842 "operation-attributes-tag (RFC 2911 section "
1843 "3.1.4).",
1844 attrptr->name ? attrptr->name : "(null)",
1845 attrptr->num_values > 1 ? "1setOf " : "",
1846 ippTagString(attrptr->value_tag),
1847 ippTagString(attrptr->group_tag));
1848 }
1849
1850 if ((attrptr = ippFindAttribute(response, "status-message",
1851 IPP_TAG_ZERO)) != NULL)
1852 {
1853 if (attrptr->value_tag != IPP_TAG_TEXT)
1854 print_test_error("status-message (text(255)) has wrong value tag "
1855 "%s (RFC 2911 section 3.1.6.2).",
1856 ippTagString(attrptr->value_tag));
1857 if (attrptr->group_tag != IPP_TAG_OPERATION)
1858 print_test_error("status-message (text(255)) has wrong group tag "
1859 "%s (RFC 2911 section 3.1.6.2).",
1860 ippTagString(attrptr->group_tag));
1861 if (attrptr->num_values != 1)
1862 print_test_error("status-message (text(255)) has %d values "
1863 "(RFC 2911 section 3.1.6.2).",
1864 attrptr->num_values);
1865 if (attrptr->value_tag == IPP_TAG_TEXT &&
1866 strlen(attrptr->values[0].string.text) > 255)
1867 print_test_error("status-message (text(255)) has bad length %d"
1868 " (RFC 2911 section 3.1.6.2).",
1869 (int)strlen(attrptr->values[0].string.text));
1870 }
1871
1872 if ((attrptr = ippFindAttribute(response, "detailed-status-message",
1873 IPP_TAG_ZERO)) != NULL)
1874 {
1875 if (attrptr->value_tag != IPP_TAG_TEXT)
1876 print_test_error("detailed-status-message (text(MAX)) has wrong "
1877 "value tag %s (RFC 2911 section 3.1.6.3).",
1878 ippTagString(attrptr->value_tag));
1879 if (attrptr->group_tag != IPP_TAG_OPERATION)
1880 print_test_error("detailed-status-message (text(MAX)) has wrong "
1881 "group tag %s (RFC 2911 section 3.1.6.3).",
1882 ippTagString(attrptr->group_tag));
1883 if (attrptr->num_values != 1)
1884 print_test_error("detailed-status-message (text(MAX)) has %d values"
1885 " (RFC 2911 section 3.1.6.3).",
1886 attrptr->num_values);
1887 if (attrptr->value_tag == IPP_TAG_TEXT &&
1888 strlen(attrptr->values[0].string.text) > 1023)
1889 print_test_error("detailed-status-message (text(MAX)) has bad "
1890 "length %d (RFC 2911 section 3.1.6.3).",
1891 (int)strlen(attrptr->values[0].string.text));
1892 }
1893
1894 for (attrptr = response->attrs, group = attrptr->group_tag;
1895 attrptr;
1896 attrptr = attrptr->next)
1897 {
1898 if (attrptr->group_tag < group && attrptr->group_tag != IPP_TAG_ZERO)
1899 print_test_error("Attribute groups out of order (%s < %s)",
1900 ippTagString(attrptr->group_tag),
1901 ippTagString(group));
1902
1903 validate_attr(attrptr, 1);
1904 }
1905
1906 for (i = 0; i < num_statuses; i ++)
1907 {
1908 if (statuses[i].if_defined &&
1909 !get_variable(vars, statuses[i].if_defined))
1910 continue;
1911
1912 if (statuses[i].if_undefined &&
1913 get_variable(vars, statuses[i].if_undefined))
1914 continue;
1915
1916 if (response->request.status.status_code == statuses[i].status)
1917 break;
1918 }
1919
1920 if (i == num_statuses && num_statuses > 0)
1921 {
1922 print_test_error("Bad status-code (%s)",
1923 ippErrorString(cupsLastError()));
1924 print_test_error("status-message=\"%s\"", cupsLastErrorString());
1925 }
1926
1927 for (i = num_expects, expect = expects; i > 0; i --, expect ++)
1928 {
1929 if (expect->if_defined && !get_variable(vars, expect->if_defined))
1930 continue;
1931
1932 if (expect->if_undefined && get_variable(vars, expect->if_undefined))
1933 continue;
1934
1935 found = ippFindAttribute(response, expect->name, IPP_TAG_ZERO);
1936
1937 if (found && expect->not_expect)
1938 print_test_error("NOT EXPECTED: %s", expect->name);
1939 else if (!found && !(expect->not_expect || expect->optional))
1940 print_test_error("EXPECTED: %s", expect->name);
1941 else if (found)
1942 {
1943 if (!expect_matches(expect, found->value_tag))
1944 print_test_error("EXPECTED: %s OF-TYPE %s (got %s)",
1945 expect->name, expect->of_type,
1946 ippTagString(found->value_tag));
1947
1948 if (expect->in_group && found->group_tag != expect->in_group)
1949 print_test_error("EXPECTED: %s IN-GROUP %s (got %s).",
1950 expect->name, ippTagString(expect->in_group),
1951 ippTagString(found->group_tag));
1952
1953 if (!with_value(expect->with_value, expect->with_regex, found))
1954 {
1955 if (expect->with_regex)
1956 print_test_error("EXPECTED: %s WITH-VALUE /%s/",
1957 expect->name, expect->with_value);
1958 else
1959 print_test_error("EXPECTED: %s WITH-VALUE \"%s\"",
1960 expect->name, expect->with_value);
1961 }
1962
1963 if (expect->count > 0 && found->num_values != expect->count)
1964 {
1965 print_test_error("EXPECTED: %s COUNT %d (got %d)", expect->name,
1966 expect->count, found->num_values);
1967 }
1968
1969 if (expect->same_count_as)
1970 {
1971 attrptr = ippFindAttribute(response, expect->same_count_as,
1972 IPP_TAG_ZERO);
1973
1974 if (!attrptr)
1975 print_test_error("EXPECTED: %s (%d values) SAME-COUNT-AS %s "
1976 "(not returned)", expect->name,
1977 found->num_values, expect->same_count_as);
1978 else if (attrptr->num_values != found->num_values)
1979 print_test_error("EXPECTED: %s (%d values) SAME-COUNT-AS %s "
1980 "(%d values)", expect->name, found->num_values,
1981 expect->same_count_as, attrptr->num_values);
1982 }
1983 }
1984 }
1985 }
1986
1987 if (Output == _CUPS_OUTPUT_PLIST)
1988 puts("</array>");
1989 }
1990
1991 if (Output == _CUPS_OUTPUT_PLIST)
1992 puts("</dict>");
1993
1994 ippDelete(response);
1995 response = NULL;
1996
1997 for (i = 0; i < num_statuses; i ++)
1998 {
1999 if (statuses[i].if_defined)
2000 free(statuses[i].if_defined);
2001 if (statuses[i].if_undefined)
2002 free(statuses[i].if_undefined);
2003 }
2004 num_statuses = 0;
2005
2006 for (i = num_expects, expect = expects; i > 0; i --, expect ++)
2007 {
2008 free(expect->name);
2009 if (expect->of_type)
2010 free(expect->of_type);
2011 if (expect->same_count_as)
2012 free(expect->same_count_as);
2013 if (expect->if_defined)
2014 free(expect->if_defined);
2015 if (expect->if_undefined)
2016 free(expect->if_undefined);
2017 if (expect->with_value)
2018 free(expect->with_value);
2019 }
2020 num_expects = 0;
2021
2022 for (i = 0; i < num_displayed; i ++)
2023 free(displayed[i]);
2024 num_displayed = 0;
2025
2026 if (!pass)
2027 break;
2028 }
2029
2030 fclose(fp);
2031 httpClose(http);
2032
2033 return (pass);
2034
2035 /*
2036 * If we get here there was a fatal test error...
2037 */
2038
2039 test_error:
2040
2041 if (fp)
2042 fclose(fp);
2043
2044 httpClose(http);
2045 ippDelete(request);
2046 ippDelete(response);
2047
2048 for (i = 0; i < num_statuses; i ++)
2049 {
2050 if (statuses[i].if_defined)
2051 free(statuses[i].if_defined);
2052 if (statuses[i].if_undefined)
2053 free(statuses[i].if_undefined);
2054 }
2055
2056 for (i = num_expects, expect = expects; i > 0; i --, expect ++)
2057 {
2058 free(expect->name);
2059 if (expect->of_type)
2060 free(expect->of_type);
2061 if (expect->same_count_as)
2062 free(expect->same_count_as);
2063 if (expect->if_defined)
2064 free(expect->if_defined);
2065 if (expect->if_undefined)
2066 free(expect->if_undefined);
2067 if (expect->with_value)
2068 free(expect->with_value);
2069 }
2070
2071 for (i = 0; i < num_displayed; i ++)
2072 free(displayed[i]);
2073
2074 return (0);
2075 }
2076
2077
2078 /*
2079 * 'expand_variables()' - Expand variables in a string.
2080 */
2081
2082 static void
2083 expand_variables(_cups_vars_t *vars, /* I - Variables */
2084 char *dst, /* I - Destination string buffer */
2085 const char *src, /* I - Source string */
2086 size_t dstsize) /* I - Size of destination buffer */
2087 {
2088 char *dstptr, /* Pointer into destination */
2089 *dstend, /* End of destination */
2090 temp[256], /* Temporary string */
2091 *tempptr; /* Pointer into temporary string */
2092 const char *value; /* Value to substitute */
2093
2094
2095 dstptr = dst;
2096 dstend = dst + dstsize - 1;
2097
2098 while (*src && dstptr < dstend)
2099 {
2100 if (*src == '$')
2101 {
2102 /*
2103 * Substitute a string/number...
2104 */
2105
2106 if (!strncmp(src, "$$", 2))
2107 {
2108 value = "$";
2109 src += 2;
2110 }
2111 else if (!strncmp(src, "$ENV[", 5))
2112 {
2113 strlcpy(temp, src + 5, sizeof(temp));
2114
2115 for (tempptr = temp; *tempptr; tempptr ++)
2116 if (*tempptr == ']')
2117 break;
2118
2119 if (*tempptr)
2120 *tempptr++ = '\0';
2121
2122 value = getenv(temp);
2123 src += tempptr - temp + 5;
2124 }
2125 else if (vars)
2126 {
2127 strlcpy(temp, src + 1, sizeof(temp));
2128
2129 for (tempptr = temp; *tempptr; tempptr ++)
2130 if (!isalnum(*tempptr & 255) && *tempptr != '-' && *tempptr != '_')
2131 break;
2132
2133 if (*tempptr)
2134 *tempptr = '\0';
2135
2136 if (!strcmp(temp, "uri"))
2137 value = vars->uri;
2138 else if (!strcmp(temp, "filename"))
2139 value = vars->filename;
2140 else if (!strcmp(temp, "scheme") || !strcmp(temp, "method"))
2141 value = vars->scheme;
2142 else if (!strcmp(temp, "username"))
2143 value = vars->userpass;
2144 else if (!strcmp(temp, "hostname"))
2145 value = vars->hostname;
2146 else if (!strcmp(temp, "port"))
2147 {
2148 snprintf(temp, sizeof(temp), "%d", vars->port);
2149 value = temp;
2150 }
2151 else if (!strcmp(temp, "resource"))
2152 value = vars->resource;
2153 else if (!strcmp(temp, "user"))
2154 value = cupsUser();
2155 else
2156 value = get_variable(vars, temp);
2157
2158 src += tempptr - temp + 1;
2159 }
2160 else
2161 {
2162 value = "$";
2163 src ++;
2164 }
2165
2166 if (value)
2167 {
2168 strlcpy(dstptr, value, dstend - dstptr + 1);
2169 dstptr += strlen(dstptr);
2170 }
2171 }
2172 else
2173 *dstptr++ = *src++;
2174 }
2175
2176 *dstptr = '\0';
2177 }
2178
2179
2180 /*
2181 * 'expect_matches()' - Return true if the tag matches the specification.
2182 */
2183
2184 static int /* O - 1 if matches, 0 otherwise */
2185 expect_matches(
2186 _cups_expect_t *expect, /* I - Expected attribute */
2187 ipp_tag_t value_tag) /* I - Value tag for attribute */
2188 {
2189 int match; /* Match? */
2190 char *of_type, /* Type name to match */
2191 *next, /* Next name to match */
2192 sep; /* Separator character */
2193
2194
2195 /*
2196 * If we don't expect a particular type, return immediately...
2197 */
2198
2199 if (!expect->of_type)
2200 return (1);
2201
2202 /*
2203 * Parse the "of_type" value since the string can contain multiple attribute
2204 * types separated by "," or "|"...
2205 */
2206
2207 for (of_type = expect->of_type, match = 0; !match && *of_type; of_type = next)
2208 {
2209 /*
2210 * Find the next separator, and set it (temporarily) to nul if present.
2211 */
2212
2213 for (next = of_type; *next && *next != '|' && *next != ','; next ++);
2214
2215 if ((sep = *next) != '\0')
2216 *next = '\0';
2217
2218 /*
2219 * Support some meta-types to make it easier to write the test file.
2220 */
2221
2222 if (!strcmp(of_type, "text"))
2223 match = value_tag == IPP_TAG_TEXTLANG || value_tag == IPP_TAG_TEXT;
2224 else if (!strcmp(of_type, "name"))
2225 match = value_tag == IPP_TAG_NAMELANG || value_tag == IPP_TAG_NAME;
2226 else if (!strcmp(of_type, "collection"))
2227 match = value_tag == IPP_TAG_BEGIN_COLLECTION;
2228 else
2229 match = value_tag == ippTagValue(of_type);
2230
2231 /*
2232 * Restore the separator if we have one...
2233 */
2234
2235 if (sep)
2236 *next++ = sep;
2237 }
2238
2239 return (match);
2240 }
2241
2242
2243 /*
2244 * 'get_collection()' - Get a collection value from the current test file.
2245 */
2246
2247 static ipp_t * /* O - Collection value */
2248 get_collection(_cups_vars_t *vars, /* I - Variables */
2249 FILE *fp, /* I - File to read from */
2250 int *linenum) /* IO - Line number */
2251 {
2252 char token[1024], /* Token from file */
2253 temp[1024], /* Temporary string */
2254 attr[128]; /* Attribute name */
2255 ipp_tag_t value; /* Current value type */
2256 ipp_t *col = ippNew(); /* Collection value */
2257 ipp_attribute_t *lastcol = NULL; /* Last collection attribute */
2258
2259
2260 while (get_token(fp, token, sizeof(token), linenum) != NULL)
2261 {
2262 if (!strcmp(token, "}"))
2263 break;
2264 else if (!strcmp(token, "{") && lastcol)
2265 {
2266 /*
2267 * Another collection value
2268 */
2269
2270 ipp_t *subcol = get_collection(vars, fp, linenum);
2271 /* Collection value */
2272
2273 if (subcol)
2274 {
2275 ipp_attribute_t *tempcol; /* Pointer to new buffer */
2276
2277
2278 /*
2279 * Reallocate memory...
2280 */
2281
2282 if ((tempcol = realloc(lastcol, sizeof(ipp_attribute_t) +
2283 (lastcol->num_values + 1) *
2284 sizeof(ipp_value_t))) == NULL)
2285 {
2286 print_fatal_error("Unable to allocate memory on line %d.", *linenum);
2287 goto col_error;
2288 }
2289
2290 if (tempcol != lastcol)
2291 {
2292 /*
2293 * Reset pointers in the list...
2294 */
2295
2296 if (col->prev)
2297 col->prev->next = tempcol;
2298 else
2299 col->attrs = tempcol;
2300
2301 lastcol = col->current = col->last = tempcol;
2302 }
2303
2304 lastcol->values[lastcol->num_values].collection = subcol;
2305 lastcol->num_values ++;
2306 }
2307 else
2308 goto col_error;
2309 }
2310 else if (!strcasecmp(token, "MEMBER"))
2311 {
2312 /*
2313 * Attribute...
2314 */
2315
2316 lastcol = NULL;
2317
2318 if (!get_token(fp, token, sizeof(token), linenum))
2319 {
2320 print_fatal_error("Missing MEMBER value tag on line %d.", *linenum);
2321 goto col_error;
2322 }
2323
2324 if ((value = ippTagValue(token)) < 0)
2325 {
2326 print_fatal_error("Bad MEMBER value tag \"%s\" on line %d.", token,
2327 *linenum);
2328 goto col_error;
2329 }
2330
2331 if (!get_token(fp, attr, sizeof(attr), linenum))
2332 {
2333 print_fatal_error("Missing MEMBER name on line %d.", *linenum);
2334 goto col_error;
2335 }
2336
2337 if (!get_token(fp, temp, sizeof(temp), linenum))
2338 {
2339 print_fatal_error("Missing MEMBER value on line %d.", *linenum);
2340 goto col_error;
2341 }
2342
2343 expand_variables(vars, token, temp, sizeof(token));
2344
2345 switch (value)
2346 {
2347 case IPP_TAG_BOOLEAN :
2348 if (!strcasecmp(token, "true"))
2349 ippAddBoolean(col, IPP_TAG_ZERO, attr, 1);
2350 else
2351 ippAddBoolean(col, IPP_TAG_ZERO, attr, atoi(token));
2352 break;
2353
2354 case IPP_TAG_INTEGER :
2355 case IPP_TAG_ENUM :
2356 ippAddInteger(col, IPP_TAG_ZERO, value, attr, atoi(token));
2357 break;
2358
2359 case IPP_TAG_RESOLUTION :
2360 {
2361 int xres, /* X resolution */
2362 yres; /* Y resolution */
2363 char units[6]; /* Units */
2364
2365 if (sscanf(token, "%dx%d%5s", &xres, &yres, units) != 3 ||
2366 (strcasecmp(units, "dpi") && strcasecmp(units, "dpc") &&
2367 strcasecmp(units, "other")))
2368 {
2369 print_fatal_error("Bad resolution value \"%s\" on line %d.",
2370 token, *linenum);
2371 goto col_error;
2372 }
2373
2374 if (!strcasecmp(units, "dpi"))
2375 ippAddResolution(col, IPP_TAG_ZERO, attr, xres, yres,
2376 IPP_RES_PER_INCH);
2377 else if (!strcasecmp(units, "dpc"))
2378 ippAddResolution(col, IPP_TAG_ZERO, attr, xres, yres,
2379 IPP_RES_PER_CM);
2380 else
2381 ippAddResolution(col, IPP_TAG_ZERO, attr, xres, yres,
2382 (ipp_res_t)0);
2383 }
2384 break;
2385
2386 case IPP_TAG_RANGE :
2387 {
2388 int lowers[4], /* Lower value */
2389 uppers[4], /* Upper values */
2390 num_vals; /* Number of values */
2391
2392
2393 num_vals = sscanf(token, "%d-%d,%d-%d,%d-%d,%d-%d",
2394 lowers + 0, uppers + 0,
2395 lowers + 1, uppers + 1,
2396 lowers + 2, uppers + 2,
2397 lowers + 3, uppers + 3);
2398
2399 if ((num_vals & 1) || num_vals == 0)
2400 {
2401 print_fatal_error("Bad rangeOfInteger value \"%s\" on line %d.",
2402 token, *linenum);
2403 goto col_error;
2404 }
2405
2406 ippAddRanges(col, IPP_TAG_ZERO, attr, num_vals / 2, lowers,
2407 uppers);
2408 }
2409 break;
2410
2411 case IPP_TAG_BEGIN_COLLECTION :
2412 if (!strcmp(token, "{"))
2413 {
2414 ipp_t *subcol = get_collection(vars, fp, linenum);
2415 /* Collection value */
2416
2417 if (subcol)
2418 {
2419 lastcol = ippAddCollection(col, IPP_TAG_ZERO, attr, subcol);
2420 ippDelete(subcol);
2421 }
2422 else
2423 goto col_error;
2424 }
2425 else
2426 {
2427 print_fatal_error("Bad collection value on line %d.", *linenum);
2428 goto col_error;
2429 }
2430 break;
2431
2432 default :
2433 if (!strchr(token, ','))
2434 ippAddString(col, IPP_TAG_ZERO, value, attr, NULL, token);
2435 else
2436 {
2437 /*
2438 * Multiple string values...
2439 */
2440
2441 int num_values; /* Number of values */
2442 char *values[100], /* Values */
2443 *ptr; /* Pointer to next value */
2444
2445
2446 values[0] = token;
2447 num_values = 1;
2448
2449 for (ptr = strchr(token, ','); ptr; ptr = strchr(ptr, ','))
2450 {
2451 *ptr++ = '\0';
2452 values[num_values] = ptr;
2453 num_values ++;
2454 }
2455
2456 ippAddStrings(col, IPP_TAG_ZERO, value, attr, num_values,
2457 NULL, (const char **)values);
2458 }
2459 break;
2460 }
2461 }
2462 }
2463
2464 return (col);
2465
2466 /*
2467 * If we get here there was a parse error; free memory and return.
2468 */
2469
2470 col_error:
2471
2472 ippDelete(col);
2473
2474 return (NULL);
2475 }
2476
2477
2478 /*
2479 * 'get_filename()' - Get a filename based on the current test file.
2480 */
2481
2482 static char * /* O - Filename */
2483 get_filename(const char *testfile, /* I - Current test file */
2484 char *dst, /* I - Destination filename */
2485 const char *src, /* I - Source filename */
2486 size_t dstsize) /* I - Size of destination buffer */
2487 {
2488 char *dstptr; /* Pointer into destination */
2489 _cups_globals_t *cg = _cupsGlobals();
2490 /* Global data */
2491
2492
2493 if (*src == '<' && src[strlen(src) - 1] == '>')
2494 {
2495 /*
2496 * Map <filename> to CUPS_DATADIR/ipptool/filename...
2497 */
2498
2499 snprintf(dst, dstsize, "%s/ipptool/%s", cg->cups_datadir, src + 1);
2500 dstptr = dst + strlen(dst) - 1;
2501 if (*dstptr == '>')
2502 *dstptr = '\0';
2503 }
2504 else if (*src == '/' || !strchr(testfile, '/'))
2505 {
2506 /*
2507 * Use the path as-is...
2508 */
2509
2510 strlcpy(dst, src, dstsize);
2511 }
2512 else
2513 {
2514 /*
2515 * Make path relative to testfile...
2516 */
2517
2518 strlcpy(dst, testfile, dstsize);
2519 if ((dstptr = strrchr(dst, '/')) != NULL)
2520 dstptr ++;
2521 else
2522 dstptr = dst; /* Should never happen */
2523
2524 strlcpy(dstptr, src, dstsize - (dstptr - dst));
2525 }
2526
2527 return (dst);
2528 }
2529
2530
2531 /*
2532 * 'get_token()' - Get a token from a file.
2533 */
2534
2535 static char * /* O - Token from file or NULL on EOF */
2536 get_token(FILE *fp, /* I - File to read from */
2537 char *buf, /* I - Buffer to read into */
2538 int buflen, /* I - Length of buffer */
2539 int *linenum) /* IO - Current line number */
2540 {
2541 int ch, /* Character from file */
2542 quote; /* Quoting character */
2543 char *bufptr, /* Pointer into buffer */
2544 *bufend; /* End of buffer */
2545
2546
2547 for (;;)
2548 {
2549 /*
2550 * Skip whitespace...
2551 */
2552
2553 while (isspace(ch = getc(fp)))
2554 {
2555 if (ch == '\n')
2556 (*linenum) ++;
2557 }
2558
2559 /*
2560 * Read a token...
2561 */
2562
2563 if (ch == EOF)
2564 return (NULL);
2565 else if (ch == '\'' || ch == '\"')
2566 {
2567 /*
2568 * Quoted text or regular expression...
2569 */
2570
2571 quote = ch;
2572 bufptr = buf;
2573 bufend = buf + buflen - 1;
2574
2575 while ((ch = getc(fp)) != EOF)
2576 {
2577 if (ch == '\\')
2578 {
2579 /*
2580 * Escape next character...
2581 */
2582
2583 if (bufptr < bufend)
2584 *bufptr++ = ch;
2585
2586 if ((ch = getc(fp)) != EOF && bufptr < bufend)
2587 *bufptr++ = ch;
2588 }
2589 else if (ch == quote)
2590 break;
2591 else if (bufptr < bufend)
2592 *bufptr++ = ch;
2593 }
2594
2595 *bufptr = '\0';
2596
2597 return (buf);
2598 }
2599 else if (ch == '#')
2600 {
2601 /*
2602 * Comment...
2603 */
2604
2605 while ((ch = getc(fp)) != EOF)
2606 if (ch == '\n')
2607 break;
2608
2609 (*linenum) ++;
2610 }
2611 else
2612 {
2613 /*
2614 * Whitespace delimited text...
2615 */
2616
2617 ungetc(ch, fp);
2618
2619 bufptr = buf;
2620 bufend = buf + buflen - 1;
2621
2622 while ((ch = getc(fp)) != EOF)
2623 if (isspace(ch) || ch == '#')
2624 break;
2625 else if (bufptr < bufend)
2626 *bufptr++ = ch;
2627
2628 if (ch == '#')
2629 ungetc(ch, fp);
2630 else if (ch == '\n')
2631 (*linenum) ++;
2632
2633 *bufptr = '\0';
2634
2635 return (buf);
2636 }
2637 }
2638 }
2639
2640
2641 /*
2642 * 'get_variable()' - Get the value of a variable.
2643 */
2644
2645 static char * /* O - Value or NULL */
2646 get_variable(_cups_vars_t *vars, /* I - Variables */
2647 const char *name) /* I - Variable name */
2648 {
2649 _cups_var_t key, /* Search key */
2650 *match; /* Matching variable, if any */
2651
2652
2653 key.name = (char *)name;
2654 match = cupsArrayFind(vars->vars, &key);
2655
2656 return (match ? match->value : NULL);
2657 }
2658
2659
2660 /*
2661 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
2662 * value.
2663 */
2664
2665 static char * /* O - ISO 8601 date/time string */
2666 iso_date(ipp_uchar_t *date) /* I - IPP (RFC 1903) date/time value */
2667 {
2668 unsigned year = (date[0] << 8) + date[1];
2669 /* Year */
2670 static char buffer[255]; /* String buffer */
2671
2672
2673 if (date[9] == 0 && date[10] == 0)
2674 snprintf(buffer, sizeof(buffer), "%04u-%02u-%02uT%02u:%02u:%02uZ",
2675 year, date[2], date[3], date[4], date[5], date[6]);
2676 else
2677 snprintf(buffer, sizeof(buffer), "%04u-%02u-%02uT%02u:%02u:%02u%c%02u%02u",
2678 year, date[2], date[3], date[4], date[5], date[6],
2679 date[8], date[9], date[10]);
2680
2681 return (buffer);
2682 }
2683
2684
2685 /*
2686 * 'print_attr()' - Print an attribute on the screen.
2687 */
2688
2689 static void
2690 print_attr(ipp_attribute_t *attr) /* I - Attribute to print */
2691 {
2692 int i; /* Looping var */
2693 ipp_attribute_t *colattr; /* Collection attribute */
2694
2695
2696 if (Output == _CUPS_OUTPUT_PLIST)
2697 {
2698 if (!attr->name)
2699 {
2700 printf("<key>%s</key>\n<true />\n", ippTagString(attr->group_tag));
2701 return;
2702 }
2703
2704 print_xml_string("key", attr->name);
2705 if (attr->num_values > 1)
2706 puts("<array>");
2707 }
2708 else if (Output == _CUPS_OUTPUT_TEST)
2709 {
2710 if (!attr->name)
2711 {
2712 puts(" -- separator --");
2713 return;
2714 }
2715
2716 printf(" %s (%s%s) = ", attr->name,
2717 attr->num_values > 1 ? "1setOf " : "",
2718 ippTagString(attr->value_tag));
2719 }
2720
2721 switch (attr->value_tag)
2722 {
2723 case IPP_TAG_INTEGER :
2724 case IPP_TAG_ENUM :
2725 for (i = 0; i < attr->num_values; i ++)
2726 if (Output == _CUPS_OUTPUT_PLIST)
2727 printf("<integer>%d</integer>\n", attr->values[i].integer);
2728 else
2729 printf("%d ", attr->values[i].integer);
2730 break;
2731
2732 case IPP_TAG_BOOLEAN :
2733 for (i = 0; i < attr->num_values; i ++)
2734 if (Output == _CUPS_OUTPUT_PLIST)
2735 puts(attr->values[i].boolean ? "<true />" : "<false />");
2736 else if (attr->values[i].boolean)
2737 fputs("true ", stdout);
2738 else
2739 fputs("false ", stdout);
2740 break;
2741
2742 case IPP_TAG_RANGE :
2743 for (i = 0; i < attr->num_values; i ++)
2744 if (Output == _CUPS_OUTPUT_PLIST)
2745 printf("<dict><key>lower</key><integer>%d</integer>"
2746 "<key>upper</key><integer>%d</integer></dict>\n",
2747 attr->values[i].range.lower, attr->values[i].range.upper);
2748 else
2749 printf("%d-%d ", attr->values[i].range.lower,
2750 attr->values[i].range.upper);
2751 break;
2752
2753 case IPP_TAG_RESOLUTION :
2754 for (i = 0; i < attr->num_values; i ++)
2755 if (Output == _CUPS_OUTPUT_PLIST)
2756 printf("<dict><key>xres</key><integer>%d</integer>"
2757 "<key>yres</key><integer>%d</integer>"
2758 "<key>units</key><string>%s</string></dict>\n",
2759 attr->values[i].resolution.xres,
2760 attr->values[i].resolution.yres,
2761 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
2762 "dpi" : "dpc");
2763 else
2764 printf("%dx%d%s ", attr->values[i].resolution.xres,
2765 attr->values[i].resolution.yres,
2766 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
2767 "dpi" : "dpc");
2768 break;
2769
2770 case IPP_TAG_DATE :
2771 for (i = 0; i < attr->num_values; i ++)
2772 if (Output == _CUPS_OUTPUT_PLIST)
2773 printf("<date>%s</date>\n", iso_date(attr->values[i].date));
2774 else
2775 printf("%s ", iso_date(attr->values[i].date));
2776 break;
2777
2778 case IPP_TAG_STRING :
2779 case IPP_TAG_TEXT :
2780 case IPP_TAG_NAME :
2781 case IPP_TAG_KEYWORD :
2782 case IPP_TAG_CHARSET :
2783 case IPP_TAG_URI :
2784 case IPP_TAG_MIMETYPE :
2785 case IPP_TAG_LANGUAGE :
2786 for (i = 0; i < attr->num_values; i ++)
2787 if (Output == _CUPS_OUTPUT_PLIST)
2788 print_xml_string("string", attr->values[i].string.text);
2789 else
2790 printf("\"%s\" ", attr->values[i].string.text);
2791 break;
2792
2793 case IPP_TAG_TEXTLANG :
2794 case IPP_TAG_NAMELANG :
2795 for (i = 0; i < attr->num_values; i ++)
2796 if (Output == _CUPS_OUTPUT_PLIST)
2797 {
2798 fputs("<dict><key>language</key><string>", stdout);
2799 print_xml_string(NULL, attr->values[i].string.charset);
2800 fputs("</string><key>string</key><string>", stdout);
2801 print_xml_string(NULL, attr->values[i].string.text);
2802 puts("</string></dict>");
2803 }
2804 else
2805 printf("\"%s\",%s ", attr->values[i].string.text,
2806 attr->values[i].string.charset);
2807 break;
2808
2809 case IPP_TAG_BEGIN_COLLECTION :
2810 for (i = 0; i < attr->num_values; i ++)
2811 {
2812 if (Output == _CUPS_OUTPUT_PLIST)
2813 {
2814 puts("<dict>");
2815 for (colattr = attr->values[i].collection->attrs;
2816 colattr;
2817 colattr = colattr->next)
2818 print_attr(colattr);
2819 puts("</dict>");
2820 }
2821 else
2822 {
2823 if (i)
2824 putchar(' ');
2825
2826 print_col(attr->values[i].collection);
2827 }
2828 }
2829 break;
2830
2831 default :
2832 if (Output == _CUPS_OUTPUT_PLIST)
2833 printf("<string>&lt;&lt;%s&gt;&gt;</string>\n",
2834 ippTagString(attr->value_tag));
2835 else
2836 fputs(ippTagString(attr->value_tag), stdout);
2837 break;
2838 }
2839
2840 if (Output == _CUPS_OUTPUT_PLIST)
2841 {
2842 if (attr->num_values > 1)
2843 puts("</array>");
2844 }
2845 else
2846 putchar('\n');
2847 }
2848
2849
2850 /*
2851 * 'print_col()' - Print a collection attribute on the screen.
2852 */
2853
2854 static void
2855 print_col(ipp_t *col) /* I - Collection attribute to print */
2856 {
2857 int i; /* Looping var */
2858 ipp_attribute_t *attr; /* Current attribute in collection */
2859
2860
2861 fputs("{ ", stdout);
2862 for (attr = col->attrs; attr; attr = attr->next)
2863 {
2864 printf("%s (%s%s) = ", attr->name, attr->num_values > 1 ? "1setOf " : "",
2865 ippTagString(attr->value_tag));
2866
2867 switch (attr->value_tag)
2868 {
2869 case IPP_TAG_INTEGER :
2870 case IPP_TAG_ENUM :
2871 for (i = 0; i < attr->num_values; i ++)
2872 printf("%d ", attr->values[i].integer);
2873 break;
2874
2875 case IPP_TAG_BOOLEAN :
2876 for (i = 0; i < attr->num_values; i ++)
2877 if (attr->values[i].boolean)
2878 printf("true ");
2879 else
2880 printf("false ");
2881 break;
2882
2883 case IPP_TAG_NOVALUE :
2884 printf("novalue");
2885 break;
2886
2887 case IPP_TAG_RANGE :
2888 for (i = 0; i < attr->num_values; i ++)
2889 printf("%d-%d ", attr->values[i].range.lower,
2890 attr->values[i].range.upper);
2891 break;
2892
2893 case IPP_TAG_RESOLUTION :
2894 for (i = 0; i < attr->num_values; i ++)
2895 printf("%dx%d%s ", attr->values[i].resolution.xres,
2896 attr->values[i].resolution.yres,
2897 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
2898 "dpi" : "dpc");
2899 break;
2900
2901 case IPP_TAG_STRING :
2902 case IPP_TAG_TEXT :
2903 case IPP_TAG_NAME :
2904 case IPP_TAG_KEYWORD :
2905 case IPP_TAG_CHARSET :
2906 case IPP_TAG_URI :
2907 case IPP_TAG_MIMETYPE :
2908 case IPP_TAG_LANGUAGE :
2909 for (i = 0; i < attr->num_values; i ++)
2910 printf("\"%s\" ", attr->values[i].string.text);
2911 break;
2912
2913 case IPP_TAG_TEXTLANG :
2914 case IPP_TAG_NAMELANG :
2915 for (i = 0; i < attr->num_values; i ++)
2916 printf("\"%s\",%s ", attr->values[i].string.text,
2917 attr->values[i].string.charset);
2918 break;
2919
2920 case IPP_TAG_BEGIN_COLLECTION :
2921 for (i = 0; i < attr->num_values; i ++)
2922 {
2923 print_col(attr->values[i].collection);
2924 putchar(' ');
2925 }
2926 break;
2927
2928 default :
2929 break; /* anti-compiler-warning-code */
2930 }
2931 }
2932
2933 putchar('}');
2934 }
2935
2936
2937 /*
2938 * 'print_csv()' - Print a line of CSV text.
2939 */
2940
2941 static void
2942 print_csv(
2943 ipp_attribute_t *attr, /* I - First attribute for line */
2944 int num_displayed, /* I - Number of attributes to display */
2945 char **displayed, /* I - Attributes to display */
2946 size_t *widths) /* I - Column widths */
2947 {
2948 int i; /* Looping var */
2949 size_t maxlength; /* Max length of all columns */
2950 char *buffer, /* String buffer */
2951 *bufptr; /* Pointer into buffer */
2952 ipp_attribute_t *current; /* Current attribute */
2953
2954
2955 /*
2956 * Get the maximum string length we have to show and allocate...
2957 */
2958
2959 for (i = 1, maxlength = widths[0]; i < num_displayed; i ++)
2960 if (widths[i] > maxlength)
2961 maxlength = widths[i];
2962
2963 maxlength += 2;
2964
2965 if ((buffer = malloc(maxlength)) == NULL)
2966 return;
2967
2968 /*
2969 * Loop through the attributes to display...
2970 */
2971
2972 if (attr)
2973 {
2974 for (i = 0; i < num_displayed; i ++)
2975 {
2976 if (i)
2977 putchar(',');
2978
2979 buffer[0] = '\0';
2980
2981 for (current = attr; current; current = current->next)
2982 {
2983 if (!current->name)
2984 break;
2985 else if (!strcmp(current->name, displayed[i]))
2986 {
2987 _ippAttrString(current, buffer, maxlength);
2988 break;
2989 }
2990 }
2991
2992 if (strchr(buffer, ',') != NULL || strchr(buffer, '\"') != NULL ||
2993 strchr(buffer, '\\') != NULL)
2994 {
2995 putchar('\"');
2996 for (bufptr = buffer; *bufptr; bufptr ++)
2997 {
2998 if (*bufptr == '\\' || *bufptr == '\"')
2999 putchar('\\');
3000 putchar(*bufptr);
3001 }
3002 putchar('\"');
3003 }
3004 else
3005 fputs(buffer, stdout);
3006 }
3007 putchar('\n');
3008 }
3009 else
3010 {
3011 for (i = 0; i < num_displayed; i ++)
3012 {
3013 if (i)
3014 putchar(',');
3015
3016 fputs(displayed[i], stdout);
3017 }
3018 putchar('\n');
3019 }
3020
3021 free(buffer);
3022 }
3023
3024
3025 /*
3026 * 'print_fatal_error()' - Print a fatal error message.
3027 */
3028
3029 static void
3030 print_fatal_error(const char *s, /* I - Printf-style format string */
3031 ...) /* I - Additional arguments as needed */
3032 {
3033 char buffer[10240]; /* Format buffer */
3034 va_list ap; /* Pointer to arguments */
3035
3036
3037 /*
3038 * Format the error message...
3039 */
3040
3041 va_start(ap, s);
3042 vsnprintf(buffer, sizeof(buffer), s, ap);
3043 va_end(ap);
3044
3045 /*
3046 * Then output it...
3047 */
3048
3049 if (Output == _CUPS_OUTPUT_PLIST)
3050 {
3051 print_xml_header();
3052 print_xml_trailer(0, buffer);
3053 }
3054 else
3055 _cupsLangPrintf(stderr, "ipptool: %s\n", buffer);
3056 }
3057
3058
3059 /*
3060 * 'print_line()' - Print a line of formatted or CSV text.
3061 */
3062
3063 static void
3064 print_line(
3065 ipp_attribute_t *attr, /* I - First attribute for line */
3066 int num_displayed, /* I - Number of attributes to display */
3067 char **displayed, /* I - Attributes to display */
3068 size_t *widths) /* I - Column widths */
3069 {
3070 int i; /* Looping var */
3071 size_t maxlength; /* Max length of all columns */
3072 char *buffer; /* String buffer */
3073 ipp_attribute_t *current; /* Current attribute */
3074
3075
3076 /*
3077 * Get the maximum string length we have to show and allocate...
3078 */
3079
3080 for (i = 1, maxlength = widths[0]; i < num_displayed; i ++)
3081 if (widths[i] > maxlength)
3082 maxlength = widths[i];
3083
3084 maxlength += 2;
3085
3086 if ((buffer = malloc(maxlength)) == NULL)
3087 return;
3088
3089 /*
3090 * Loop through the attributes to display...
3091 */
3092
3093 if (attr)
3094 {
3095 for (i = 0; i < num_displayed; i ++)
3096 {
3097 if (i)
3098 putchar(' ');
3099
3100 buffer[0] = '\0';
3101
3102 for (current = attr; current; current = current->next)
3103 {
3104 if (!current->name)
3105 break;
3106 else if (!strcmp(current->name, displayed[i]))
3107 {
3108 _ippAttrString(current, buffer, maxlength);
3109 break;
3110 }
3111 }
3112
3113 printf("%*s", (int)-widths[i], buffer);
3114 }
3115 putchar('\n');
3116 }
3117 else
3118 {
3119 for (i = 0; i < num_displayed; i ++)
3120 {
3121 if (i)
3122 putchar(' ');
3123
3124 printf("%*s", (int)-widths[i], displayed[i]);
3125 }
3126 putchar('\n');
3127
3128 for (i = 0; i < num_displayed; i ++)
3129 {
3130 if (i)
3131 putchar(' ');
3132
3133 memset(buffer, '-', widths[i]);
3134 buffer[widths[i]] = '\0';
3135 fputs(buffer, stdout);
3136 }
3137 putchar('\n');
3138 }
3139
3140 free(buffer);
3141 }
3142
3143
3144 /*
3145 * 'print_test_error()' - Print a test error message.
3146 */
3147
3148 static void
3149 print_test_error(const char *s, /* I - Printf-style format string */
3150 ...) /* I - Additional arguments as needed */
3151 {
3152 char buffer[10240]; /* Format buffer */
3153 va_list ap; /* Pointer to arguments */
3154
3155
3156 /*
3157 * Format the error message...
3158 */
3159
3160 va_start(ap, s);
3161 vsnprintf(buffer, sizeof(buffer), s, ap);
3162 va_end(ap);
3163
3164 /*
3165 * Then output it...
3166 */
3167
3168 if (Output == _CUPS_OUTPUT_PLIST)
3169 print_xml_string("string", buffer);
3170 else
3171 printf(" %s\n", buffer);
3172 }
3173
3174
3175 /*
3176 * 'print_xml_header()' - Print a standard XML plist header.
3177 */
3178
3179 static void
3180 print_xml_header(void)
3181 {
3182 if (!XMLHeader)
3183 {
3184 puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
3185 puts("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
3186 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
3187 puts("<plist version=\"1.0\">");
3188 puts("<dict>");
3189 puts("<key>Transfer</key>");
3190 printf("<string>%s</string>\n",
3191 Transfer == _CUPS_TRANSFER_AUTO ? "auto" :
3192 Transfer == _CUPS_TRANSFER_CHUNKED ? "chunked" : "length");
3193 puts("<key>Tests</key>");
3194 puts("<array>");
3195
3196 XMLHeader = 1;
3197 }
3198 }
3199
3200
3201 /*
3202 * 'print_xml_string()' - Print an XML string with escaping.
3203 */
3204
3205 static void
3206 print_xml_string(const char *element, /* I - Element name or NULL */
3207 const char *s) /* I - String to print */
3208 {
3209 if (element)
3210 printf("<%s>", element);
3211
3212 while (*s)
3213 {
3214 if (*s == '&')
3215 fputs("&amp;", stdout);
3216 else if (*s == '<')
3217 fputs("&lt;", stdout);
3218 else if (*s == '>')
3219 fputs("&gt;", stdout);
3220 else
3221 putchar(*s);
3222
3223 s ++;
3224 }
3225
3226 if (element)
3227 printf("</%s>\n", element);
3228 }
3229
3230
3231 /*
3232 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
3233 */
3234
3235 static void
3236 print_xml_trailer(int success, /* I - 1 on success, 0 on failure */
3237 const char *message) /* I - Error message or NULL */
3238 {
3239 if (XMLHeader)
3240 {
3241 puts("</array>");
3242 puts("<key>Successful</key>");
3243 puts(success ? "<true />" : "<false />");
3244 if (message)
3245 {
3246 puts("<key>ErrorMessage</key>");
3247 print_xml_string("string", message);
3248 }
3249 puts("</dict>");
3250 puts("</plist>");
3251
3252 XMLHeader = 0;
3253 }
3254 }
3255
3256
3257 /*
3258 * 'set_variable()' - Set a variable value.
3259 */
3260
3261 static void
3262 set_variable(_cups_vars_t *vars, /* I - Variables */
3263 const char *name, /* I - Variable name */
3264 const char *value) /* I - Value string */
3265 {
3266 _cups_var_t key, /* Search key */
3267 *var; /* New variable */
3268
3269
3270 key.name = (char *)name;
3271 if ((var = cupsArrayFind(vars->vars, &key)) != NULL)
3272 {
3273 free(var->value);
3274 var->value = strdup(value);
3275 }
3276 else if ((var = malloc(sizeof(_cups_var_t))) == NULL)
3277 {
3278 print_fatal_error("Unable to allocate memory for variable \"%s\".", name);
3279 exit(1);
3280 }
3281 else
3282 {
3283 var->name = strdup(name);
3284 var->value = strdup(value);
3285
3286 cupsArrayAdd(vars->vars, var);
3287 }
3288 }
3289
3290
3291 /*
3292 * 'usage()' - Show program usage.
3293 */
3294
3295 static void
3296 usage(void)
3297 {
3298 _cupsLangPuts(stderr,
3299 _("Usage: ipptool [options] URI filename [ ... "
3300 "filenameN ]\n"
3301 "\n"
3302 "Options:\n"
3303 "\n"
3304 "-C Send requests using chunking (default)\n"
3305 "-E Test with TLS encryption.\n"
3306 "-L Send requests using content-length\n"
3307 "-S Test with SSL encryption.\n"
3308 "-V version Set default IPP version.\n"
3309 "-X Produce XML plist instead of plain text.\n"
3310 "-d name=value Define variable.\n"
3311 "-f filename Set default request filename.\n"
3312 "-i seconds Repeat the last file with the given time "
3313 "interval.\n"
3314 "-n count Repeat the last file the given number of "
3315 "times.\n"
3316 "-q Be quiet - no output except errors.\n"
3317 "-t Produce a test report.\n"
3318 "-v Show all attributes sent and received.\n"));
3319
3320 exit(1);
3321 }
3322
3323
3324 /*
3325 * 'validate_attr()' - Determine whether an attribute is valid.
3326 */
3327
3328 static int /* O - 1 if valid, 0 otherwise */
3329 validate_attr(ipp_attribute_t *attr, /* I - Attribute to validate */
3330 int print) /* I - 1 = report issues to stdout */
3331 {
3332 int i; /* Looping var */
3333 char scheme[64], /* Scheme from URI */
3334 userpass[256], /* Username/password from URI */
3335 hostname[256], /* Hostname from URI */
3336 resource[1024]; /* Resource from URI */
3337 int port, /* Port number from URI */
3338 uri_status, /* URI separation status */
3339 valid = 1; /* Is the attribute valid? */
3340 const char *ptr; /* Pointer into string */
3341 ipp_attribute_t *colattr; /* Collection attribute */
3342 regex_t re; /* Regular expression */
3343 ipp_uchar_t *date; /* Current date value */
3344
3345
3346 /*
3347 * Skip separators.
3348 */
3349
3350 if (!attr->name)
3351 return (1);
3352
3353 /*
3354 * Validate the attribute name.
3355 */
3356
3357 for (ptr = attr->name; *ptr; ptr ++)
3358 if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' && *ptr != '_')
3359 break;
3360
3361 if (*ptr || ptr == attr->name)
3362 {
3363 valid = 0;
3364
3365 if (print)
3366 print_test_error("\"%s\": Bad attribute name - invalid character (RFC "
3367 "2911 section 4.1.3).", attr->name);
3368 }
3369
3370 if ((ptr - attr->name) > 255)
3371 {
3372 valid = 0;
3373
3374 if (print)
3375 print_test_error("\"%s\": Bad attribute name - bad length (RFC 2911 "
3376 "section 4.1.3).", attr->name);
3377 }
3378
3379 switch (attr->value_tag)
3380 {
3381 case IPP_TAG_INTEGER :
3382 break;
3383
3384 case IPP_TAG_BOOLEAN :
3385 for (i = 0; i < attr->num_values; i ++)
3386 {
3387 if (attr->values[i].boolean != 0 &&
3388 attr->values[i].boolean != 1)
3389 {
3390 valid = 0;
3391
3392 if (print)
3393 print_test_error("\"%s\": Bad boolen value %d (RFC 2911 section "
3394 "4.1.10).", attr->name, attr->values[i].boolean);
3395 else
3396 break;
3397 }
3398 }
3399 break;
3400
3401 case IPP_TAG_ENUM :
3402 for (i = 0; i < attr->num_values; i ++)
3403 {
3404 if (attr->values[i].integer < 1)
3405 {
3406 valid = 0;
3407
3408 if (print)
3409 print_test_error("\"%s\": Bad enum value %d - out of range "
3410 "(RFC 2911 section 4.1.4).", attr->name,
3411 attr->values[i].integer);
3412 else
3413 break;
3414 }
3415 }
3416 break;
3417
3418 case IPP_TAG_STRING :
3419 for (i = 0; i < attr->num_values; i ++)
3420 {
3421 if (attr->values[i].unknown.length > 1023)
3422 {
3423 valid = 0;
3424
3425 if (print)
3426 print_test_error("\"%s\": Bad octetString value - bad length %d "
3427 "(RFC 2911 section 4.1.10).", attr->name,
3428 attr->values[i].unknown.length);
3429 else
3430 break;
3431 }
3432 }
3433 break;
3434
3435 case IPP_TAG_DATE :
3436 for (i = 0; i < attr->num_values; i ++)
3437 {
3438 date = attr->values[i].date;
3439
3440 if (date[2] < 1 || date[2] > 12)
3441 {
3442 valid = 0;
3443
3444 if (print)
3445 print_test_error("\"%s\": Bad dateTime month %u (RFC 2911 "
3446 "section 4.1.13).", attr->name, date[2]);
3447 else
3448 break;
3449 }
3450
3451 if (date[3] < 1 || date[3] > 31)
3452 {
3453 valid = 0;
3454
3455 if (print)
3456 print_test_error("\"%s\": Bad dateTime day %u (RFC 2911 "
3457 "section 4.1.13).", attr->name, date[3]);
3458 else
3459 break;
3460 }
3461
3462 if (date[4] > 23)
3463 {
3464 valid = 0;
3465
3466 if (print)
3467 print_test_error("\"%s\": Bad dateTime hours %u (RFC 2911 "
3468 "section 4.1.13).", attr->name, date[4]);
3469 else
3470 break;
3471 }
3472
3473 if (date[5] > 59)
3474 {
3475 valid = 0;
3476
3477 if (print)
3478 print_test_error("\"%s\": Bad dateTime minutes %u (RFC 2911 "
3479 "section 4.1.13).", attr->name, date[5]);
3480 else
3481 break;
3482 }
3483
3484 if (date[6] > 60)
3485 {
3486 valid = 0;
3487
3488 if (print)
3489 print_test_error("\"%s\": Bad dateTime seconds %u (RFC 2911 "
3490 "section 4.1.13).", attr->name, date[6]);
3491 else
3492 break;
3493 }
3494
3495 if (date[7] > 9)
3496 {
3497 valid = 0;
3498
3499 if (print)
3500 print_test_error("\"%s\": Bad dateTime deciseconds %u (RFC 2911 "
3501 "section 4.1.13).", attr->name, date[7]);
3502 else
3503 break;
3504 }
3505
3506 if (date[8] != '-' && date[8] != '+')
3507 {
3508 valid = 0;
3509
3510 if (print)
3511 print_test_error("\"%s\": Bad dateTime UTC sign '%c' (RFC 2911 "
3512 "section 4.1.13).", attr->name, date[8]);
3513 else
3514 break;
3515 }
3516
3517 if (date[9] > 11)
3518 {
3519 valid = 0;
3520
3521 if (print)
3522 print_test_error("\"%s\": Bad dateTime UTC hours %u (RFC 2911 "
3523 "section 4.1.13).", attr->name, date[9]);
3524 else
3525 break;
3526 }
3527
3528 if (date[10] > 59)
3529 {
3530 valid = 0;
3531
3532 if (print)
3533 print_test_error("\"%s\": Bad dateTime UTC minutes %u (RFC 2911 "
3534 "section 4.1.13).", attr->name, date[10]);
3535 else
3536 break;
3537 }
3538 }
3539 break;
3540
3541 case IPP_TAG_RESOLUTION :
3542 for (i = 0; i < attr->num_values; i ++)
3543 {
3544 if (attr->values[i].resolution.xres <= 0)
3545 {
3546 valid = 0;
3547
3548 if (print)
3549 print_test_error("\"%s\": Bad resolution value %dx%d%s - cross "
3550 "feed resolution must be positive (RFC 2911 "
3551 "section 4.1.13).", attr->name,
3552 attr->values[i].resolution.xres,
3553 attr->values[i].resolution.yres,
3554 attr->values[i].resolution.units ==
3555 IPP_RES_PER_INCH ? "dpi" :
3556 attr->values[i].resolution.units ==
3557 IPP_RES_PER_CM ? "dpc" : "unknown");
3558 else
3559 break;
3560 }
3561
3562 if (attr->values[i].resolution.yres <= 0)
3563 {
3564 valid = 0;
3565
3566 if (print)
3567 print_test_error("\"%s\": Bad resolution value %dx%d%s - feed "
3568 "resolution must be positive (RFC 2911 section "
3569 "4.1.13).", attr->name,
3570 attr->values[i].resolution.xres,
3571 attr->values[i].resolution.yres,
3572 attr->values[i].resolution.units ==
3573 IPP_RES_PER_INCH ? "dpi" :
3574 attr->values[i].resolution.units ==
3575 IPP_RES_PER_CM ? "dpc" : "unknown");
3576 else
3577 break;
3578 }
3579
3580 if (attr->values[i].resolution.units != IPP_RES_PER_INCH &&
3581 attr->values[i].resolution.units != IPP_RES_PER_CM)
3582 {
3583 valid = 0;
3584
3585 if (print)
3586 print_test_error("\"%s\": Bad resolution value %dx%d%s - bad "
3587 "units value (RFC 2911 section 4.1.13).",
3588 attr->name, attr->values[i].resolution.xres,
3589 attr->values[i].resolution.yres,
3590 attr->values[i].resolution.units ==
3591 IPP_RES_PER_INCH ? "dpi" :
3592 attr->values[i].resolution.units ==
3593 IPP_RES_PER_CM ? "dpc" : "unknown");
3594 else
3595 break;
3596 }
3597 }
3598 break;
3599
3600 case IPP_TAG_RANGE :
3601 for (i = 0; i < attr->num_values; i ++)
3602 {
3603 if (attr->values[i].range.lower > attr->values[i].range.upper)
3604 {
3605 valid = 0;
3606
3607 if (print)
3608 print_test_error("\"%s\": Bad rangeOfInteger value %d-%d - lower "
3609 "greater than upper (RFC 2911 section 4.1.13).",
3610 attr->name, attr->values[i].range.lower,
3611 attr->values[i].range.upper);
3612 else
3613 break;
3614 }
3615 }
3616 break;
3617
3618 case IPP_TAG_BEGIN_COLLECTION :
3619 for (i = 0; i < attr->num_values; i ++)
3620 {
3621 for (colattr = attr->values[i].collection->attrs;
3622 colattr;
3623 colattr = colattr->next)
3624 {
3625 if (!validate_attr(colattr, 0))
3626 {
3627 valid = 0;
3628 break;
3629 }
3630 }
3631
3632 if (colattr && print)
3633 {
3634 print_test_error("\"%s\": Bad collection value.", attr->name);
3635
3636 while (colattr)
3637 {
3638 validate_attr(colattr, print);
3639 colattr = colattr->next;
3640 }
3641 }
3642 }
3643 break;
3644
3645 case IPP_TAG_TEXT :
3646 case IPP_TAG_TEXTLANG :
3647 for (i = 0; i < attr->num_values; i ++)
3648 {
3649 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
3650 {
3651 if ((*ptr & 0xe0) == 0xc0)
3652 {
3653 ptr ++;
3654 if ((*ptr & 0xc0) != 0x80)
3655 break;
3656 }
3657 else if ((*ptr & 0xf0) == 0xe0)
3658 {
3659 ptr ++;
3660 if ((*ptr & 0xc0) != 0x80)
3661 break;
3662 ptr ++;
3663 if ((*ptr & 0xc0) != 0x80)
3664 break;
3665 }
3666 else if ((*ptr & 0xf8) == 0xf0)
3667 {
3668 ptr ++;
3669 if ((*ptr & 0xc0) != 0x80)
3670 break;
3671 ptr ++;
3672 if ((*ptr & 0xc0) != 0x80)
3673 break;
3674 ptr ++;
3675 if ((*ptr & 0xc0) != 0x80)
3676 break;
3677 }
3678 else if (*ptr & 0x80)
3679 break;
3680 }
3681
3682 if (*ptr)
3683 {
3684 valid = 0;
3685
3686 if (print)
3687 print_test_error("\"%s\": Bad text value \"%s\" - bad UTF-8 "
3688 "sequence (RFC 2911 section 4.1.1).", attr->name,
3689 attr->values[i].string.text);
3690 else
3691 break;
3692 }
3693
3694 if ((ptr - attr->values[i].string.text) > 1023)
3695 {
3696 valid = 0;
3697
3698 if (print)
3699 print_test_error("\"%s\": Bad text value \"%s\" - bad length %d "
3700 "(RFC 2911 section 4.1.1).", attr->name,
3701 attr->values[i].string.text,
3702 (int)strlen(attr->values[i].string.text));
3703 else
3704 break;
3705 }
3706 }
3707 break;
3708
3709 case IPP_TAG_NAME :
3710 case IPP_TAG_NAMELANG :
3711 for (i = 0; i < attr->num_values; i ++)
3712 {
3713 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
3714 {
3715 if ((*ptr & 0xe0) == 0xc0)
3716 {
3717 ptr ++;
3718 if ((*ptr & 0xc0) != 0x80)
3719 break;
3720 }
3721 else if ((*ptr & 0xf0) == 0xe0)
3722 {
3723 ptr ++;
3724 if ((*ptr & 0xc0) != 0x80)
3725 break;
3726 ptr ++;
3727 if ((*ptr & 0xc0) != 0x80)
3728 break;
3729 }
3730 else if ((*ptr & 0xf8) == 0xf0)
3731 {
3732 ptr ++;
3733 if ((*ptr & 0xc0) != 0x80)
3734 break;
3735 ptr ++;
3736 if ((*ptr & 0xc0) != 0x80)
3737 break;
3738 ptr ++;
3739 if ((*ptr & 0xc0) != 0x80)
3740 break;
3741 }
3742 else if (*ptr & 0x80)
3743 break;
3744 }
3745
3746 if (*ptr)
3747 {
3748 valid = 0;
3749
3750 if (print)
3751 print_test_error("\"%s\": Bad name value \"%s\" - bad UTF-8 "
3752 "sequence (RFC 2911 section 4.1.2).", attr->name,
3753 attr->values[i].string.text);
3754 else
3755 break;
3756 }
3757
3758 if ((ptr - attr->values[i].string.text) > 1023)
3759 {
3760 valid = 0;
3761
3762 if (print)
3763 print_test_error("\"%s\": Bad name value \"%s\" - bad length %d "
3764 "(RFC 2911 section 4.1.2).", attr->name,
3765 attr->values[i].string.text,
3766 (int)strlen(attr->values[i].string.text));
3767 else
3768 break;
3769 }
3770 }
3771 break;
3772
3773 case IPP_TAG_KEYWORD :
3774 for (i = 0; i < attr->num_values; i ++)
3775 {
3776 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
3777 if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' &&
3778 *ptr != '_')
3779 break;
3780
3781 if (*ptr || ptr == attr->values[i].string.text)
3782 {
3783 valid = 0;
3784
3785 if (print)
3786 print_test_error("\"%s\": Bad keyword value \"%s\" - invalid "
3787 "character (RFC 2911 section 4.1.3).",
3788 attr->name, attr->values[i].string.text);
3789 else
3790 break;
3791 }
3792
3793 if ((ptr - attr->values[i].string.text) > 255)
3794 {
3795 valid = 0;
3796
3797 if (print)
3798 print_test_error("\"%s\": Bad keyword value \"%s\" - bad "
3799 "length %d (RFC 2911 section 4.1.3).",
3800 attr->name, attr->values[i].string.text,
3801 (int)strlen(attr->values[i].string.text));
3802 else
3803 break;
3804 }
3805 }
3806 break;
3807
3808 case IPP_TAG_URI :
3809 for (i = 0; i < attr->num_values; i ++)
3810 {
3811 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL,
3812 attr->values[i].string.text,
3813 scheme, sizeof(scheme),
3814 userpass, sizeof(userpass),
3815 hostname, sizeof(hostname),
3816 &port, resource, sizeof(resource));
3817
3818 if (uri_status < HTTP_URI_OK)
3819 {
3820 valid = 0;
3821
3822 if (print)
3823 print_test_error("\"%s\": Bad URI value \"%s\" - %s "
3824 "(RFC 2911 section 4.1.5).", attr->name,
3825 attr->values[i].string.text,
3826 URIStatusStrings[uri_status -
3827 HTTP_URI_OVERFLOW]);
3828 else
3829 break;
3830 }
3831
3832 if (strlen(attr->values[i].string.text) > 1023)
3833 {
3834 valid = 0;
3835
3836 if (print)
3837 print_test_error("\"%s\": Bad URI value \"%s\" - bad length %d "
3838 "(RFC 2911 section 4.1.5).", attr->name,
3839 attr->values[i].string.text,
3840 (int)strlen(attr->values[i].string.text));
3841 else
3842 break;
3843 }
3844 }
3845 break;
3846
3847 case IPP_TAG_URISCHEME :
3848 for (i = 0; i < attr->num_values; i ++)
3849 {
3850 ptr = attr->values[i].string.text;
3851 if (islower(*ptr & 255))
3852 {
3853 for (ptr ++; *ptr; ptr ++)
3854 if (!islower(*ptr & 255) && !isdigit(*ptr & 255) &&
3855 *ptr != '+' && *ptr != '-' && *ptr != '.')
3856 break;
3857 }
3858
3859 if (*ptr || ptr == attr->values[i].string.text)
3860 {
3861 valid = 0;
3862
3863 if (print)
3864 print_test_error("\"%s\": Bad uriScheme value \"%s\" - bad "
3865 "characters (RFC 2911 section 4.1.6).",
3866 attr->name, attr->values[i].string.text);
3867 else
3868 break;
3869 }
3870
3871 if ((ptr - attr->values[i].string.text) > 63)
3872 {
3873 valid = 0;
3874
3875 if (print)
3876 print_test_error("\"%s\": Bad uriScheme value \"%s\" - bad "
3877 "length %d (RFC 2911 section 4.1.6).",
3878 attr->name, attr->values[i].string.text,
3879 (int)strlen(attr->values[i].string.text));
3880 else
3881 break;
3882 }
3883 }
3884 break;
3885
3886 case IPP_TAG_CHARSET :
3887 for (i = 0; i < attr->num_values; i ++)
3888 {
3889 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
3890 if (!isprint(*ptr & 255) || isupper(*ptr & 255) ||
3891 isspace(*ptr & 255))
3892 break;
3893
3894 if (*ptr || ptr == attr->values[i].string.text)
3895 {
3896 valid = 0;
3897
3898 if (print)
3899 print_test_error("\"%s\": Bad charset value \"%s\" - bad "
3900 "characters (RFC 2911 section 4.1.7).",
3901 attr->name, attr->values[i].string.text);
3902 else
3903 break;
3904 }
3905
3906 if ((ptr - attr->values[i].string.text) > 40)
3907 {
3908 valid = 0;
3909
3910 if (print)
3911 print_test_error("\"%s\": Bad charset value \"%s\" - bad "
3912 "length %d (RFC 2911 section 4.1.7).",
3913 attr->name, attr->values[i].string.text,
3914 (int)strlen(attr->values[i].string.text));
3915 else
3916 break;
3917 }
3918 }
3919 break;
3920
3921 case IPP_TAG_LANGUAGE :
3922 /*
3923 * The following regular expression is derived from the ABNF for
3924 * language tags in RFC 4646. All I can say is that this is the
3925 * easiest way to check the values...
3926 */
3927
3928 if ((i = regcomp(&re,
3929 "^("
3930 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
3931 /* language */
3932 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
3933 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
3934 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
3935 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
3936 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
3937 "|"
3938 "x(-[a-z0-9]{1,8})+" /* privateuse */
3939 "|"
3940 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
3941 ")$",
3942 REG_NOSUB | REG_EXTENDED)) != 0)
3943 {
3944 char temp[256]; /* Temporary error string */
3945
3946 regerror(i, &re, temp, sizeof(temp));
3947 print_fatal_error("Unable to compile naturalLanguage regular "
3948 "expression: %s.", temp);
3949 }
3950
3951 for (i = 0; i < attr->num_values; i ++)
3952 {
3953 if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
3954 {
3955 valid = 0;
3956
3957 if (print)
3958 print_test_error("\"%s\": Bad naturalLanguage value \"%s\" - bad "
3959 "characters (RFC 2911 section 4.1.8).",
3960 attr->name, attr->values[i].string.text);
3961 else
3962 break;
3963 }
3964
3965 if (strlen(attr->values[i].string.text) > 63)
3966 {
3967 valid = 0;
3968
3969 if (print)
3970 print_test_error("\"%s\": Bad naturalLanguage value \"%s\" - bad "
3971 "length %d (RFC 2911 section 4.1.8).",
3972 attr->name, attr->values[i].string.text,
3973 (int)strlen(attr->values[i].string.text));
3974 else
3975 break;
3976 }
3977 }
3978
3979 regfree(&re);
3980 break;
3981
3982 case IPP_TAG_MIMETYPE :
3983 /*
3984 * The following regular expression is derived from the ABNF for
3985 * language tags in RFC 2045 and 4288. All I can say is that this is
3986 * the easiest way to check the values...
3987 */
3988
3989 if ((i = regcomp(&re,
3990 "^"
3991 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
3992 "/"
3993 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
3994 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
3995 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
3996 /* value */
3997 "$",
3998 REG_NOSUB | REG_EXTENDED)) != 0)
3999 {
4000 char temp[256]; /* Temporary error string */
4001
4002 regerror(i, &re, temp, sizeof(temp));
4003 print_fatal_error("Unable to compile mimeMediaType regular "
4004 "expression: %s.", temp);
4005 }
4006
4007 for (i = 0; i < attr->num_values; i ++)
4008 {
4009 if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
4010 {
4011 valid = 0;
4012
4013 if (print)
4014 print_test_error("\"%s\": Bad mimeMediaType value \"%s\" - bad "
4015 "characters (RFC 2911 section 4.1.9).",
4016 attr->name, attr->values[i].string.text);
4017 else
4018 break;
4019 }
4020
4021 if (strlen(attr->values[i].string.text) > 255)
4022 {
4023 valid = 0;
4024
4025 if (print)
4026 print_test_error("\"%s\": Bad mimeMediaType value \"%s\" - bad "
4027 "length %d (RFC 2911 section 4.1.9).",
4028 attr->name, attr->values[i].string.text,
4029 (int)strlen(attr->values[i].string.text));
4030 else
4031 break;
4032 }
4033 }
4034 break;
4035
4036 default :
4037 break;
4038 }
4039
4040 return (valid);
4041 }
4042
4043
4044 /*
4045 * 'with_value()' - Test a WITH-VALUE predicate.
4046 */
4047
4048 static int /* O - 1 on match, 0 on non-match */
4049 with_value(char *value, /* I - Value string */
4050 int regex, /* I - Value is a regular expression */
4051 ipp_attribute_t *attr) /* I - Attribute to compare */
4052 {
4053 int i; /* Looping var */
4054 char *valptr; /* Pointer into value */
4055
4056
4057 /*
4058 * NULL matches everything.
4059 */
4060
4061 if (!value || !*value)
4062 return (1);
4063
4064 /*
4065 * Compare the value string to the attribute value.
4066 */
4067
4068 switch (attr->value_tag)
4069 {
4070 case IPP_TAG_INTEGER :
4071 case IPP_TAG_ENUM :
4072 for (i = 0; i < attr->num_values; i ++)
4073 {
4074 char op, /* Comparison operator */
4075 *nextptr; /* Next pointer */
4076 int intvalue; /* Integer value */
4077
4078
4079 valptr = value;
4080 if (!strncmp(valptr, "no-value,", 9))
4081 valptr += 9;
4082
4083 while (isspace(*valptr & 255) || isdigit(*valptr & 255) ||
4084 *valptr == '-' || *valptr == ',' || *valptr == '<' ||
4085 *valptr == '=' || *valptr == '>')
4086 {
4087 op = '=';
4088 while (*valptr && !isdigit(*valptr & 255) && *valptr != '-')
4089 {
4090 if (*valptr == '<' || *valptr == '>' || *valptr == '=')
4091 op = *valptr;
4092 valptr ++;
4093 }
4094
4095 if (!*valptr)
4096 break;
4097
4098 intvalue = strtol(valptr, &nextptr, 0);
4099 if (nextptr == valptr)
4100 break;
4101 valptr = nextptr;
4102
4103 switch (op)
4104 {
4105 case '=' :
4106 if (attr->values[i].integer == intvalue)
4107 return (1);
4108 break;
4109 case '<' :
4110 if (attr->values[i].integer < intvalue)
4111 return (1);
4112 break;
4113 case '>' :
4114 if (attr->values[i].integer > intvalue)
4115 return (1);
4116 break;
4117 }
4118 }
4119 }
4120 break;
4121
4122 case IPP_TAG_BOOLEAN :
4123 for (i = 0; i < attr->num_values; i ++)
4124 {
4125 if (!strcmp(value, "true") == attr->values[i].boolean)
4126 return (1);
4127 }
4128 break;
4129
4130 case IPP_TAG_NOVALUE :
4131 return (!strcmp(value, "no-value") || !strncmp(value, "no-value,", 9));
4132
4133 case IPP_TAG_CHARSET :
4134 case IPP_TAG_KEYWORD :
4135 case IPP_TAG_LANGUAGE :
4136 case IPP_TAG_MIMETYPE :
4137 case IPP_TAG_NAME :
4138 case IPP_TAG_NAMELANG :
4139 case IPP_TAG_TEXT :
4140 case IPP_TAG_TEXTLANG :
4141 case IPP_TAG_URI :
4142 case IPP_TAG_URISCHEME :
4143 if (regex)
4144 {
4145 /*
4146 * Value is an extended, case-sensitive POSIX regular expression...
4147 */
4148
4149 regex_t re; /* Regular expression */
4150
4151 if ((i = regcomp(&re, value, REG_EXTENDED | REG_NOSUB)) != 0)
4152 {
4153 char temp[256]; /* Temporary string */
4154
4155 regerror(i, &re, temp, sizeof(temp));
4156
4157 print_fatal_error("Unable to compile WITH-VALUE regular expression "
4158 "\"%s\" - %s", value, temp);
4159 return (0);
4160 }
4161
4162 /*
4163 * See if ALL of the values match the given regular expression.
4164 */
4165
4166 for (i = 0; i < attr->num_values; i ++)
4167 {
4168 if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
4169 break;
4170 }
4171
4172 regfree(&re);
4173
4174 return (i == attr->num_values);
4175 }
4176 else
4177 {
4178 /*
4179 * Value is a literal string, see if at least one value matches the
4180 * literal string...
4181 */
4182
4183 for (i = 0; i < attr->num_values; i ++)
4184 {
4185 if (!strcmp(value, attr->values[i].string.text))
4186 return (1);
4187 }
4188 }
4189 break;
4190
4191 default :
4192 break;
4193 }
4194
4195 return (0);
4196 }
4197
4198
4199 /*
4200 * End of "$Id$".
4201 */