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