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