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