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