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