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