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