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