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