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