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