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