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