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