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