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