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