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