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