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