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