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