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