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