4 * ipptool command for CUPS.
6 * Copyright 2007-2013 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
15 * This file is subject to the Apple OS-Developed Software exception.
19 * main() - Parse options and do tests.
20 * add_stringf() - Add a formatted string to an array.
21 * compare_vars() - Compare two variables.
22 * do_tests() - Do tests as specified in the test file.
23 * expand_variables() - Expand variables in a string.
24 * expect_matches() - Return true if the tag matches the specification.
25 * get_collection() - Get a collection value from the current test file.
26 * get_filename() - Get a filename based on the current test file.
27 * get_token() - Get a token from a file.
28 * get_variable() - Get the value of a variable.
29 * iso_date() - Return an ISO 8601 date/time string for the given IPP
31 * password_cb() - Password callback for authenticated tests.
32 * print_attr() - Print an attribute on the screen.
33 * print_col() - Print a collection attribute on the screen.
34 * print_csv() - Print a line of CSV text.
35 * print_fatal_error() - Print a fatal error message.
36 * print_line() - Print a line of formatted or CSV text.
37 * print_xml_header() - Print a standard XML plist header.
38 * print_xml_string() - Print an XML string with escaping.
39 * print_xml_trailer() - Print the XML trailer with success/fail value.
40 * set_variable() - Set a variable value.
41 * sigterm_handler() - Handle SIGINT and SIGTERM.
42 * timeout_cb() - Handle HTTP timeouts.
43 * usage() - Show program usage.
44 * validate_attr() - Determine whether an attribute is valid.
45 * with_value() - Test a WITH-VALUE predicate.
49 * Include necessary headers...
52 #include <cups/cups-private.h>
53 #include <cups/file-private.h>
63 #endif /* !O_BINARY */
70 typedef enum _cups_transfer_e
/**** How to send request data ****/
72 _CUPS_TRANSFER_AUTO
, /* Chunk for files, length for static */
73 _CUPS_TRANSFER_CHUNKED
, /* Chunk always */
74 _CUPS_TRANSFER_LENGTH
/* Length always */
77 typedef enum _cups_output_e
/**** Output mode ****/
79 _CUPS_OUTPUT_QUIET
, /* No output */
80 _CUPS_OUTPUT_TEST
, /* Traditional CUPS test output */
81 _CUPS_OUTPUT_PLIST
, /* XML plist test output */
82 _CUPS_OUTPUT_LIST
, /* Tabular list output */
83 _CUPS_OUTPUT_CSV
/* Comma-separated values output */
86 typedef enum _cups_with_e
/**** WITH flags ****/
88 _CUPS_WITH_LITERAL
= 0, /* Match string is a literal value */
89 _CUPS_WITH_ALL
= 1, /* Must match all values */
90 _CUPS_WITH_REGEX
= 2 /* Match string is a regular expression */
93 typedef struct _cups_expect_s
/**** Expected attribute info ****/
95 int optional
, /* Optional attribute? */
96 not_expect
; /* Don't expect attribute? */
97 char *name
, /* Attribute name */
98 *of_type
, /* Type name */
99 *same_count_as
, /* Parallel attribute name */
100 *if_defined
, /* Only required if variable defined */
101 *if_not_defined
, /* Only required if variable is not defined */
102 *with_value
, /* Attribute must include this value */
103 *define_match
, /* Variable to define on match */
104 *define_no_match
, /* Variable to define on no-match */
105 *define_value
; /* Variable to define with value */
106 int repeat_limit
, /* Maximum number of times to repeat */
107 repeat_match
, /* Repeat test on match */
108 repeat_no_match
, /* Repeat test on no match */
109 with_flags
, /* WITH flags */
110 count
; /* Expected count if > 0 */
111 ipp_tag_t in_group
; /* IN-GROUP value */
114 typedef struct _cups_status_s
/**** Status info ****/
116 ipp_status_t status
; /* Expected status code */
117 char *if_defined
, /* Only if variable is defined */
118 *if_not_defined
, /* Only if variable is not defined */
119 *define_match
, /* Variable to define on match */
120 *define_no_match
, /* Variable to define on no-match */
121 *define_value
; /* Variable to define with value */
122 int repeat_limit
, /* Maximum number of times to repeat */
123 repeat_match
, /* Repeat the test when it does not match */
124 repeat_no_match
; /* Repeat the test when it matches */
127 typedef struct _cups_var_s
/**** Variable ****/
129 char *name
, /* Name of variable */
130 *value
; /* Value of variable */
133 typedef struct _cups_vars_s
/**** Set of variables ****/
135 char *uri
, /* URI for printer */
136 *filename
, /* Filename */
137 scheme
[64], /* Scheme from URI */
138 userpass
[256], /* Username/password from URI */
139 hostname
[256], /* Hostname from URI */
140 resource
[1024]; /* Resource path from URI */
141 int port
; /* Port number from URI */
142 http_encryption_t encryption
; /* Encryption for connection? */
143 double timeout
; /* Timeout for connection */
144 int family
; /* Address family */
145 cups_array_t
*vars
; /* Array of variables */
153 _cups_transfer_t Transfer
= _CUPS_TRANSFER_AUTO
;
154 /* How to transfer requests */
155 _cups_output_t Output
= _CUPS_OUTPUT_LIST
;
157 int Cancel
= 0, /* Cancel test? */
158 IgnoreErrors
= 0, /* Ignore errors? */
159 StopAfterIncludeError
= 0,
160 /* Stop after include errors? */
161 Verbosity
= 0, /* Show all attributes? */
162 Version
= 11, /* Default IPP version */
163 XMLHeader
= 0, /* 1 if header is written */
164 TestCount
= 0, /* Number of tests run */
165 PassCount
= 0, /* Number of passing tests */
166 FailCount
= 0, /* Number of failing tests */
167 SkipCount
= 0; /* Number of skipped tests */
168 char *Password
= NULL
; /* Password from URI */
169 const char * const URIStatusStrings
[] = /* URI status strings */
172 "Bad arguments to function",
173 "Bad resource in URI",
174 "Bad port number in URI",
175 "Bad hostname/address in URI",
176 "Bad username in URI",
180 "Missing scheme in URI",
181 "Unknown scheme in URI",
182 "Missing resource in URI"
190 static void add_stringf(cups_array_t
*a
, const char *s
, ...)
191 __attribute__ ((__format__ (__printf__
, 2, 3)));
192 static int compare_vars(_cups_var_t
*a
, _cups_var_t
*b
);
193 static int do_tests(_cups_vars_t
*vars
, const char *testfile
);
194 static void expand_variables(_cups_vars_t
*vars
, char *dst
, const char *src
,
195 size_t dstsize
) __attribute__((nonnull(1,2,3)));
196 static int expect_matches(_cups_expect_t
*expect
, ipp_tag_t value_tag
);
197 static ipp_t
*get_collection(_cups_vars_t
*vars
, FILE *fp
, int *linenum
);
198 static char *get_filename(const char *testfile
, char *dst
, const char *src
,
200 static char *get_token(FILE *fp
, char *buf
, int buflen
,
202 static char *get_variable(_cups_vars_t
*vars
, const char *name
);
203 static char *iso_date(ipp_uchar_t
*date
);
204 static const char *password_cb(const char *prompt
);
205 static void print_attr(ipp_attribute_t
*attr
, ipp_tag_t
*group
);
206 static void print_col(ipp_t
*col
);
207 static void print_csv(ipp_attribute_t
*attr
, int num_displayed
,
208 char **displayed
, size_t *widths
);
209 static void print_fatal_error(const char *s
, ...)
210 __attribute__ ((__format__ (__printf__
, 1, 2)));
211 static void print_line(ipp_attribute_t
*attr
, int num_displayed
,
212 char **displayed
, size_t *widths
);
213 static void print_xml_header(void);
214 static void print_xml_string(const char *element
, const char *s
);
215 static void print_xml_trailer(int success
, const char *message
);
216 static void set_variable(_cups_vars_t
*vars
, const char *name
,
219 static void sigterm_handler(int sig
);
221 static int timeout_cb(http_t
*http
, void *user_data
);
222 static void usage(void) __attribute__((noreturn
));
223 static int validate_attr(cups_array_t
*errors
, ipp_attribute_t
*attr
);
224 static int with_value(cups_array_t
*errors
, char *value
, int flags
,
225 ipp_attribute_t
*attr
, char *matchbuf
,
230 * 'main()' - Parse options and do tests.
233 int /* O - Exit status */
234 main(int argc
, /* I - Number of command-line args */
235 char *argv
[]) /* I - Command-line arguments */
237 int i
; /* Looping var */
238 int status
; /* Status of tests... */
239 char *opt
, /* Current option */
240 name
[1024], /* Name/value buffer */
241 *value
, /* Pointer to value */
242 filename
[1024], /* Real filename */
243 testname
[1024], /* Real test filename */
244 uri
[1024]; /* Copy of printer URI */
245 const char *ext
, /* Extension on filename */
246 *testfile
; /* Test file to use */
247 int interval
, /* Test interval in microseconds */
248 repeat
; /* Repeat count */
249 _cups_vars_t vars
; /* Variables */
250 http_uri_status_t uri_status
; /* URI separation status */
251 _cups_globals_t
*cg
= _cupsGlobals();
257 * Catch SIGINT and SIGTERM...
260 signal(SIGINT
, sigterm_handler
);
261 signal(SIGTERM
, sigterm_handler
);
265 * Initialize the locale and variables...
268 _cupsSetLocale(argv
);
270 memset(&vars
, 0, sizeof(vars
));
271 vars
.family
= AF_UNSPEC
;
272 vars
.vars
= cupsArrayNew((cups_array_func_t
)compare_vars
, NULL
);
277 * ipptool URI testfile
285 for (i
= 1; i
< argc
; i
++)
287 if (!strcmp(argv
[i
], "--help"))
291 else if (!strcmp(argv
[i
], "--stop-after-include-error"))
293 StopAfterIncludeError
= 1;
295 else if (!strcmp(argv
[i
], "--version"))
300 else if (argv
[i
][0] == '-')
302 for (opt
= argv
[i
] + 1; *opt
; opt
++)
306 case '4' : /* Connect using IPv4 only */
307 vars
.family
= AF_INET
;
311 case '6' : /* Connect using IPv6 only */
312 vars
.family
= AF_INET6
;
314 #endif /* AF_INET6 */
316 case 'C' : /* Enable HTTP chunking */
317 Transfer
= _CUPS_TRANSFER_CHUNKED
;
320 case 'E' : /* Encrypt with TLS */
322 vars
.encryption
= HTTP_ENCRYPT_REQUIRED
;
324 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
326 #endif /* HAVE_SSL */
329 case 'I' : /* Ignore errors */
333 case 'L' : /* Disable HTTP chunking */
334 Transfer
= _CUPS_TRANSFER_LENGTH
;
337 case 'S' : /* Encrypt with SSL */
339 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
341 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
343 #endif /* HAVE_SSL */
346 case 'T' : /* Set timeout */
351 _cupsLangPuts(stderr
,
352 _("ipptool: Missing timeout for \"-T\"."));
356 vars
.timeout
= _cupsStrScand(argv
[i
], NULL
, localeconv());
359 case 'V' : /* Set IPP version */
364 _cupsLangPuts(stderr
,
365 _("ipptool: Missing version for \"-V\"."));
369 if (!strcmp(argv
[i
], "1.0"))
371 else if (!strcmp(argv
[i
], "1.1"))
373 else if (!strcmp(argv
[i
], "2.0"))
375 else if (!strcmp(argv
[i
], "2.1"))
377 else if (!strcmp(argv
[i
], "2.2"))
381 _cupsLangPrintf(stderr
,
382 _("ipptool: Bad version %s for \"-V\"."),
388 case 'X' : /* Produce XML output */
389 Output
= _CUPS_OUTPUT_PLIST
;
391 if (interval
|| repeat
)
393 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are "
394 "incompatible with -X\"."));
399 case 'c' : /* CSV output */
400 Output
= _CUPS_OUTPUT_CSV
;
403 case 'd' : /* Define a variable */
408 _cupsLangPuts(stderr
,
409 _("ipptool: Missing name=value for \"-d\"."));
413 strlcpy(name
, argv
[i
], sizeof(name
));
414 if ((value
= strchr(name
, '=')) != NULL
)
417 value
= name
+ strlen(name
);
419 set_variable(&vars
, name
, value
);
422 case 'f' : /* Set the default test filename */
427 _cupsLangPuts(stderr
,
428 _("ipptool: Missing filename for \"-f\"."));
435 vars
.filename
= NULL
;
438 if (access(argv
[i
], 0))
444 snprintf(filename
, sizeof(filename
), "%s.gz", argv
[i
]);
445 if (access(filename
, 0) && filename
[0] != '/'
447 && (!isalpha(filename
[0] & 255) || filename
[1] != ':')
451 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s",
452 cg
->cups_datadir
, argv
[i
]);
453 if (access(filename
, 0))
455 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s.gz",
456 cg
->cups_datadir
, argv
[i
]);
457 if (access(filename
, 0))
458 vars
.filename
= strdup(argv
[i
]);
460 vars
.filename
= strdup(filename
);
463 vars
.filename
= strdup(filename
);
466 vars
.filename
= strdup(filename
);
469 vars
.filename
= strdup(argv
[i
]);
471 if ((ext
= strrchr(vars
.filename
, '.')) != NULL
)
474 * Guess the MIME media type based on the extension...
477 if (!_cups_strcasecmp(ext
, ".gif"))
478 set_variable(&vars
, "filetype", "image/gif");
479 else if (!_cups_strcasecmp(ext
, ".htm") ||
480 !_cups_strcasecmp(ext
, ".htm.gz") ||
481 !_cups_strcasecmp(ext
, ".html") ||
482 !_cups_strcasecmp(ext
, ".html.gz"))
483 set_variable(&vars
, "filetype", "text/html");
484 else if (!_cups_strcasecmp(ext
, ".jpg"))
485 set_variable(&vars
, "filetype", "image/jpeg");
486 else if (!_cups_strcasecmp(ext
, ".pcl") ||
487 !_cups_strcasecmp(ext
, ".pcl.gz"))
488 set_variable(&vars
, "filetype", "application/vnd.hp-PCL");
489 else if (!_cups_strcasecmp(ext
, ".pdf"))
490 set_variable(&vars
, "filetype", "application/pdf");
491 else if (!_cups_strcasecmp(ext
, ".png"))
492 set_variable(&vars
, "filetype", "image/png");
493 else if (!_cups_strcasecmp(ext
, ".ps") ||
494 !_cups_strcasecmp(ext
, ".ps.gz"))
495 set_variable(&vars
, "filetype", "application/postscript");
496 else if (!_cups_strcasecmp(ext
, ".pwg") ||
497 !_cups_strcasecmp(ext
, ".pwg.gz") ||
498 !_cups_strcasecmp(ext
, ".ras") ||
499 !_cups_strcasecmp(ext
, ".ras.gz"))
500 set_variable(&vars
, "filetype", "image/pwg-raster");
501 else if (!_cups_strcasecmp(ext
, ".txt") ||
502 !_cups_strcasecmp(ext
, ".txt.gz"))
503 set_variable(&vars
, "filetype", "text/plain");
504 else if (!_cups_strcasecmp(ext
, ".xps"))
505 set_variable(&vars
, "filetype", "application/openxps");
507 set_variable(&vars
, "filetype", "application/octet-stream");
512 * Use the "auto-type" MIME media type...
515 set_variable(&vars
, "filetype", "application/octet-stream");
519 case 'i' : /* Test every N seconds */
524 _cupsLangPuts(stderr
,
525 _("ipptool: Missing seconds for \"-i\"."));
530 interval
= (int)(_cupsStrScand(argv
[i
], NULL
, localeconv()) *
534 _cupsLangPuts(stderr
,
535 _("ipptool: Invalid seconds for \"-i\"."));
540 if (Output
== _CUPS_OUTPUT_PLIST
&& interval
)
542 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are "
543 "incompatible with -X\"."));
548 case 'l' : /* List as a table */
549 Output
= _CUPS_OUTPUT_LIST
;
552 case 'n' : /* Repeat count */
557 _cupsLangPuts(stderr
,
558 _("ipptool: Missing count for \"-n\"."));
562 repeat
= atoi(argv
[i
]);
564 if (Output
== _CUPS_OUTPUT_PLIST
&& repeat
)
566 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are "
567 "incompatible with -X\"."));
572 case 'q' : /* Be quiet */
573 Output
= _CUPS_OUTPUT_QUIET
;
576 case 't' : /* CUPS test output */
577 Output
= _CUPS_OUTPUT_TEST
;
580 case 'v' : /* Be verbose */
585 _cupsLangPrintf(stderr
, _("ipptool: Unknown option \"-%c\"."),
592 else if (!strncmp(argv
[i
], "ipp://", 6) || !strncmp(argv
[i
], "http://", 7)
594 || !strncmp(argv
[i
], "ipps://", 7)
595 || !strncmp(argv
[i
], "https://", 8)
596 #endif /* HAVE_SSL */
605 _cupsLangPuts(stderr
, _("ipptool: May only specify a single URI."));
610 if (!strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8))
611 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
612 #endif /* HAVE_SSL */
615 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, vars
.uri
,
616 vars
.scheme
, sizeof(vars
.scheme
),
617 vars
.userpass
, sizeof(vars
.userpass
),
618 vars
.hostname
, sizeof(vars
.hostname
),
620 vars
.resource
, sizeof(vars
.resource
));
622 if (uri_status
!= HTTP_URI_OK
)
624 _cupsLangPrintf(stderr
, _("ipptool: Bad URI - %s."),
625 URIStatusStrings
[uri_status
- HTTP_URI_OVERFLOW
]);
629 if (vars
.userpass
[0])
631 if ((Password
= strchr(vars
.userpass
, ':')) != NULL
)
634 cupsSetUser(vars
.userpass
);
635 cupsSetPasswordCB(password_cb
);
636 set_variable(&vars
, "uriuser", vars
.userpass
);
639 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), vars
.scheme
, NULL
,
640 vars
.hostname
, vars
.port
, vars
.resource
);
651 _cupsLangPuts(stderr
, _("ipptool: URI required before test file."));
655 if (access(argv
[i
], 0) && argv
[i
][0] != '/'
657 && (!isalpha(argv
[i
][0] & 255) || argv
[i
][1] != ':')
661 snprintf(testname
, sizeof(testname
), "%s/ipptool/%s", cg
->cups_datadir
,
663 if (access(testname
, 0))
671 if (!do_tests(&vars
, testfile
))
676 if (!vars
.uri
|| !testfile
)
680 * Loop if the interval is set...
683 if (Output
== _CUPS_OUTPUT_PLIST
)
684 print_xml_trailer(!status
, NULL
);
685 else if (interval
> 0 && repeat
> 0)
690 do_tests(&vars
, testfile
);
694 else if (interval
> 0)
699 do_tests(&vars
, testfile
);
702 else if (Output
== _CUPS_OUTPUT_TEST
&& TestCount
> 1)
705 * Show a summary report if there were multiple tests...
708 printf("\nSummary: %d tests, %d passed, %d failed, %d skipped\n"
709 "Score: %d%%\n", TestCount
, PassCount
, FailCount
, SkipCount
,
710 100 * (PassCount
+ SkipCount
) / TestCount
);
722 * 'add_stringf()' - Add a formatted string to an array.
726 add_stringf(cups_array_t
*a
, /* I - Array */
727 const char *s
, /* I - Printf-style format string */
728 ...) /* I - Additional args as needed */
730 char buffer
[10240]; /* Format buffer */
731 va_list ap
; /* Argument pointer */
735 * Don't bother is the array is NULL...
742 * Format the message...
746 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
750 * Add it to the array...
753 cupsArrayAdd(a
, buffer
);
758 * 'compare_vars()' - Compare two variables.
761 static int /* O - Result of comparison */
762 compare_vars(_cups_var_t
*a
, /* I - First variable */
763 _cups_var_t
*b
) /* I - Second variable */
765 return (_cups_strcasecmp(a
->name
, b
->name
));
770 * 'do_tests()' - Do tests as specified in the test file.
773 static int /* 1 = success, 0 = failure */
774 do_tests(_cups_vars_t
*vars
, /* I - Variables */
775 const char *testfile
) /* I - Test file to use */
777 int i
, /* Looping var */
778 linenum
, /* Current line number */
779 pass
, /* Did we pass the test? */
780 prev_pass
= 1, /* Did we pass the previous test? */
781 request_id
, /* Current request ID */
782 show_header
= 1, /* Show the test header? */
783 ignore_errors
, /* Ignore test failures? */
784 skip_previous
= 0, /* Skip on previous test failure? */
785 repeat_count
, /* Repeat count */
786 repeat_interval
, /* Repeat interval */
787 repeat_prev
, /* Previous repeat interval */
788 repeat_test
; /* Repeat a test? */
789 http_t
*http
= NULL
; /* HTTP connection to server */
790 FILE *fp
= NULL
; /* Test file */
791 char resource
[512], /* Resource for request */
792 token
[1024], /* Token from file */
793 *tokenptr
, /* Pointer into token */
794 temp
[1024], /* Temporary string */
795 buffer
[8192], /* Copy buffer */
796 compression
[16]; /* COMPRESSION value */
797 ipp_t
*request
= NULL
, /* IPP request */
798 *response
= NULL
; /* IPP response */
799 size_t length
; /* Length of IPP request */
800 http_status_t status
; /* HTTP status */
801 cups_file_t
*reqfile
; /* File to send */
802 ssize_t bytes
; /* Bytes read/written */
803 char attr
[128]; /* Attribute name */
804 ipp_op_t op
; /* Operation */
805 ipp_tag_t group
; /* Current group */
806 ipp_tag_t value
; /* Current value type */
807 ipp_attribute_t
*attrptr
, /* Attribute pointer */
808 *found
, /* Found attribute */
809 *lastcol
= NULL
; /* Last collection attribute */
810 char name
[1024], /* Name of test */
811 file_id
[1024], /* File identifier */
812 test_id
[1024]; /* Test identifier */
813 char filename
[1024]; /* Filename */
814 _cups_transfer_t transfer
; /* To chunk or not to chunk */
815 int version
, /* IPP version number to use */
816 skip_test
; /* Skip this test? */
817 int num_statuses
= 0; /* Number of valid status codes */
818 _cups_status_t statuses
[100], /* Valid status codes */
819 *last_status
; /* Last STATUS (for predicates) */
820 int num_expects
= 0; /* Number of expected attributes */
821 _cups_expect_t expects
[200], /* Expected attributes */
822 *expect
, /* Current expected attribute */
823 *last_expect
; /* Last EXPECT (for predicates) */
824 int num_displayed
= 0; /* Number of displayed attributes */
825 char *displayed
[200]; /* Displayed attributes */
826 size_t widths
[200]; /* Width of columns */
827 cups_array_t
*a
, /* Duplicate attribute array */
828 *errors
= NULL
; /* Errors array */
829 const char *error
; /* Current error */
833 * Open the test file...
836 if ((fp
= fopen(testfile
, "r")) == NULL
)
838 print_fatal_error("Unable to open test file %s - %s", testfile
,
845 * Connect to the server...
848 if ((http
= httpConnect2(vars
->hostname
, vars
->port
, NULL
, vars
->family
,
849 vars
->encryption
, 1, 30000, NULL
)) == NULL
)
851 print_fatal_error("Unable to connect to %s on port %d - %s", vars
->hostname
,
852 vars
->port
, cupsLastErrorString());
858 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
,
859 "deflate, gzip, identity");
861 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
, "identity");
862 #endif /* HAVE_LIBZ */
864 if (vars
->timeout
> 0.0)
865 httpSetTimeout(http
, vars
->timeout
, timeout_cb
, NULL
);
871 CUPS_SRAND(time(NULL
));
873 errors
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
,
874 (cups_afree_func_t
)free
);
878 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
880 while (!Cancel
&& get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
883 * Expect an open brace...
886 if (!strcmp(token
, "DEFINE"))
892 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
893 get_token(fp
, temp
, sizeof(temp
), &linenum
))
895 expand_variables(vars
, token
, temp
, sizeof(token
));
896 set_variable(vars
, attr
, token
);
900 print_fatal_error("Missing DEFINE name and/or value on line %d.",
908 else if (!strcmp(token
, "DEFINE-DEFAULT"))
911 * DEFINE-DEFAULT name value
914 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
915 get_token(fp
, temp
, sizeof(temp
), &linenum
))
917 expand_variables(vars
, token
, temp
, sizeof(token
));
918 if (!get_variable(vars
, attr
))
919 set_variable(vars
, attr
, token
);
923 print_fatal_error("Missing DEFINE-DEFAULT name and/or value on line "
931 else if (!strcmp(token
, "FILE-ID"))
937 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
939 expand_variables(vars
, file_id
, temp
, sizeof(file_id
));
943 print_fatal_error("Missing FILE-ID value on line %d.", linenum
);
950 else if (!strcmp(token
, "IGNORE-ERRORS"))
957 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
958 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
960 IgnoreErrors
= !_cups_strcasecmp(temp
, "yes");
964 print_fatal_error("Missing IGNORE-ERRORS value on line %d.", linenum
);
971 else if (!strcmp(token
, "INCLUDE"))
978 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
981 * Map the filename to and then run the tests...
984 if (!do_tests(vars
, get_filename(testfile
, filename
, temp
,
989 if (StopAfterIncludeError
)
995 print_fatal_error("Missing INCLUDE filename on line %d.", linenum
);
1003 else if (!strcmp(token
, "INCLUDE-IF-DEFINED"))
1006 * INCLUDE-IF-DEFINED name "filename"
1007 * INCLUDE-IF-DEFINED name <filename>
1010 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1011 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1014 * Map the filename to and then run the tests...
1017 if (get_variable(vars
, attr
) &&
1018 !do_tests(vars
, get_filename(testfile
, filename
, temp
,
1023 if (StopAfterIncludeError
)
1029 print_fatal_error("Missing INCLUDE-IF-DEFINED name or filename on line "
1038 else if (!strcmp(token
, "INCLUDE-IF-NOT-DEFINED"))
1041 * INCLUDE-IF-NOT-DEFINED name "filename"
1042 * INCLUDE-IF-NOT-DEFINED name <filename>
1045 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1046 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1049 * Map the filename to and then run the tests...
1052 if (!get_variable(vars
, attr
) &&
1053 !do_tests(vars
, get_filename(testfile
, filename
, temp
,
1058 if (StopAfterIncludeError
)
1064 print_fatal_error("Missing INCLUDE-IF-NOT-DEFINED name or filename on "
1065 "line %d.", linenum
);
1073 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1076 * SKIP-IF-DEFINED variable
1079 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1081 if (get_variable(vars
, temp
))
1086 print_fatal_error("Missing SKIP-IF-DEFINED variable on line %d.",
1092 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1095 * SKIP-IF-NOT-DEFINED variable
1098 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1100 if (!get_variable(vars
, temp
))
1105 print_fatal_error("Missing SKIP-IF-NOT-DEFINED variable on line %d.",
1111 else if (!strcmp(token
, "STOP-AFTER-INCLUDE-ERROR"))
1114 * STOP-AFTER-INCLUDE-ERROR yes
1115 * STOP-AFTER-INCLUDE-ERROR no
1118 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1119 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1121 StopAfterIncludeError
= !_cups_strcasecmp(temp
, "yes");
1125 print_fatal_error("Missing STOP-AFTER-INCLUDE-ERROR value on line %d.",
1133 else if (!strcmp(token
, "TRANSFER"))
1141 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1143 if (!strcmp(temp
, "auto"))
1144 Transfer
= _CUPS_TRANSFER_AUTO
;
1145 else if (!strcmp(temp
, "chunked"))
1146 Transfer
= _CUPS_TRANSFER_CHUNKED
;
1147 else if (!strcmp(temp
, "length"))
1148 Transfer
= _CUPS_TRANSFER_LENGTH
;
1151 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp
,
1159 print_fatal_error("Missing TRANSFER value on line %d.", linenum
);
1166 else if (!strcmp(token
, "VERSION"))
1168 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1170 if (!strcmp(temp
, "1.0"))
1172 else if (!strcmp(temp
, "1.1"))
1174 else if (!strcmp(temp
, "2.0"))
1176 else if (!strcmp(temp
, "2.1"))
1178 else if (!strcmp(temp
, "2.2"))
1182 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1189 print_fatal_error("Missing VERSION number on line %d.", linenum
);
1196 else if (strcmp(token
, "{"))
1198 print_fatal_error("Unexpected token %s seen on line %d.", token
, linenum
);
1204 * Initialize things...
1209 if (Output
== _CUPS_OUTPUT_PLIST
)
1211 else if (Output
== _CUPS_OUTPUT_TEST
)
1212 printf("\"%s\":\n", testfile
);
1217 strlcpy(resource
, vars
->resource
, sizeof(resource
));
1222 group
= IPP_TAG_ZERO
;
1223 ignore_errors
= IgnoreErrors
;
1231 transfer
= Transfer
;
1232 compression
[0] = '\0';
1234 strlcpy(name
, testfile
, sizeof(name
));
1235 if (strrchr(name
, '.') != NULL
)
1236 *strrchr(name
, '.') = '\0';
1239 * Parse until we see a close brace...
1242 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
1244 if (_cups_strcasecmp(token
, "COUNT") &&
1245 _cups_strcasecmp(token
, "DEFINE-MATCH") &&
1246 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1247 _cups_strcasecmp(token
, "DEFINE-VALUE") &&
1248 _cups_strcasecmp(token
, "IF-DEFINED") &&
1249 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1250 _cups_strcasecmp(token
, "IN-GROUP") &&
1251 _cups_strcasecmp(token
, "OF-TYPE") &&
1252 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1253 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1254 _cups_strcasecmp(token
, "REPEAT-NO-MATCH") &&
1255 _cups_strcasecmp(token
, "SAME-COUNT-AS") &&
1256 _cups_strcasecmp(token
, "WITH-ALL-VALUES") &&
1257 _cups_strcasecmp(token
, "WITH-VALUE"))
1260 if (_cups_strcasecmp(token
, "DEFINE-MATCH") &&
1261 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1262 _cups_strcasecmp(token
, "IF-DEFINED") &&
1263 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1264 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1265 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1266 _cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
1269 if (!strcmp(token
, "}"))
1271 else if (!strcmp(token
, "{") && lastcol
)
1274 * Another collection value
1277 ipp_t
*col
= get_collection(vars
, fp
, &linenum
);
1278 /* Collection value */
1282 ipp_attribute_t
*tempcol
; /* Pointer to new buffer */
1286 * Reallocate memory...
1289 if ((tempcol
= realloc(lastcol
, sizeof(ipp_attribute_t
) +
1290 (lastcol
->num_values
+ 1) *
1291 sizeof(_ipp_value_t
))) == NULL
)
1293 print_fatal_error("Unable to allocate memory on line %d.", linenum
);
1298 if (tempcol
!= lastcol
)
1301 * Reset pointers in the list...
1305 request
->prev
->next
= tempcol
;
1307 request
->attrs
= tempcol
;
1309 lastcol
= request
->current
= request
->last
= tempcol
;
1312 lastcol
->values
[lastcol
->num_values
].collection
= col
;
1313 lastcol
->num_values
++;
1321 else if (!strcmp(token
, "COMPRESSION"))
1325 * COMPRESSION deflate
1329 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1331 expand_variables(vars
, compression
, temp
, sizeof(compression
));
1333 if (strcmp(compression
, "none") && strcmp(compression
, "deflate") &&
1334 strcmp(compression
, "gzip"))
1336 if (strcmp(compression
, "none"))
1337 #endif /* HAVE_LIBZ */
1339 print_fatal_error("Unsupported COMPRESSION value '%s' on line %d.",
1340 compression
, linenum
);
1345 if (!strcmp(compression
, "none"))
1346 compression
[0] = '\0';
1350 print_fatal_error("Missing COMPRESSION value on line %d.", linenum
);
1355 else if (!strcmp(token
, "DEFINE"))
1361 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1362 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1364 expand_variables(vars
, token
, temp
, sizeof(token
));
1365 set_variable(vars
, attr
, token
);
1369 print_fatal_error("Missing DEFINE name and/or value on line %d.",
1375 else if (!strcmp(token
, "IGNORE-ERRORS"))
1382 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1383 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1385 ignore_errors
= !_cups_strcasecmp(temp
, "yes");
1389 print_fatal_error("Missing IGNORE-ERRORS value on line %d.", linenum
);
1396 else if (!_cups_strcasecmp(token
, "NAME"))
1402 get_token(fp
, name
, sizeof(name
), &linenum
);
1404 else if (!strcmp(token
, "REQUEST-ID"))
1411 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1413 if (isdigit(temp
[0] & 255))
1414 request_id
= atoi(temp
);
1415 else if (!_cups_strcasecmp(temp
, "random"))
1416 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
1419 print_fatal_error("Bad REQUEST-ID value \"%s\" on line %d.", temp
,
1427 print_fatal_error("Missing REQUEST-ID value on line %d.", linenum
);
1432 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1435 * SKIP-IF-DEFINED variable
1438 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1440 if (get_variable(vars
, temp
))
1445 print_fatal_error("Missing SKIP-IF-DEFINED value on line %d.",
1451 else if (!strcmp(token
, "SKIP-IF-MISSING"))
1454 * SKIP-IF-MISSING filename
1457 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1459 expand_variables(vars
, token
, temp
, sizeof(token
));
1460 get_filename(testfile
, filename
, token
, sizeof(filename
));
1462 if (access(filename
, R_OK
))
1467 print_fatal_error("Missing SKIP-IF-MISSING filename on line %d.",
1473 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1476 * SKIP-IF-NOT-DEFINED variable
1479 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1481 if (!get_variable(vars
, temp
))
1486 print_fatal_error("Missing SKIP-IF-NOT-DEFINED value on line %d.",
1492 else if (!strcmp(token
, "SKIP-PREVIOUS-ERROR"))
1495 * SKIP-PREVIOUS-ERROR yes
1496 * SKIP-PREVIOUS-ERROR no
1499 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1500 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1502 skip_previous
= !_cups_strcasecmp(temp
, "yes");
1506 print_fatal_error("Missing SKIP-PREVIOUS-ERROR value on line %d.", linenum
);
1513 else if (!strcmp(token
, "TEST-ID"))
1519 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1521 expand_variables(vars
, test_id
, temp
, sizeof(test_id
));
1525 print_fatal_error("Missing TEST-ID value on line %d.", linenum
);
1532 else if (!strcmp(token
, "TRANSFER"))
1540 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1542 if (!strcmp(temp
, "auto"))
1543 transfer
= _CUPS_TRANSFER_AUTO
;
1544 else if (!strcmp(temp
, "chunked"))
1545 transfer
= _CUPS_TRANSFER_CHUNKED
;
1546 else if (!strcmp(temp
, "length"))
1547 transfer
= _CUPS_TRANSFER_LENGTH
;
1550 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp
,
1558 print_fatal_error("Missing TRANSFER value on line %d.", linenum
);
1563 else if (!_cups_strcasecmp(token
, "VERSION"))
1565 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1567 if (!strcmp(temp
, "0.0"))
1569 else if (!strcmp(temp
, "1.0"))
1571 else if (!strcmp(temp
, "1.1"))
1573 else if (!strcmp(temp
, "2.0"))
1575 else if (!strcmp(temp
, "2.1"))
1577 else if (!strcmp(temp
, "2.2"))
1581 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1588 print_fatal_error("Missing VERSION number on line %d.", linenum
);
1593 else if (!_cups_strcasecmp(token
, "RESOURCE"))
1599 if (!get_token(fp
, resource
, sizeof(resource
), &linenum
))
1601 print_fatal_error("Missing RESOURCE path on line %d.", linenum
);
1606 else if (!_cups_strcasecmp(token
, "OPERATION"))
1612 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1614 print_fatal_error("Missing OPERATION code on line %d.", linenum
);
1619 expand_variables(vars
, token
, temp
, sizeof(token
));
1621 if ((op
= ippOpValue(token
)) == (ipp_op_t
)-1 &&
1622 (op
= strtol(token
, NULL
, 0)) == 0)
1624 print_fatal_error("Bad OPERATION code \"%s\" on line %d.", token
,
1630 else if (!_cups_strcasecmp(token
, "GROUP"))
1633 * Attribute group...
1636 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1638 print_fatal_error("Missing GROUP tag on line %d.", linenum
);
1643 if ((value
= ippTagValue(token
)) < 0)
1645 print_fatal_error("Bad GROUP tag \"%s\" on line %d.", token
, linenum
);
1651 ippAddSeparator(request
);
1655 else if (!_cups_strcasecmp(token
, "DELAY"))
1658 * Delay before operation...
1663 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1665 print_fatal_error("Missing DELAY value on line %d.", linenum
);
1670 expand_variables(vars
, token
, temp
, sizeof(token
));
1672 if ((delay
= _cupsStrScand(token
, NULL
, localeconv())) <= 0.0)
1674 print_fatal_error("Bad DELAY value \"%s\" on line %d.", token
,
1681 if (Output
== _CUPS_OUTPUT_TEST
)
1682 printf(" [%g second delay]\n", delay
);
1684 usleep((int)(1000000.0 * delay
));
1687 else if (!_cups_strcasecmp(token
, "ATTR"))
1693 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1695 print_fatal_error("Missing ATTR value tag on line %d.", linenum
);
1700 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
1702 print_fatal_error("Bad ATTR value tag \"%s\" on line %d.", token
,
1708 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
1710 print_fatal_error("Missing ATTR name on line %d.", linenum
);
1715 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1717 print_fatal_error("Missing ATTR value on line %d.", linenum
);
1722 expand_variables(vars
, token
, temp
, sizeof(token
));
1727 case IPP_TAG_BOOLEAN
:
1728 if (!_cups_strcasecmp(token
, "true"))
1729 attrptr
= ippAddBoolean(request
, group
, attr
, 1);
1731 attrptr
= ippAddBoolean(request
, group
, attr
, atoi(token
));
1734 case IPP_TAG_INTEGER
:
1736 if (!strchr(token
, ','))
1737 attrptr
= ippAddInteger(request
, group
, value
, attr
,
1738 strtol(token
, &tokenptr
, 0));
1741 int values
[100], /* Values */
1742 num_values
= 1; /* Number of values */
1744 values
[0] = strtol(token
, &tokenptr
, 10);
1745 while (tokenptr
&& *tokenptr
&&
1746 num_values
< (int)(sizeof(values
) / sizeof(values
[0])))
1748 if (*tokenptr
== ',')
1750 else if (!isdigit(*tokenptr
& 255) && *tokenptr
!= '-')
1753 values
[num_values
] = strtol(tokenptr
, &tokenptr
, 0);
1757 attrptr
= ippAddIntegers(request
, group
, value
, attr
, num_values
, values
);
1760 if (!tokenptr
|| *tokenptr
)
1762 print_fatal_error("Bad %s value \"%s\" on line %d.",
1763 ippTagString(value
), token
, linenum
);
1769 case IPP_TAG_RESOLUTION
:
1771 int xres
, /* X resolution */
1772 yres
; /* Y resolution */
1773 char *ptr
; /* Pointer into value */
1775 xres
= yres
= strtol(token
, (char **)&ptr
, 10);
1776 if (ptr
> token
&& xres
> 0)
1779 yres
= strtol(ptr
+ 1, (char **)&ptr
, 10);
1782 if (ptr
<= token
|| xres
<= 0 || yres
<= 0 || !ptr
||
1783 (_cups_strcasecmp(ptr
, "dpi") &&
1784 _cups_strcasecmp(ptr
, "dpc") &&
1785 _cups_strcasecmp(ptr
, "dpcm") &&
1786 _cups_strcasecmp(ptr
, "other")))
1788 print_fatal_error("Bad resolution value \"%s\" on line %d.",
1794 if (!_cups_strcasecmp(ptr
, "dpi"))
1795 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_INCH
,
1797 else if (!_cups_strcasecmp(ptr
, "dpc") ||
1798 !_cups_strcasecmp(ptr
, "dpcm"))
1799 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_CM
,
1802 attrptr
= ippAddResolution(request
, group
, attr
, (ipp_res_t
)0,
1807 case IPP_TAG_RANGE
:
1809 int lowers
[4], /* Lower value */
1810 uppers
[4], /* Upper values */
1811 num_vals
; /* Number of values */
1814 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
1815 lowers
+ 0, uppers
+ 0,
1816 lowers
+ 1, uppers
+ 1,
1817 lowers
+ 2, uppers
+ 2,
1818 lowers
+ 3, uppers
+ 3);
1820 if ((num_vals
& 1) || num_vals
== 0)
1822 print_fatal_error("Bad rangeOfInteger value \"%s\" on line "
1823 "%d.", token
, linenum
);
1828 attrptr
= ippAddRanges(request
, group
, attr
, num_vals
/ 2, lowers
,
1833 case IPP_TAG_BEGIN_COLLECTION
:
1834 if (!strcmp(token
, "{"))
1836 ipp_t
*col
= get_collection(vars
, fp
, &linenum
);
1837 /* Collection value */
1841 attrptr
= lastcol
= ippAddCollection(request
, group
, attr
, col
);
1852 print_fatal_error("Bad ATTR collection value on line %d.",
1859 case IPP_TAG_STRING
:
1860 attrptr
= ippAddOctetString(request
, group
, attr
, token
,
1865 print_fatal_error("Unsupported ATTR value tag %s on line %d.",
1866 ippTagString(value
), linenum
);
1870 case IPP_TAG_TEXTLANG
:
1871 case IPP_TAG_NAMELANG
:
1874 case IPP_TAG_KEYWORD
:
1876 case IPP_TAG_URISCHEME
:
1877 case IPP_TAG_CHARSET
:
1878 case IPP_TAG_LANGUAGE
:
1879 case IPP_TAG_MIMETYPE
:
1880 if (!strchr(token
, ','))
1881 attrptr
= ippAddString(request
, group
, value
, attr
, NULL
, token
);
1885 * Multiple string values...
1888 int num_values
; /* Number of values */
1889 char *values
[100], /* Values */
1890 *ptr
; /* Pointer to next value */
1896 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
1898 if (ptr
> token
&& ptr
[-1] == '\\')
1899 _cups_strcpy(ptr
- 1, ptr
);
1903 values
[num_values
] = ptr
;
1908 attrptr
= ippAddStrings(request
, group
, value
, attr
, num_values
,
1909 NULL
, (const char **)values
);
1916 print_fatal_error("Unable to add attribute on line %d: %s", linenum
,
1917 cupsLastErrorString());
1922 else if (!_cups_strcasecmp(token
, "FILE"))
1928 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1930 print_fatal_error("Missing FILE filename on line %d.", linenum
);
1935 expand_variables(vars
, token
, temp
, sizeof(token
));
1936 get_filename(testfile
, filename
, token
, sizeof(filename
));
1938 if (access(filename
, R_OK
))
1940 print_fatal_error("Filename \"%s\" on line %d cannot be read.",
1942 print_fatal_error("Filename mapped to \"%s\".", filename
);
1947 else if (!_cups_strcasecmp(token
, "STATUS"))
1953 if (num_statuses
>= (int)(sizeof(statuses
) / sizeof(statuses
[0])))
1955 print_fatal_error("Too many STATUS's on line %d.", linenum
);
1960 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1962 print_fatal_error("Missing STATUS code on line %d.", linenum
);
1967 if ((statuses
[num_statuses
].status
= ippErrorValue(token
))
1968 == (ipp_status_t
)-1 &&
1969 (statuses
[num_statuses
].status
= strtol(token
, NULL
, 0)) == 0)
1971 print_fatal_error("Bad STATUS code \"%s\" on line %d.", token
,
1977 last_status
= statuses
+ num_statuses
;
1980 last_status
->define_match
= NULL
;
1981 last_status
->define_no_match
= NULL
;
1982 last_status
->if_defined
= NULL
;
1983 last_status
->if_not_defined
= NULL
;
1984 last_status
->repeat_limit
= 1000;
1985 last_status
->repeat_match
= 0;
1986 last_status
->repeat_no_match
= 0;
1988 else if (!_cups_strcasecmp(token
, "EXPECT"))
1991 * Expected attributes...
1994 if (num_expects
>= (int)(sizeof(expects
) / sizeof(expects
[0])))
1996 print_fatal_error("Too many EXPECT's on line %d.", linenum
);
2001 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2003 print_fatal_error("Missing EXPECT name on line %d.", linenum
);
2008 last_expect
= expects
+ num_expects
;
2011 memset(last_expect
, 0, sizeof(_cups_expect_t
));
2012 last_expect
->repeat_limit
= 1000;
2014 if (token
[0] == '!')
2016 last_expect
->not_expect
= 1;
2017 last_expect
->name
= strdup(token
+ 1);
2019 else if (token
[0] == '?')
2021 last_expect
->optional
= 1;
2022 last_expect
->name
= strdup(token
+ 1);
2025 last_expect
->name
= strdup(token
);
2027 else if (!_cups_strcasecmp(token
, "COUNT"))
2029 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2031 print_fatal_error("Missing COUNT number on line %d.", linenum
);
2036 if ((i
= atoi(token
)) <= 0)
2038 print_fatal_error("Bad COUNT \"%s\" on line %d.", token
, linenum
);
2044 last_expect
->count
= i
;
2047 print_fatal_error("COUNT without a preceding EXPECT on line %d.",
2053 else if (!_cups_strcasecmp(token
, "DEFINE-MATCH"))
2055 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2057 print_fatal_error("Missing DEFINE-MATCH variable on line %d.",
2064 last_expect
->define_match
= strdup(token
);
2065 else if (last_status
)
2066 last_status
->define_match
= strdup(token
);
2069 print_fatal_error("DEFINE-MATCH without a preceding EXPECT or STATUS "
2070 "on line %d.", linenum
);
2075 else if (!_cups_strcasecmp(token
, "DEFINE-NO-MATCH"))
2077 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2079 print_fatal_error("Missing DEFINE-NO-MATCH variable on line %d.",
2086 last_expect
->define_no_match
= strdup(token
);
2087 else if (last_status
)
2088 last_status
->define_no_match
= strdup(token
);
2091 print_fatal_error("DEFINE-NO-MATCH without a preceding EXPECT or "
2092 "STATUS on line %d.", linenum
);
2097 else if (!_cups_strcasecmp(token
, "DEFINE-VALUE"))
2099 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2101 print_fatal_error("Missing DEFINE-VALUE variable on line %d.",
2108 last_expect
->define_value
= strdup(token
);
2111 print_fatal_error("DEFINE-VALUE without a preceding EXPECT on "
2112 "line %d.", linenum
);
2117 else if (!_cups_strcasecmp(token
, "OF-TYPE"))
2119 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2121 print_fatal_error("Missing OF-TYPE value tag(s) on line %d.",
2128 last_expect
->of_type
= strdup(token
);
2131 print_fatal_error("OF-TYPE without a preceding EXPECT on line %d.",
2137 else if (!_cups_strcasecmp(token
, "IN-GROUP"))
2139 ipp_tag_t in_group
; /* IN-GROUP value */
2142 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2144 print_fatal_error("Missing IN-GROUP group tag on line %d.", linenum
);
2149 if ((in_group
= ippTagValue(token
)) == (ipp_tag_t
)-1)
2152 else if (last_expect
)
2153 last_expect
->in_group
= in_group
;
2156 print_fatal_error("IN-GROUP without a preceding EXPECT on line %d.",
2162 else if (!_cups_strcasecmp(token
, "REPEAT-LIMIT"))
2164 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2166 print_fatal_error("Missing REPEAT-LIMIT value on line %d.", linenum
);
2170 else if (atoi(token
) <= 0)
2172 print_fatal_error("Bad REPEAT-LIMIT value on line %d.", linenum
);
2178 last_status
->repeat_limit
= atoi(token
);
2179 else if (last_expect
)
2180 last_expect
->repeat_limit
= atoi(token
);
2183 print_fatal_error("REPEAT-LIMIT without a preceding EXPECT or STATUS "
2184 "on line %d.", linenum
);
2189 else if (!_cups_strcasecmp(token
, "REPEAT-MATCH"))
2192 last_status
->repeat_match
= 1;
2193 else if (last_expect
)
2194 last_expect
->repeat_match
= 1;
2197 print_fatal_error("REPEAT-MATCH without a preceding EXPECT or STATUS "
2198 "on line %d.", linenum
);
2203 else if (!_cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
2206 last_status
->repeat_no_match
= 1;
2207 else if (last_expect
)
2208 last_expect
->repeat_no_match
= 1;
2211 print_fatal_error("REPEAT-NO-MATCH without a preceding EXPECT or "
2212 "STATUS on ine %d.", linenum
);
2217 else if (!_cups_strcasecmp(token
, "SAME-COUNT-AS"))
2219 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2221 print_fatal_error("Missing SAME-COUNT-AS name on line %d.", linenum
);
2227 last_expect
->same_count_as
= strdup(token
);
2230 print_fatal_error("SAME-COUNT-AS without a preceding EXPECT on line "
2236 else if (!_cups_strcasecmp(token
, "IF-DEFINED"))
2238 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2240 print_fatal_error("Missing IF-DEFINED name on line %d.", linenum
);
2246 last_expect
->if_defined
= strdup(token
);
2247 else if (last_status
)
2248 last_status
->if_defined
= strdup(token
);
2251 print_fatal_error("IF-DEFINED without a preceding EXPECT or STATUS "
2252 "on line %d.", linenum
);
2257 else if (!_cups_strcasecmp(token
, "IF-NOT-DEFINED"))
2259 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2261 print_fatal_error("Missing IF-NOT-DEFINED name on line %d.", linenum
);
2267 last_expect
->if_not_defined
= strdup(token
);
2268 else if (last_status
)
2269 last_status
->if_not_defined
= strdup(token
);
2272 print_fatal_error("IF-NOT-DEFINED without a preceding EXPECT or STATUS "
2273 "on line %d.", linenum
);
2278 else if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") ||
2279 !_cups_strcasecmp(token
, "WITH-VALUE"))
2281 if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") && last_expect
)
2282 last_expect
->with_flags
= _CUPS_WITH_ALL
;
2284 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
2286 print_fatal_error("Missing %s value on line %d.", token
, linenum
);
2294 * Expand any variables in the value and then save it.
2297 expand_variables(vars
, token
, temp
, sizeof(token
));
2299 tokenptr
= token
+ strlen(token
) - 1;
2301 if (token
[0] == '/' && tokenptr
> token
&& *tokenptr
== '/')
2304 * WITH-VALUE is a POSIX extended regular expression.
2307 last_expect
->with_value
= calloc(1, tokenptr
- token
);
2308 last_expect
->with_flags
|= _CUPS_WITH_REGEX
;
2310 if (last_expect
->with_value
)
2311 memcpy(last_expect
->with_value
, token
+ 1, tokenptr
- token
- 1);
2316 * WITH-VALUE is a literal value...
2319 char *ptr
; /* Pointer into value */
2321 for (ptr
= token
; *ptr
; ptr
++)
2323 if (*ptr
== '\\' && ptr
[1])
2326 * Remove \ from \foo...
2329 _cups_strcpy(ptr
, ptr
+ 1);
2333 last_expect
->with_value
= strdup(token
);
2334 last_expect
->with_flags
|= _CUPS_WITH_LITERAL
;
2339 print_fatal_error("%s without a preceding EXPECT on line %d.", token
,
2345 else if (!_cups_strcasecmp(token
, "DISPLAY"))
2348 * Display attributes...
2351 if (num_displayed
>= (int)(sizeof(displayed
) / sizeof(displayed
[0])))
2353 print_fatal_error("Too many DISPLAY's on line %d", linenum
);
2358 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2360 print_fatal_error("Missing DISPLAY name on line %d.", linenum
);
2365 displayed
[num_displayed
] = strdup(token
);
2370 print_fatal_error("Unexpected token %s seen on line %d.", token
,
2378 * Submit the IPP request...
2383 request
->request
.op
.version
[0] = version
/ 10;
2384 request
->request
.op
.version
[1] = version
% 10;
2385 request
->request
.op
.operation_id
= op
;
2386 request
->request
.op
.request_id
= request_id
;
2388 if (Output
== _CUPS_OUTPUT_PLIST
)
2391 puts("<key>Name</key>");
2392 print_xml_string("string", name
);
2395 puts("<key>FileId</key>");
2396 print_xml_string("string", file_id
);
2400 puts("<key>TestId</key>");
2401 print_xml_string("string", test_id
);
2403 puts("<key>Version</key>");
2404 printf("<string>%d.%d</string>\n", version
/ 10, version
% 10);
2405 puts("<key>Operation</key>");
2406 print_xml_string("string", ippOpString(op
));
2407 puts("<key>RequestId</key>");
2408 printf("<integer>%d</integer>\n", request_id
);
2409 puts("<key>RequestAttributes</key>");
2414 for (attrptr
= request
->attrs
,
2415 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2417 attrptr
= attrptr
->next
)
2418 print_attr(attrptr
, &group
);
2423 else if (Output
== _CUPS_OUTPUT_TEST
)
2427 printf(" %s:\n", ippOpString(op
));
2429 for (attrptr
= request
->attrs
; attrptr
; attrptr
= attrptr
->next
)
2430 print_attr(attrptr
, NULL
);
2433 printf(" %-68.68s [", name
);
2437 if ((skip_previous
&& !prev_pass
) || skip_test
)
2444 if (Output
== _CUPS_OUTPUT_PLIST
)
2446 puts("<key>Successful</key>");
2448 puts("<key>StatusCode</key>");
2449 print_xml_string("string", "skip");
2450 puts("<key>ResponseAttributes</key>");
2453 else if (Output
== _CUPS_OUTPUT_TEST
)
2460 repeat_interval
= 1;
2469 if (transfer
== _CUPS_TRANSFER_CHUNKED
||
2470 (transfer
== _CUPS_TRANSFER_AUTO
&& filename
[0]))
2473 * Send request using chunking - a 0 length means "chunk".
2481 * Send request using content length...
2484 length
= ippLength(request
);
2486 if (filename
[0] && (reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2489 * Read the file to get the uncompressed file size...
2492 while ((bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
2495 cupsFileClose(reqfile
);
2500 * Send the request...
2507 if (status
!= HTTP_ERROR
)
2509 while (!response
&& !Cancel
&& prev_pass
)
2511 status
= cupsSendRequest(http
, request
, resource
, length
);
2515 httpSetField(http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
2516 #endif /* HAVE_LIBZ */
2518 if (!Cancel
&& status
== HTTP_CONTINUE
&&
2519 request
->state
== IPP_DATA
&& filename
[0])
2521 if ((reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2524 (bytes
= cupsFileRead(reqfile
, buffer
,
2525 sizeof(buffer
))) > 0)
2526 if ((status
= cupsWriteRequestData(http
, buffer
,
2527 bytes
)) != HTTP_CONTINUE
)
2530 cupsFileClose(reqfile
);
2534 snprintf(buffer
, sizeof(buffer
), "%s: %s", filename
,
2536 _cupsSetError(IPP_INTERNAL_ERROR
, buffer
, 0);
2538 status
= HTTP_ERROR
;
2543 * Get the server's response...
2546 if (!Cancel
&& status
!= HTTP_ERROR
)
2548 response
= cupsGetResponse(http
, resource
);
2549 status
= httpGetStatus(http
);
2552 if (!Cancel
&& status
== HTTP_ERROR
&& http
->error
!= EINVAL
&&
2554 http
->error
!= WSAETIMEDOUT
)
2556 http
->error
!= ETIMEDOUT
)
2559 if (httpReconnect(http
))
2562 else if (status
== HTTP_ERROR
)
2567 else if (status
!= HTTP_OK
)
2571 if (status
== HTTP_STATUS_UNAUTHORIZED
)
2579 if (!Cancel
&& status
== HTTP_ERROR
&& http
->error
!= EINVAL
&&
2581 http
->error
!= WSAETIMEDOUT
)
2583 http
->error
!= ETIMEDOUT
)
2586 if (httpReconnect(http
))
2589 else if (status
== HTTP_ERROR
)
2592 httpReconnect(http
);
2596 else if (status
!= HTTP_OK
)
2603 * Check results of request...
2606 cupsArrayClear(errors
);
2608 if (http
->version
!= HTTP_1_1
)
2609 add_stringf(errors
, "Bad HTTP version (%d.%d)", http
->version
/ 100,
2610 http
->version
% 100);
2615 * No response, log error...
2618 add_stringf(errors
, "IPP request failed with status %s (%s)",
2619 ippErrorString(cupsLastError()),
2620 cupsLastErrorString());
2625 * Collect common attribute values...
2628 if ((attrptr
= ippFindAttribute(response
, "job-id",
2629 IPP_TAG_INTEGER
)) != NULL
)
2631 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2632 set_variable(vars
, "job-id", temp
);
2635 if ((attrptr
= ippFindAttribute(response
, "job-uri",
2636 IPP_TAG_URI
)) != NULL
)
2637 set_variable(vars
, "job-uri", attrptr
->values
[0].string
.text
);
2639 if ((attrptr
= ippFindAttribute(response
, "notify-subscription-id",
2640 IPP_TAG_INTEGER
)) != NULL
)
2642 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2643 set_variable(vars
, "notify-subscription-id", temp
);
2647 * Check response, validating groups and attributes and logging errors
2651 if (response
->state
!= IPP_DATA
)
2653 "Missing end-of-attributes-tag in response "
2654 "(RFC 2910 section 3.5.1)");
2657 (response
->request
.status
.version
[0] != (version
/ 10) ||
2658 response
->request
.status
.version
[1] != (version
% 10)))
2660 "Bad version %d.%d in response - expected %d.%d "
2661 "(RFC 2911 section 3.1.8).",
2662 response
->request
.status
.version
[0],
2663 response
->request
.status
.version
[1],
2664 version
/ 10, version
% 10);
2666 if (response
->request
.status
.request_id
!= request_id
)
2668 "Bad request ID %d in response - expected %d "
2669 "(RFC 2911 section 3.1.1)",
2670 response
->request
.status
.request_id
, request_id
);
2672 attrptr
= response
->attrs
;
2675 "Missing first attribute \"attributes-charset "
2676 "(charset)\" in group operation-attributes-tag "
2677 "(RFC 2911 section 3.1.4).");
2680 if (!attrptr
->name
||
2681 attrptr
->value_tag
!= IPP_TAG_CHARSET
||
2682 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2683 attrptr
->num_values
!= 1 ||
2684 strcmp(attrptr
->name
, "attributes-charset"))
2686 "Bad first attribute \"%s (%s%s)\" in group %s, "
2687 "expected \"attributes-charset (charset)\" in "
2688 "group operation-attributes-tag (RFC 2911 section "
2690 attrptr
->name
? attrptr
->name
: "(null)",
2691 attrptr
->num_values
> 1 ? "1setOf " : "",
2692 ippTagString(attrptr
->value_tag
),
2693 ippTagString(attrptr
->group_tag
));
2695 attrptr
= attrptr
->next
;
2698 "Missing second attribute \"attributes-natural-"
2699 "language (naturalLanguage)\" in group "
2700 "operation-attributes-tag (RFC 2911 section "
2702 else if (!attrptr
->name
||
2703 attrptr
->value_tag
!= IPP_TAG_LANGUAGE
||
2704 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2705 attrptr
->num_values
!= 1 ||
2706 strcmp(attrptr
->name
, "attributes-natural-language"))
2708 "Bad first attribute \"%s (%s%s)\" in group %s, "
2709 "expected \"attributes-natural-language "
2710 "(naturalLanguage)\" in group "
2711 "operation-attributes-tag (RFC 2911 section "
2713 attrptr
->name
? attrptr
->name
: "(null)",
2714 attrptr
->num_values
> 1 ? "1setOf " : "",
2715 ippTagString(attrptr
->value_tag
),
2716 ippTagString(attrptr
->group_tag
));
2719 if ((attrptr
= ippFindAttribute(response
, "status-message",
2720 IPP_TAG_ZERO
)) != NULL
)
2722 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2724 "status-message (text(255)) has wrong value tag "
2725 "%s (RFC 2911 section 3.1.6.2).",
2726 ippTagString(attrptr
->value_tag
));
2727 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2729 "status-message (text(255)) has wrong group tag "
2730 "%s (RFC 2911 section 3.1.6.2).",
2731 ippTagString(attrptr
->group_tag
));
2732 if (attrptr
->num_values
!= 1)
2734 "status-message (text(255)) has %d values "
2735 "(RFC 2911 section 3.1.6.2).",
2736 attrptr
->num_values
);
2737 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2738 strlen(attrptr
->values
[0].string
.text
) > 255)
2740 "status-message (text(255)) has bad length %d"
2741 " (RFC 2911 section 3.1.6.2).",
2742 (int)strlen(attrptr
->values
[0].string
.text
));
2745 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
2746 IPP_TAG_ZERO
)) != NULL
)
2748 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2750 "detailed-status-message (text(MAX)) has wrong "
2751 "value tag %s (RFC 2911 section 3.1.6.3).",
2752 ippTagString(attrptr
->value_tag
));
2753 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2755 "detailed-status-message (text(MAX)) has wrong "
2756 "group tag %s (RFC 2911 section 3.1.6.3).",
2757 ippTagString(attrptr
->group_tag
));
2758 if (attrptr
->num_values
!= 1)
2760 "detailed-status-message (text(MAX)) has %d values"
2761 " (RFC 2911 section 3.1.6.3).",
2762 attrptr
->num_values
);
2763 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2764 strlen(attrptr
->values
[0].string
.text
) > 1023)
2766 "detailed-status-message (text(MAX)) has bad "
2767 "length %d (RFC 2911 section 3.1.6.3).",
2768 (int)strlen(attrptr
->values
[0].string
.text
));
2771 a
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2773 for (attrptr
= response
->attrs
,
2774 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2776 attrptr
= attrptr
->next
)
2778 if (attrptr
->group_tag
!= group
)
2780 int out_of_order
= 0; /* Are attribute groups out-of-order? */
2783 switch (attrptr
->group_tag
)
2788 case IPP_TAG_OPERATION
:
2792 case IPP_TAG_UNSUPPORTED_GROUP
:
2793 if (group
!= IPP_TAG_OPERATION
)
2798 case IPP_TAG_PRINTER
:
2799 if (group
!= IPP_TAG_OPERATION
&&
2800 group
!= IPP_TAG_UNSUPPORTED_GROUP
)
2804 case IPP_TAG_SUBSCRIPTION
:
2805 if (group
> attrptr
->group_tag
&&
2806 group
!= IPP_TAG_DOCUMENT
)
2811 if (group
> attrptr
->group_tag
)
2817 add_stringf(errors
, "Attribute groups out of order (%s < %s)",
2818 ippTagString(attrptr
->group_tag
),
2819 ippTagString(group
));
2821 if (attrptr
->group_tag
!= IPP_TAG_ZERO
)
2822 group
= attrptr
->group_tag
;
2825 validate_attr(errors
, attrptr
);
2829 if (cupsArrayFind(a
, attrptr
->name
))
2830 add_stringf(errors
, "Duplicate \"%s\" attribute in %s group",
2831 attrptr
->name
, ippTagString(group
));
2833 cupsArrayAdd(a
, attrptr
->name
);
2840 * Now check the test-defined expected status-code and attribute
2844 for (i
= 0; i
< num_statuses
; i
++)
2846 if (statuses
[i
].if_defined
&&
2847 !get_variable(vars
, statuses
[i
].if_defined
))
2850 if (statuses
[i
].if_not_defined
&&
2851 get_variable(vars
, statuses
[i
].if_not_defined
))
2854 if (response
->request
.status
.status_code
== statuses
[i
].status
)
2856 if (statuses
[i
].repeat_match
&&
2857 repeat_count
< statuses
[i
].repeat_limit
)
2860 if (statuses
[i
].define_match
)
2861 set_variable(vars
, statuses
[i
].define_match
, "1");
2867 if (statuses
[i
].repeat_no_match
&&
2868 repeat_count
< statuses
[i
].repeat_limit
)
2871 if (statuses
[i
].define_no_match
)
2873 set_variable(vars
, statuses
[i
].define_no_match
, "1");
2879 if (i
== num_statuses
&& num_statuses
> 0)
2881 for (i
= 0; i
< num_statuses
; i
++)
2883 if (statuses
[i
].if_defined
&&
2884 !get_variable(vars
, statuses
[i
].if_defined
))
2887 if (statuses
[i
].if_not_defined
&&
2888 get_variable(vars
, statuses
[i
].if_not_defined
))
2891 if (!statuses
[i
].repeat_match
)
2892 add_stringf(errors
, "EXPECTED: STATUS %s (got %s)",
2893 ippErrorString(statuses
[i
].status
),
2894 ippErrorString(cupsLastError()));
2897 if ((attrptr
= ippFindAttribute(response
, "status-message",
2898 IPP_TAG_TEXT
)) != NULL
)
2899 add_stringf(errors
, "status-message=\"%s\"",
2900 attrptr
->values
[0].string
.text
);
2903 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
2905 if (expect
->if_defined
&& !get_variable(vars
, expect
->if_defined
))
2908 if (expect
->if_not_defined
&&
2909 get_variable(vars
, expect
->if_not_defined
))
2912 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
2914 if ((found
&& expect
->not_expect
) ||
2915 (!found
&& !(expect
->not_expect
|| expect
->optional
)) ||
2916 (found
&& !expect_matches(expect
, found
->value_tag
)) ||
2917 (found
&& expect
->in_group
&&
2918 found
->group_tag
!= expect
->in_group
))
2920 if (expect
->define_no_match
)
2921 set_variable(vars
, expect
->define_no_match
, "1");
2922 else if (!expect
->define_match
&& !expect
->define_value
)
2924 if (found
&& expect
->not_expect
)
2925 add_stringf(errors
, "NOT EXPECTED: %s", expect
->name
);
2926 else if (!found
&& !(expect
->not_expect
|| expect
->optional
))
2927 add_stringf(errors
, "EXPECTED: %s", expect
->name
);
2930 if (!expect_matches(expect
, found
->value_tag
))
2931 add_stringf(errors
, "EXPECTED: %s OF-TYPE %s (got %s)",
2932 expect
->name
, expect
->of_type
,
2933 ippTagString(found
->value_tag
));
2935 if (expect
->in_group
&& found
->group_tag
!= expect
->in_group
)
2936 add_stringf(errors
, "EXPECTED: %s IN-GROUP %s (got %s).",
2937 expect
->name
, ippTagString(expect
->in_group
),
2938 ippTagString(found
->group_tag
));
2942 if (expect
->repeat_no_match
&&
2943 repeat_count
< expect
->repeat_limit
)
2950 ippAttributeString(found
, buffer
, sizeof(buffer
));
2953 !with_value(NULL
, expect
->with_value
, expect
->with_flags
, found
,
2954 buffer
, sizeof(buffer
)))
2956 if (expect
->define_no_match
)
2957 set_variable(vars
, expect
->define_no_match
, "1");
2958 else if (!expect
->define_match
&& !expect
->define_value
&&
2959 !expect
->repeat_match
&& !expect
->repeat_no_match
)
2961 if (expect
->with_flags
& _CUPS_WITH_REGEX
)
2962 add_stringf(errors
, "EXPECTED: %s %s /%s/",
2964 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
2965 "WITH-ALL-VALUES" : "WITH-VALUE",
2966 expect
->with_value
);
2968 add_stringf(errors
, "EXPECTED: %s %s \"%s\"",
2970 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
2971 "WITH-ALL-VALUES" : "WITH-VALUE",
2972 expect
->with_value
);
2974 with_value(errors
, expect
->with_value
, expect
->with_flags
, found
,
2975 buffer
, sizeof(buffer
));
2978 if (expect
->repeat_no_match
&&
2979 repeat_count
< expect
->repeat_limit
)
2985 if (found
&& expect
->count
> 0 &&
2986 found
->num_values
!= expect
->count
)
2988 if (expect
->define_no_match
)
2989 set_variable(vars
, expect
->define_no_match
, "1");
2990 else if (!expect
->define_match
&& !expect
->define_value
)
2992 add_stringf(errors
, "EXPECTED: %s COUNT %d (got %d)", expect
->name
,
2993 expect
->count
, found
->num_values
);
2996 if (expect
->repeat_no_match
&&
2997 repeat_count
< expect
->repeat_limit
)
3003 if (found
&& expect
->same_count_as
)
3005 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
3008 if (!attrptr
|| attrptr
->num_values
!= found
->num_values
)
3010 if (expect
->define_no_match
)
3011 set_variable(vars
, expect
->define_no_match
, "1");
3012 else if (!expect
->define_match
&& !expect
->define_value
)
3016 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
3017 "(not returned)", expect
->name
,
3018 found
->num_values
, expect
->same_count_as
);
3019 else if (attrptr
->num_values
!= found
->num_values
)
3021 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
3022 "(%d values)", expect
->name
, found
->num_values
,
3023 expect
->same_count_as
, attrptr
->num_values
);
3026 if (expect
->repeat_no_match
&&
3027 repeat_count
< expect
->repeat_limit
)
3034 if (found
&& expect
->define_match
)
3035 set_variable(vars
, expect
->define_match
, "1");
3037 if (found
&& expect
->define_value
)
3038 set_variable(vars
, expect
->define_value
, buffer
);
3040 if (found
&& expect
->repeat_match
&&
3041 repeat_count
< expect
->repeat_limit
)
3047 * If we are going to repeat this test, sleep 1 second so we don't flood
3048 * the printer with requests...
3053 if (Output
== _CUPS_OUTPUT_TEST
)
3055 printf("%04d]\n", repeat_count
);
3059 sleep(repeat_interval
);
3060 repeat_interval
= _cupsNextDelay(repeat_interval
, &repeat_prev
);
3062 if (Output
== _CUPS_OUTPUT_TEST
)
3064 printf(" %-68.68s [", name
);
3069 while (repeat_test
);
3075 if (cupsArrayCount(errors
) > 0)
3076 prev_pass
= pass
= 0;
3083 if (Output
== _CUPS_OUTPUT_PLIST
)
3085 puts("<key>Successful</key>");
3086 puts(prev_pass
? "<true />" : "<false />");
3087 puts("<key>StatusCode</key>");
3088 print_xml_string("string", ippErrorString(cupsLastError()));
3089 puts("<key>ResponseAttributes</key>");
3092 for (attrptr
= response
? response
->attrs
: NULL
,
3093 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
3095 attrptr
= attrptr
->next
)
3096 print_attr(attrptr
, &group
);
3100 else if (Output
== _CUPS_OUTPUT_TEST
)
3102 puts(prev_pass
? "PASS]" : "FAIL]");
3104 if (!prev_pass
|| (Verbosity
&& response
))
3106 printf(" RECEIVED: %lu bytes in response\n",
3107 (unsigned long)ippLength(response
));
3108 printf(" status-code = %s (%s)\n", ippErrorString(cupsLastError()),
3109 cupsLastErrorString());
3113 for (attrptr
= response
->attrs
;
3115 attrptr
= attrptr
->next
)
3116 print_attr(attrptr
, NULL
);
3120 else if (!prev_pass
)
3121 fprintf(stderr
, "%s\n", cupsLastErrorString());
3123 if (prev_pass
&& Output
>= _CUPS_OUTPUT_LIST
&& !Verbosity
&&
3126 size_t width
; /* Length of value */
3128 for (i
= 0; i
< num_displayed
; i
++)
3130 widths
[i
] = strlen(displayed
[i
]);
3132 for (attrptr
= ippFindAttribute(response
, displayed
[i
], IPP_TAG_ZERO
);
3134 attrptr
= ippFindNextAttribute(response
, displayed
[i
],
3137 width
= ippAttributeString(attrptr
, NULL
, 0);
3138 if (width
> widths
[i
])
3143 if (Output
== _CUPS_OUTPUT_CSV
)
3144 print_csv(NULL
, num_displayed
, displayed
, widths
);
3146 print_line(NULL
, num_displayed
, displayed
, widths
);
3148 attrptr
= response
->attrs
;
3152 while (attrptr
&& attrptr
->group_tag
<= IPP_TAG_OPERATION
)
3153 attrptr
= attrptr
->next
;
3157 if (Output
== _CUPS_OUTPUT_CSV
)
3158 print_csv(attrptr
, num_displayed
, displayed
, widths
);
3160 print_line(attrptr
, num_displayed
, displayed
, widths
);
3162 while (attrptr
&& attrptr
->group_tag
> IPP_TAG_OPERATION
)
3163 attrptr
= attrptr
->next
;
3167 else if (!prev_pass
)
3169 if (Output
== _CUPS_OUTPUT_PLIST
)
3171 puts("<key>Errors</key>");
3174 for (error
= (char *)cupsArrayFirst(errors
);
3176 error
= (char *)cupsArrayNext(errors
))
3177 print_xml_string("string", error
);
3183 for (error
= (char *)cupsArrayFirst(errors
);
3185 error
= (char *)cupsArrayNext(errors
))
3186 printf(" %s\n", error
);
3190 if (num_displayed
> 0 && !Verbosity
&& response
&&
3191 Output
== _CUPS_OUTPUT_TEST
)
3193 for (attrptr
= response
->attrs
;
3195 attrptr
= attrptr
->next
)
3199 for (i
= 0; i
< num_displayed
; i
++)
3201 if (!strcmp(displayed
[i
], attrptr
->name
))
3203 print_attr(attrptr
, NULL
);
3213 if (Output
== _CUPS_OUTPUT_PLIST
)
3218 ippDelete(response
);
3221 for (i
= 0; i
< num_statuses
; i
++)
3223 if (statuses
[i
].if_defined
)
3224 free(statuses
[i
].if_defined
);
3225 if (statuses
[i
].if_not_defined
)
3226 free(statuses
[i
].if_not_defined
);
3227 if (statuses
[i
].define_match
)
3228 free(statuses
[i
].define_match
);
3229 if (statuses
[i
].define_no_match
)
3230 free(statuses
[i
].define_no_match
);
3234 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3237 if (expect
->of_type
)
3238 free(expect
->of_type
);
3239 if (expect
->same_count_as
)
3240 free(expect
->same_count_as
);
3241 if (expect
->if_defined
)
3242 free(expect
->if_defined
);
3243 if (expect
->if_not_defined
)
3244 free(expect
->if_not_defined
);
3245 if (expect
->with_value
)
3246 free(expect
->with_value
);
3247 if (expect
->define_match
)
3248 free(expect
->define_match
);
3249 if (expect
->define_no_match
)
3250 free(expect
->define_no_match
);
3251 if (expect
->define_value
)
3252 free(expect
->define_value
);
3256 for (i
= 0; i
< num_displayed
; i
++)
3260 if (!ignore_errors
&& !prev_pass
)
3266 cupsArrayDelete(errors
);
3273 ippDelete(response
);
3275 for (i
= 0; i
< num_statuses
; i
++)
3277 if (statuses
[i
].if_defined
)
3278 free(statuses
[i
].if_defined
);
3279 if (statuses
[i
].if_not_defined
)
3280 free(statuses
[i
].if_not_defined
);
3281 if (statuses
[i
].define_match
)
3282 free(statuses
[i
].define_match
);
3283 if (statuses
[i
].define_no_match
)
3284 free(statuses
[i
].define_no_match
);
3287 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3290 if (expect
->of_type
)
3291 free(expect
->of_type
);
3292 if (expect
->same_count_as
)
3293 free(expect
->same_count_as
);
3294 if (expect
->if_defined
)
3295 free(expect
->if_defined
);
3296 if (expect
->if_not_defined
)
3297 free(expect
->if_not_defined
);
3298 if (expect
->with_value
)
3299 free(expect
->with_value
);
3300 if (expect
->define_match
)
3301 free(expect
->define_match
);
3302 if (expect
->define_no_match
)
3303 free(expect
->define_no_match
);
3304 if (expect
->define_value
)
3305 free(expect
->define_value
);
3308 for (i
= 0; i
< num_displayed
; i
++)
3316 * 'expand_variables()' - Expand variables in a string.
3320 expand_variables(_cups_vars_t
*vars
, /* I - Variables */
3321 char *dst
, /* I - Destination string buffer */
3322 const char *src
, /* I - Source string */
3323 size_t dstsize
) /* I - Size of destination buffer */
3325 char *dstptr
, /* Pointer into destination */
3326 *dstend
, /* End of destination */
3327 temp
[256], /* Temporary string */
3328 *tempptr
; /* Pointer into temporary string */
3329 const char *value
; /* Value to substitute */
3333 dstend
= dst
+ dstsize
- 1;
3335 while (*src
&& dstptr
< dstend
)
3340 * Substitute a string/number...
3343 if (!strncmp(src
, "$$", 2))
3348 else if (!strncmp(src
, "$ENV[", 5))
3350 strlcpy(temp
, src
+ 5, sizeof(temp
));
3352 for (tempptr
= temp
; *tempptr
; tempptr
++)
3353 if (*tempptr
== ']')
3359 value
= getenv(temp
);
3360 src
+= tempptr
- temp
+ 5;
3364 strlcpy(temp
, src
+ 1, sizeof(temp
));
3366 for (tempptr
= temp
; *tempptr
; tempptr
++)
3367 if (!isalnum(*tempptr
& 255) && *tempptr
!= '-' && *tempptr
!= '_')
3373 if (!strcmp(temp
, "uri"))
3375 else if (!strcmp(temp
, "filename"))
3376 value
= vars
->filename
;
3377 else if (!strcmp(temp
, "scheme") || !strcmp(temp
, "method"))
3378 value
= vars
->scheme
;
3379 else if (!strcmp(temp
, "username"))
3380 value
= vars
->userpass
;
3381 else if (!strcmp(temp
, "hostname"))
3382 value
= vars
->hostname
;
3383 else if (!strcmp(temp
, "port"))
3385 snprintf(temp
, sizeof(temp
), "%d", vars
->port
);
3388 else if (!strcmp(temp
, "resource"))
3389 value
= vars
->resource
;
3390 else if (!strcmp(temp
, "user"))
3393 value
= get_variable(vars
, temp
);
3395 src
+= tempptr
- temp
+ 1;
3405 strlcpy(dstptr
, value
, dstend
- dstptr
+ 1);
3406 dstptr
+= strlen(dstptr
);
3418 * 'expect_matches()' - Return true if the tag matches the specification.
3421 static int /* O - 1 if matches, 0 otherwise */
3423 _cups_expect_t
*expect
, /* I - Expected attribute */
3424 ipp_tag_t value_tag
) /* I - Value tag for attribute */
3426 int match
; /* Match? */
3427 char *of_type
, /* Type name to match */
3428 *next
, /* Next name to match */
3429 sep
; /* Separator character */
3433 * If we don't expect a particular type, return immediately...
3436 if (!expect
->of_type
)
3440 * Parse the "of_type" value since the string can contain multiple attribute
3441 * types separated by "," or "|"...
3444 for (of_type
= expect
->of_type
, match
= 0; !match
&& *of_type
; of_type
= next
)
3447 * Find the next separator, and set it (temporarily) to nul if present.
3450 for (next
= of_type
; *next
&& *next
!= '|' && *next
!= ','; next
++);
3452 if ((sep
= *next
) != '\0')
3456 * Support some meta-types to make it easier to write the test file.
3459 if (!strcmp(of_type
, "text"))
3460 match
= value_tag
== IPP_TAG_TEXTLANG
|| value_tag
== IPP_TAG_TEXT
;
3461 else if (!strcmp(of_type
, "name"))
3462 match
= value_tag
== IPP_TAG_NAMELANG
|| value_tag
== IPP_TAG_NAME
;
3463 else if (!strcmp(of_type
, "collection"))
3464 match
= value_tag
== IPP_TAG_BEGIN_COLLECTION
;
3466 match
= value_tag
== ippTagValue(of_type
);
3469 * Restore the separator if we have one...
3481 * 'get_collection()' - Get a collection value from the current test file.
3484 static ipp_t
* /* O - Collection value */
3485 get_collection(_cups_vars_t
*vars
, /* I - Variables */
3486 FILE *fp
, /* I - File to read from */
3487 int *linenum
) /* IO - Line number */
3489 char token
[1024], /* Token from file */
3490 temp
[1024], /* Temporary string */
3491 attr
[128]; /* Attribute name */
3492 ipp_tag_t value
; /* Current value type */
3493 ipp_t
*col
= ippNew(); /* Collection value */
3494 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
3497 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
3499 if (!strcmp(token
, "}"))
3501 else if (!strcmp(token
, "{") && lastcol
)
3504 * Another collection value
3507 ipp_t
*subcol
= get_collection(vars
, fp
, linenum
);
3508 /* Collection value */
3512 ipp_attribute_t
*tempcol
; /* Pointer to new buffer */
3516 * Reallocate memory...
3519 if ((tempcol
= realloc(lastcol
, sizeof(ipp_attribute_t
) +
3520 (lastcol
->num_values
+ 1) *
3521 sizeof(_ipp_value_t
))) == NULL
)
3523 print_fatal_error("Unable to allocate memory on line %d.", *linenum
);
3527 if (tempcol
!= lastcol
)
3530 * Reset pointers in the list...
3534 col
->prev
->next
= tempcol
;
3536 col
->attrs
= tempcol
;
3538 lastcol
= col
->current
= col
->last
= tempcol
;
3541 lastcol
->values
[lastcol
->num_values
].collection
= subcol
;
3542 lastcol
->num_values
++;
3547 else if (!_cups_strcasecmp(token
, "MEMBER"))
3555 if (!get_token(fp
, token
, sizeof(token
), linenum
))
3557 print_fatal_error("Missing MEMBER value tag on line %d.", *linenum
);
3561 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
3563 print_fatal_error("Bad MEMBER value tag \"%s\" on line %d.", token
,
3568 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
3570 print_fatal_error("Missing MEMBER name on line %d.", *linenum
);
3574 if (!get_token(fp
, temp
, sizeof(temp
), linenum
))
3576 print_fatal_error("Missing MEMBER value on line %d.", *linenum
);
3580 expand_variables(vars
, token
, temp
, sizeof(token
));
3584 case IPP_TAG_BOOLEAN
:
3585 if (!_cups_strcasecmp(token
, "true"))
3586 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
3588 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, atoi(token
));
3591 case IPP_TAG_INTEGER
:
3593 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
3596 case IPP_TAG_RESOLUTION
:
3598 int xres
, /* X resolution */
3599 yres
; /* Y resolution */
3600 char units
[6]; /* Units */
3602 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
3603 (_cups_strcasecmp(units
, "dpi") &&
3604 _cups_strcasecmp(units
, "dpc") &&
3605 _cups_strcasecmp(units
, "dpcm") &&
3606 _cups_strcasecmp(units
, "other")))
3608 print_fatal_error("Bad resolution value \"%s\" on line %d.",
3613 if (!_cups_strcasecmp(units
, "dpi"))
3614 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
3616 else if (!_cups_strcasecmp(units
, "dpc") ||
3617 !_cups_strcasecmp(units
, "dpcm"))
3618 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
3621 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
3626 case IPP_TAG_RANGE
:
3628 int lowers
[4], /* Lower value */
3629 uppers
[4], /* Upper values */
3630 num_vals
; /* Number of values */
3633 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
3634 lowers
+ 0, uppers
+ 0,
3635 lowers
+ 1, uppers
+ 1,
3636 lowers
+ 2, uppers
+ 2,
3637 lowers
+ 3, uppers
+ 3);
3639 if ((num_vals
& 1) || num_vals
== 0)
3641 print_fatal_error("Bad rangeOfInteger value \"%s\" on line %d.",
3646 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
3651 case IPP_TAG_BEGIN_COLLECTION
:
3652 if (!strcmp(token
, "{"))
3654 ipp_t
*subcol
= get_collection(vars
, fp
, linenum
);
3655 /* Collection value */
3659 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
3667 print_fatal_error("Bad collection value on line %d.", *linenum
);
3671 case IPP_TAG_STRING
:
3672 ippAddOctetString(col
, IPP_TAG_ZERO
, attr
, token
, strlen(token
));
3676 if (!strchr(token
, ','))
3677 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
3681 * Multiple string values...
3684 int num_values
; /* Number of values */
3685 char *values
[100], /* Values */
3686 *ptr
; /* Pointer to next value */
3692 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
3695 values
[num_values
] = ptr
;
3699 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
3700 NULL
, (const char **)values
);
3710 * If we get here there was a parse error; free memory and return.
3722 * 'get_filename()' - Get a filename based on the current test file.
3725 static char * /* O - Filename */
3726 get_filename(const char *testfile
, /* I - Current test file */
3727 char *dst
, /* I - Destination filename */
3728 const char *src
, /* I - Source filename */
3729 size_t dstsize
) /* I - Size of destination buffer */
3731 char *dstptr
; /* Pointer into destination */
3732 _cups_globals_t
*cg
= _cupsGlobals();
3736 if (*src
== '<' && src
[strlen(src
) - 1] == '>')
3739 * Map <filename> to CUPS_DATADIR/ipptool/filename...
3742 snprintf(dst
, dstsize
, "%s/ipptool/%s", cg
->cups_datadir
, src
+ 1);
3743 dstptr
= dst
+ strlen(dst
) - 1;
3747 else if (*src
== '/' || !strchr(testfile
, '/')
3749 || (isalpha(*src
& 255) && src
[1] == ':')
3754 * Use the path as-is...
3757 strlcpy(dst
, src
, dstsize
);
3762 * Make path relative to testfile...
3765 strlcpy(dst
, testfile
, dstsize
);
3766 if ((dstptr
= strrchr(dst
, '/')) != NULL
)
3769 dstptr
= dst
; /* Should never happen */
3771 strlcpy(dstptr
, src
, dstsize
- (dstptr
- dst
));
3779 * 'get_token()' - Get a token from a file.
3782 static char * /* O - Token from file or NULL on EOF */
3783 get_token(FILE *fp
, /* I - File to read from */
3784 char *buf
, /* I - Buffer to read into */
3785 int buflen
, /* I - Length of buffer */
3786 int *linenum
) /* IO - Current line number */
3788 int ch
, /* Character from file */
3789 quote
; /* Quoting character */
3790 char *bufptr
, /* Pointer into buffer */
3791 *bufend
; /* End of buffer */
3797 * Skip whitespace...
3800 while (isspace(ch
= getc(fp
)))
3812 else if (ch
== '\'' || ch
== '\"')
3815 * Quoted text or regular expression...
3820 bufend
= buf
+ buflen
- 1;
3822 while ((ch
= getc(fp
)) != EOF
)
3827 * Escape next character...
3830 if (bufptr
< bufend
)
3833 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
3836 else if (ch
== quote
)
3838 else if (bufptr
< bufend
)
3852 while ((ch
= getc(fp
)) != EOF
)
3861 * Whitespace delimited text...
3867 bufend
= buf
+ buflen
- 1;
3869 while ((ch
= getc(fp
)) != EOF
)
3870 if (isspace(ch
) || ch
== '#')
3872 else if (bufptr
< bufend
)
3877 else if (ch
== '\n')
3889 * 'get_variable()' - Get the value of a variable.
3892 static char * /* O - Value or NULL */
3893 get_variable(_cups_vars_t
*vars
, /* I - Variables */
3894 const char *name
) /* I - Variable name */
3896 _cups_var_t key
, /* Search key */
3897 *match
; /* Matching variable, if any */
3900 key
.name
= (char *)name
;
3901 match
= cupsArrayFind(vars
->vars
, &key
);
3903 return (match
? match
->value
: NULL
);
3908 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
3912 static char * /* O - ISO 8601 date/time string */
3913 iso_date(ipp_uchar_t
*date
) /* I - IPP (RFC 1903) date/time value */
3915 time_t utctime
; /* UTC time since 1970 */
3916 struct tm
*utcdate
; /* UTC date/time */
3917 static char buffer
[255]; /* String buffer */
3920 utctime
= ippDateToTime(date
);
3921 utcdate
= gmtime(&utctime
);
3923 snprintf(buffer
, sizeof(buffer
), "%04d-%02d-%02dT%02d:%02d:%02dZ",
3924 utcdate
->tm_year
+ 1900, utcdate
->tm_mon
+ 1, utcdate
->tm_mday
,
3925 utcdate
->tm_hour
, utcdate
->tm_min
, utcdate
->tm_sec
);
3932 * 'password_cb()' - Password callback for authenticated tests.
3935 static const char * /* O - Password */
3936 password_cb(const char *prompt
) /* I - Prompt (unused) */
3945 * 'print_attr()' - Print an attribute on the screen.
3949 print_attr(ipp_attribute_t
*attr
, /* I - Attribute to print */
3950 ipp_tag_t
*group
) /* IO - Current group */
3952 int i
; /* Looping var */
3953 ipp_attribute_t
*colattr
; /* Collection attribute */
3956 if (Output
== _CUPS_OUTPUT_PLIST
)
3958 if (!attr
->name
|| (group
&& *group
!= attr
->group_tag
))
3960 if (attr
->group_tag
!= IPP_TAG_ZERO
)
3967 *group
= attr
->group_tag
;
3973 print_xml_string("key", attr
->name
);
3974 if (attr
->num_values
> 1)
3977 switch (attr
->value_tag
)
3979 case IPP_TAG_INTEGER
:
3981 for (i
= 0; i
< attr
->num_values
; i
++)
3982 if (Output
== _CUPS_OUTPUT_PLIST
)
3983 printf("<integer>%d</integer>\n", attr
->values
[i
].integer
);
3985 printf("%d ", attr
->values
[i
].integer
);
3988 case IPP_TAG_BOOLEAN
:
3989 for (i
= 0; i
< attr
->num_values
; i
++)
3990 if (Output
== _CUPS_OUTPUT_PLIST
)
3991 puts(attr
->values
[i
].boolean
? "<true />" : "<false />");
3992 else if (attr
->values
[i
].boolean
)
3993 fputs("true ", stdout
);
3995 fputs("false ", stdout
);
3998 case IPP_TAG_RANGE
:
3999 for (i
= 0; i
< attr
->num_values
; i
++)
4000 if (Output
== _CUPS_OUTPUT_PLIST
)
4001 printf("<dict><key>lower</key><integer>%d</integer>"
4002 "<key>upper</key><integer>%d</integer></dict>\n",
4003 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
4005 printf("%d-%d ", attr
->values
[i
].range
.lower
,
4006 attr
->values
[i
].range
.upper
);
4009 case IPP_TAG_RESOLUTION
:
4010 for (i
= 0; i
< attr
->num_values
; i
++)
4011 if (Output
== _CUPS_OUTPUT_PLIST
)
4012 printf("<dict><key>xres</key><integer>%d</integer>"
4013 "<key>yres</key><integer>%d</integer>"
4014 "<key>units</key><string>%s</string></dict>\n",
4015 attr
->values
[i
].resolution
.xres
,
4016 attr
->values
[i
].resolution
.yres
,
4017 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
4020 printf("%dx%d%s ", attr
->values
[i
].resolution
.xres
,
4021 attr
->values
[i
].resolution
.yres
,
4022 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
4027 for (i
= 0; i
< attr
->num_values
; i
++)
4028 if (Output
== _CUPS_OUTPUT_PLIST
)
4029 printf("<date>%s</date>\n", iso_date(attr
->values
[i
].date
));
4031 printf("%s ", iso_date(attr
->values
[i
].date
));
4034 case IPP_TAG_STRING
:
4035 for (i
= 0; i
< attr
->num_values
; i
++)
4037 if (Output
== _CUPS_OUTPUT_PLIST
)
4039 char buffer
[IPP_MAX_LENGTH
* 5 / 4 + 1];
4042 printf("<data>%s</data>\n",
4043 httpEncode64_2(buffer
, sizeof(buffer
),
4044 attr
->values
[i
].unknown
.data
,
4045 attr
->values
[i
].unknown
.length
));
4049 char *ptr
, /* Pointer into data */
4050 *end
; /* End of data */
4053 for (ptr
= attr
->values
[i
].unknown
.data
,
4054 end
= ptr
+ attr
->values
[i
].unknown
.length
;
4058 if (*ptr
== '\\' || *ptr
== '\"')
4059 printf("\\%c", *ptr
);
4060 else if (!isprint(*ptr
& 255))
4061 printf("\\%03o", *ptr
& 255);
4072 case IPP_TAG_KEYWORD
:
4073 case IPP_TAG_CHARSET
:
4075 case IPP_TAG_MIMETYPE
:
4076 case IPP_TAG_LANGUAGE
:
4077 for (i
= 0; i
< attr
->num_values
; i
++)
4078 if (Output
== _CUPS_OUTPUT_PLIST
)
4079 print_xml_string("string", attr
->values
[i
].string
.text
);
4081 printf("\"%s\" ", attr
->values
[i
].string
.text
);
4084 case IPP_TAG_TEXTLANG
:
4085 case IPP_TAG_NAMELANG
:
4086 for (i
= 0; i
< attr
->num_values
; i
++)
4087 if (Output
== _CUPS_OUTPUT_PLIST
)
4089 fputs("<dict><key>language</key><string>", stdout
);
4090 print_xml_string(NULL
, attr
->values
[i
].string
.language
);
4091 fputs("</string><key>string</key><string>", stdout
);
4092 print_xml_string(NULL
, attr
->values
[i
].string
.text
);
4093 puts("</string></dict>");
4096 printf("\"%s\"[%s] ", attr
->values
[i
].string
.text
,
4097 attr
->values
[i
].string
.language
);
4100 case IPP_TAG_BEGIN_COLLECTION
:
4101 for (i
= 0; i
< attr
->num_values
; i
++)
4103 if (Output
== _CUPS_OUTPUT_PLIST
)
4106 for (colattr
= attr
->values
[i
].collection
->attrs
;
4108 colattr
= colattr
->next
)
4109 print_attr(colattr
, NULL
);
4117 print_col(attr
->values
[i
].collection
);
4123 if (Output
== _CUPS_OUTPUT_PLIST
)
4124 printf("<string><<%s>></string>\n",
4125 ippTagString(attr
->value_tag
));
4127 fputs(ippTagString(attr
->value_tag
), stdout
);
4131 if (attr
->num_values
> 1)
4136 char buffer
[8192]; /* Value buffer */
4138 if (Output
== _CUPS_OUTPUT_TEST
)
4142 puts(" -- separator --");
4146 printf(" %s (%s%s) = ", attr
->name
,
4147 attr
->num_values
> 1 ? "1setOf " : "",
4148 ippTagString(attr
->value_tag
));
4151 ippAttributeString(attr
, buffer
, sizeof(buffer
));
4158 * 'print_col()' - Print a collection attribute on the screen.
4162 print_col(ipp_t
*col
) /* I - Collection attribute to print */
4164 int i
; /* Looping var */
4165 ipp_attribute_t
*attr
; /* Current attribute in collection */
4168 fputs("{ ", stdout
);
4169 for (attr
= col
->attrs
; attr
; attr
= attr
->next
)
4171 printf("%s (%s%s) = ", attr
->name
, attr
->num_values
> 1 ? "1setOf " : "",
4172 ippTagString(attr
->value_tag
));
4174 switch (attr
->value_tag
)
4176 case IPP_TAG_INTEGER
:
4178 for (i
= 0; i
< attr
->num_values
; i
++)
4179 printf("%d ", attr
->values
[i
].integer
);
4182 case IPP_TAG_BOOLEAN
:
4183 for (i
= 0; i
< attr
->num_values
; i
++)
4184 if (attr
->values
[i
].boolean
)
4190 case IPP_TAG_NOVALUE
:
4194 case IPP_TAG_RANGE
:
4195 for (i
= 0; i
< attr
->num_values
; i
++)
4196 printf("%d-%d ", attr
->values
[i
].range
.lower
,
4197 attr
->values
[i
].range
.upper
);
4200 case IPP_TAG_RESOLUTION
:
4201 for (i
= 0; i
< attr
->num_values
; i
++)
4202 printf("%dx%d%s ", attr
->values
[i
].resolution
.xres
,
4203 attr
->values
[i
].resolution
.yres
,
4204 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
4208 case IPP_TAG_STRING
:
4211 case IPP_TAG_KEYWORD
:
4212 case IPP_TAG_CHARSET
:
4214 case IPP_TAG_MIMETYPE
:
4215 case IPP_TAG_LANGUAGE
:
4216 for (i
= 0; i
< attr
->num_values
; i
++)
4217 printf("\"%s\" ", attr
->values
[i
].string
.text
);
4220 case IPP_TAG_TEXTLANG
:
4221 case IPP_TAG_NAMELANG
:
4222 for (i
= 0; i
< attr
->num_values
; i
++)
4223 printf("\"%s\"[%s] ", attr
->values
[i
].string
.text
,
4224 attr
->values
[i
].string
.language
);
4227 case IPP_TAG_BEGIN_COLLECTION
:
4228 for (i
= 0; i
< attr
->num_values
; i
++)
4230 print_col(attr
->values
[i
].collection
);
4236 break; /* anti-compiler-warning-code */
4245 * 'print_csv()' - Print a line of CSV text.
4250 ipp_attribute_t
*attr
, /* I - First attribute for line */
4251 int num_displayed
, /* I - Number of attributes to display */
4252 char **displayed
, /* I - Attributes to display */
4253 size_t *widths
) /* I - Column widths */
4255 int i
; /* Looping var */
4256 size_t maxlength
; /* Max length of all columns */
4257 char *buffer
, /* String buffer */
4258 *bufptr
; /* Pointer into buffer */
4259 ipp_attribute_t
*current
; /* Current attribute */
4263 * Get the maximum string length we have to show and allocate...
4266 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4267 if (widths
[i
] > maxlength
)
4268 maxlength
= widths
[i
];
4272 if ((buffer
= malloc(maxlength
)) == NULL
)
4276 * Loop through the attributes to display...
4281 for (i
= 0; i
< num_displayed
; i
++)
4288 for (current
= attr
; current
; current
= current
->next
)
4292 else if (!strcmp(current
->name
, displayed
[i
]))
4294 ippAttributeString(current
, buffer
, maxlength
);
4299 if (strchr(buffer
, ',') != NULL
|| strchr(buffer
, '\"') != NULL
||
4300 strchr(buffer
, '\\') != NULL
)
4303 for (bufptr
= buffer
; *bufptr
; bufptr
++)
4305 if (*bufptr
== '\\' || *bufptr
== '\"')
4312 fputs(buffer
, stdout
);
4318 for (i
= 0; i
< num_displayed
; i
++)
4323 fputs(displayed
[i
], stdout
);
4333 * 'print_fatal_error()' - Print a fatal error message.
4337 print_fatal_error(const char *s
, /* I - Printf-style format string */
4338 ...) /* I - Additional arguments as needed */
4340 char buffer
[10240]; /* Format buffer */
4341 va_list ap
; /* Pointer to arguments */
4345 * Format the error message...
4349 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
4356 if (Output
== _CUPS_OUTPUT_PLIST
)
4359 print_xml_trailer(0, buffer
);
4362 _cupsLangPrintf(stderr
, "ipptool: %s", buffer
);
4367 * 'print_line()' - Print a line of formatted or CSV text.
4372 ipp_attribute_t
*attr
, /* I - First attribute for line */
4373 int num_displayed
, /* I - Number of attributes to display */
4374 char **displayed
, /* I - Attributes to display */
4375 size_t *widths
) /* I - Column widths */
4377 int i
; /* Looping var */
4378 size_t maxlength
; /* Max length of all columns */
4379 char *buffer
; /* String buffer */
4380 ipp_attribute_t
*current
; /* Current attribute */
4384 * Get the maximum string length we have to show and allocate...
4387 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4388 if (widths
[i
] > maxlength
)
4389 maxlength
= widths
[i
];
4393 if ((buffer
= malloc(maxlength
)) == NULL
)
4397 * Loop through the attributes to display...
4402 for (i
= 0; i
< num_displayed
; i
++)
4409 for (current
= attr
; current
; current
= current
->next
)
4413 else if (!strcmp(current
->name
, displayed
[i
]))
4415 ippAttributeString(current
, buffer
, maxlength
);
4420 printf("%*s", (int)-widths
[i
], buffer
);
4426 for (i
= 0; i
< num_displayed
; i
++)
4431 printf("%*s", (int)-widths
[i
], displayed
[i
]);
4435 for (i
= 0; i
< num_displayed
; i
++)
4440 memset(buffer
, '-', widths
[i
]);
4441 buffer
[widths
[i
]] = '\0';
4442 fputs(buffer
, stdout
);
4452 * 'print_xml_header()' - Print a standard XML plist header.
4456 print_xml_header(void)
4460 puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
4461 puts("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
4462 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
4463 puts("<plist version=\"1.0\">");
4465 puts("<key>ipptoolVersion</key>");
4466 puts("<string>" CUPS_SVERSION
"</string>");
4467 puts("<key>Transfer</key>");
4468 printf("<string>%s</string>\n",
4469 Transfer
== _CUPS_TRANSFER_AUTO
? "auto" :
4470 Transfer
== _CUPS_TRANSFER_CHUNKED
? "chunked" : "length");
4471 puts("<key>Tests</key>");
4480 * 'print_xml_string()' - Print an XML string with escaping.
4484 print_xml_string(const char *element
, /* I - Element name or NULL */
4485 const char *s
) /* I - String to print */
4488 printf("<%s>", element
);
4493 fputs("&", stdout
);
4495 fputs("<", stdout
);
4497 fputs(">", stdout
);
4498 else if ((*s
& 0xe0) == 0xc0)
4501 * Validate UTF-8 two-byte sequence...
4504 if ((s
[1] & 0xc0) != 0x80)
4515 else if ((*s
& 0xf0) == 0xe0)
4518 * Validate UTF-8 three-byte sequence...
4521 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80)
4533 else if ((*s
& 0xf8) == 0xf0)
4536 * Validate UTF-8 four-byte sequence...
4539 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80 ||
4540 (s
[3] & 0xc0) != 0x80)
4553 else if ((*s
& 0x80) || (*s
< ' ' && !isspace(*s
& 255)))
4556 * Invalid control character...
4568 printf("</%s>\n", element
);
4573 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
4577 print_xml_trailer(int success
, /* I - 1 on success, 0 on failure */
4578 const char *message
) /* I - Error message or NULL */
4583 puts("<key>Successful</key>");
4584 puts(success
? "<true />" : "<false />");
4587 puts("<key>ErrorMessage</key>");
4588 print_xml_string("string", message
);
4599 * 'set_variable()' - Set a variable value.
4603 set_variable(_cups_vars_t
*vars
, /* I - Variables */
4604 const char *name
, /* I - Variable name */
4605 const char *value
) /* I - Value string */
4607 _cups_var_t key
, /* Search key */
4608 *var
; /* New variable */
4611 if (!_cups_strcasecmp(name
, "filename"))
4614 free(vars
->filename
);
4616 vars
->filename
= strdup(value
);
4620 key
.name
= (char *)name
;
4621 if ((var
= cupsArrayFind(vars
->vars
, &key
)) != NULL
)
4624 var
->value
= strdup(value
);
4626 else if ((var
= malloc(sizeof(_cups_var_t
))) == NULL
)
4628 print_fatal_error("Unable to allocate memory for variable \"%s\".", name
);
4633 var
->name
= strdup(name
);
4634 var
->value
= strdup(value
);
4636 cupsArrayAdd(vars
->vars
, var
);
4643 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
4647 sigterm_handler(int sig
) /* I - Signal number (unused) */
4653 signal(SIGINT
, SIG_DFL
);
4654 signal(SIGTERM
, SIG_DFL
);
4660 * 'timeout_cb()' - Handle HTTP timeouts.
4663 static int /* O - 1 to continue, 0 to cancel */
4664 timeout_cb(http_t
*http
, /* I - Connection to server (unused) */
4665 void *user_data
) /* I - User data (unused) */
4670 /* Always cancel on timeout */
4676 * 'usage()' - Show program usage.
4682 _cupsLangPuts(stderr
, _("Usage: ipptool [options] URI filename [ ... "
4684 _cupsLangPuts(stderr
, _("Options:"));
4685 _cupsLangPuts(stderr
, _(" -4 Connect using IPv4."));
4686 _cupsLangPuts(stderr
, _(" -6 Connect using IPv6."));
4687 _cupsLangPuts(stderr
, _(" -C Send requests using "
4688 "chunking (default)."));
4689 _cupsLangPuts(stdout
, _(" -E Test with HTTP Upgrade to "
4691 _cupsLangPuts(stderr
, _(" -I Ignore errors."));
4692 _cupsLangPuts(stderr
, _(" -L Send requests using "
4693 "content-length."));
4694 _cupsLangPuts(stderr
, _(" -S Test with SSL "
4696 _cupsLangPuts(stderr
, _(" -T seconds Set the receive/send "
4697 "timeout in seconds."));
4698 _cupsLangPuts(stderr
, _(" -V version Set default IPP "
4700 _cupsLangPuts(stderr
, _(" -X Produce XML plist instead "
4702 _cupsLangPuts(stderr
, _(" -d name=value Set named variable to "
4704 _cupsLangPuts(stderr
, _(" -f filename Set default request "
4706 _cupsLangPuts(stderr
, _(" -i seconds Repeat the last file with "
4707 "the given time interval."));
4708 _cupsLangPuts(stderr
, _(" -n count Repeat the last file the "
4709 "given number of times."));
4710 _cupsLangPuts(stderr
, _(" -q Run silently."));
4711 _cupsLangPuts(stderr
, _(" -t Produce a test report."));
4712 _cupsLangPuts(stderr
, _(" -v Be verbose."));
4719 * 'validate_attr()' - Determine whether an attribute is valid.
4722 static int /* O - 1 if valid, 0 otherwise */
4723 validate_attr(cups_array_t
*errors
, /* I - Errors array */
4724 ipp_attribute_t
*attr
) /* I - Attribute to validate */
4726 int i
; /* Looping var */
4727 char scheme
[64], /* Scheme from URI */
4728 userpass
[256], /* Username/password from URI */
4729 hostname
[256], /* Hostname from URI */
4730 resource
[1024]; /* Resource from URI */
4731 int port
, /* Port number from URI */
4732 uri_status
, /* URI separation status */
4733 valid
= 1; /* Is the attribute valid? */
4734 const char *ptr
; /* Pointer into string */
4735 ipp_attribute_t
*colattr
; /* Collection attribute */
4736 regex_t re
; /* Regular expression */
4737 ipp_uchar_t
*date
; /* Current date value */
4748 * Validate the attribute name.
4751 for (ptr
= attr
->name
; *ptr
; ptr
++)
4752 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' && *ptr
!= '_')
4755 if (*ptr
|| ptr
== attr
->name
)
4760 "\"%s\": Bad attribute name - invalid character "
4761 "(RFC 2911 section 4.1.3).", attr
->name
);
4764 if ((ptr
- attr
->name
) > 255)
4769 "\"%s\": Bad attribute name - bad length "
4770 "(RFC 2911 section 4.1.3).", attr
->name
);
4773 switch (attr
->value_tag
)
4775 case IPP_TAG_INTEGER
:
4778 case IPP_TAG_BOOLEAN
:
4779 for (i
= 0; i
< attr
->num_values
; i
++)
4781 if (attr
->values
[i
].boolean
!= 0 &&
4782 attr
->values
[i
].boolean
!= 1)
4787 "\"%s\": Bad boolen value %d "
4788 "(RFC 2911 section 4.1.11).", attr
->name
,
4789 attr
->values
[i
].boolean
);
4795 for (i
= 0; i
< attr
->num_values
; i
++)
4797 if (attr
->values
[i
].integer
< 1)
4802 "\"%s\": Bad enum value %d - out of range "
4803 "(RFC 2911 section 4.1.4).", attr
->name
,
4804 attr
->values
[i
].integer
);
4809 case IPP_TAG_STRING
:
4810 for (i
= 0; i
< attr
->num_values
; i
++)
4812 if (attr
->values
[i
].unknown
.length
> IPP_MAX_OCTETSTRING
)
4817 "\"%s\": Bad octetString value - bad length %d "
4818 "(RFC 2911 section 4.1.10).", attr
->name
,
4819 attr
->values
[i
].unknown
.length
);
4825 for (i
= 0; i
< attr
->num_values
; i
++)
4827 date
= attr
->values
[i
].date
;
4829 if (date
[2] < 1 || date
[2] > 12)
4834 "\"%s\": Bad dateTime month %u "
4835 "(RFC 2911 section 4.1.14).", attr
->name
, date
[2]);
4838 if (date
[3] < 1 || date
[3] > 31)
4843 "\"%s\": Bad dateTime day %u "
4844 "(RFC 2911 section 4.1.14).", attr
->name
, date
[3]);
4852 "\"%s\": Bad dateTime hours %u "
4853 "(RFC 2911 section 4.1.14).", attr
->name
, date
[4]);
4861 "\"%s\": Bad dateTime minutes %u "
4862 "(RFC 2911 section 4.1.14).", attr
->name
, date
[5]);
4870 "\"%s\": Bad dateTime seconds %u "
4871 "(RFC 2911 section 4.1.14).", attr
->name
, date
[6]);
4879 "\"%s\": Bad dateTime deciseconds %u "
4880 "(RFC 2911 section 4.1.14).", attr
->name
, date
[7]);
4883 if (date
[8] != '-' && date
[8] != '+')
4888 "\"%s\": Bad dateTime UTC sign '%c' "
4889 "(RFC 2911 section 4.1.14).", attr
->name
, date
[8]);
4897 "\"%s\": Bad dateTime UTC hours %u "
4898 "(RFC 2911 section 4.1.14).", attr
->name
, date
[9]);
4906 "\"%s\": Bad dateTime UTC minutes %u "
4907 "(RFC 2911 section 4.1.14).", attr
->name
, date
[10]);
4912 case IPP_TAG_RESOLUTION
:
4913 for (i
= 0; i
< attr
->num_values
; i
++)
4915 if (attr
->values
[i
].resolution
.xres
<= 0)
4920 "\"%s\": Bad resolution value %dx%d%s - cross "
4921 "feed resolution must be positive "
4922 "(RFC 2911 section 4.1.15).", attr
->name
,
4923 attr
->values
[i
].resolution
.xres
,
4924 attr
->values
[i
].resolution
.yres
,
4925 attr
->values
[i
].resolution
.units
==
4926 IPP_RES_PER_INCH
? "dpi" :
4927 attr
->values
[i
].resolution
.units
==
4928 IPP_RES_PER_CM
? "dpcm" : "unknown");
4931 if (attr
->values
[i
].resolution
.yres
<= 0)
4936 "\"%s\": Bad resolution value %dx%d%s - feed "
4937 "resolution must be positive "
4938 "(RFC 2911 section 4.1.15).", attr
->name
,
4939 attr
->values
[i
].resolution
.xres
,
4940 attr
->values
[i
].resolution
.yres
,
4941 attr
->values
[i
].resolution
.units
==
4942 IPP_RES_PER_INCH
? "dpi" :
4943 attr
->values
[i
].resolution
.units
==
4944 IPP_RES_PER_CM
? "dpcm" : "unknown");
4947 if (attr
->values
[i
].resolution
.units
!= IPP_RES_PER_INCH
&&
4948 attr
->values
[i
].resolution
.units
!= IPP_RES_PER_CM
)
4953 "\"%s\": Bad resolution value %dx%d%s - bad "
4954 "units value (RFC 2911 section 4.1.15).",
4955 attr
->name
, attr
->values
[i
].resolution
.xres
,
4956 attr
->values
[i
].resolution
.yres
,
4957 attr
->values
[i
].resolution
.units
==
4958 IPP_RES_PER_INCH
? "dpi" :
4959 attr
->values
[i
].resolution
.units
==
4960 IPP_RES_PER_CM
? "dpcm" : "unknown");
4965 case IPP_TAG_RANGE
:
4966 for (i
= 0; i
< attr
->num_values
; i
++)
4968 if (attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
4973 "\"%s\": Bad rangeOfInteger value %d-%d - lower "
4974 "greater than upper (RFC 2911 section 4.1.13).",
4975 attr
->name
, attr
->values
[i
].range
.lower
,
4976 attr
->values
[i
].range
.upper
);
4981 case IPP_TAG_BEGIN_COLLECTION
:
4982 for (i
= 0; i
< attr
->num_values
; i
++)
4984 for (colattr
= attr
->values
[i
].collection
->attrs
;
4986 colattr
= colattr
->next
)
4988 if (!validate_attr(NULL
, colattr
))
4995 if (colattr
&& errors
)
4997 add_stringf(errors
, "\"%s\": Bad collection value.", attr
->name
);
5001 validate_attr(errors
, colattr
);
5002 colattr
= colattr
->next
;
5009 case IPP_TAG_TEXTLANG
:
5010 for (i
= 0; i
< attr
->num_values
; i
++)
5012 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5014 if ((*ptr
& 0xe0) == 0xc0)
5017 if ((*ptr
& 0xc0) != 0x80)
5020 else if ((*ptr
& 0xf0) == 0xe0)
5023 if ((*ptr
& 0xc0) != 0x80)
5026 if ((*ptr
& 0xc0) != 0x80)
5029 else if ((*ptr
& 0xf8) == 0xf0)
5032 if ((*ptr
& 0xc0) != 0x80)
5035 if ((*ptr
& 0xc0) != 0x80)
5038 if ((*ptr
& 0xc0) != 0x80)
5041 else if (*ptr
& 0x80)
5050 "\"%s\": Bad text value \"%s\" - bad UTF-8 "
5051 "sequence (RFC 2911 section 4.1.1).", attr
->name
,
5052 attr
->values
[i
].string
.text
);
5055 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_TEXT
- 1))
5060 "\"%s\": Bad text value \"%s\" - bad length %d "
5061 "(RFC 2911 section 4.1.1).", attr
->name
,
5062 attr
->values
[i
].string
.text
,
5063 (int)strlen(attr
->values
[i
].string
.text
));
5069 case IPP_TAG_NAMELANG
:
5070 for (i
= 0; i
< attr
->num_values
; i
++)
5072 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5074 if ((*ptr
& 0xe0) == 0xc0)
5077 if ((*ptr
& 0xc0) != 0x80)
5080 else if ((*ptr
& 0xf0) == 0xe0)
5083 if ((*ptr
& 0xc0) != 0x80)
5086 if ((*ptr
& 0xc0) != 0x80)
5089 else if ((*ptr
& 0xf8) == 0xf0)
5092 if ((*ptr
& 0xc0) != 0x80)
5095 if ((*ptr
& 0xc0) != 0x80)
5098 if ((*ptr
& 0xc0) != 0x80)
5101 else if (*ptr
& 0x80)
5110 "\"%s\": Bad name value \"%s\" - bad UTF-8 "
5111 "sequence (RFC 2911 section 4.1.2).", attr
->name
,
5112 attr
->values
[i
].string
.text
);
5115 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_NAME
- 1))
5120 "\"%s\": Bad name value \"%s\" - bad length %d "
5121 "(RFC 2911 section 4.1.2).", attr
->name
,
5122 attr
->values
[i
].string
.text
,
5123 (int)strlen(attr
->values
[i
].string
.text
));
5128 case IPP_TAG_KEYWORD
:
5129 for (i
= 0; i
< attr
->num_values
; i
++)
5131 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5132 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' &&
5136 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5141 "\"%s\": Bad keyword value \"%s\" - invalid "
5142 "character (RFC 2911 section 4.1.3).",
5143 attr
->name
, attr
->values
[i
].string
.text
);
5146 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_KEYWORD
- 1))
5151 "\"%s\": Bad keyword value \"%s\" - bad "
5152 "length %d (RFC 2911 section 4.1.3).",
5153 attr
->name
, attr
->values
[i
].string
.text
,
5154 (int)strlen(attr
->values
[i
].string
.text
));
5160 for (i
= 0; i
< attr
->num_values
; i
++)
5162 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
,
5163 attr
->values
[i
].string
.text
,
5164 scheme
, sizeof(scheme
),
5165 userpass
, sizeof(userpass
),
5166 hostname
, sizeof(hostname
),
5167 &port
, resource
, sizeof(resource
));
5169 if (uri_status
< HTTP_URI_OK
)
5174 "\"%s\": Bad URI value \"%s\" - %s "
5175 "(RFC 2911 section 4.1.5).", attr
->name
,
5176 attr
->values
[i
].string
.text
,
5177 URIStatusStrings
[uri_status
-
5178 HTTP_URI_OVERFLOW
]);
5181 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_URI
- 1))
5186 "\"%s\": Bad URI value \"%s\" - bad length %d "
5187 "(RFC 2911 section 4.1.5).", attr
->name
,
5188 attr
->values
[i
].string
.text
,
5189 (int)strlen(attr
->values
[i
].string
.text
));
5194 case IPP_TAG_URISCHEME
:
5195 for (i
= 0; i
< attr
->num_values
; i
++)
5197 ptr
= attr
->values
[i
].string
.text
;
5198 if (islower(*ptr
& 255))
5200 for (ptr
++; *ptr
; ptr
++)
5201 if (!islower(*ptr
& 255) && !isdigit(*ptr
& 255) &&
5202 *ptr
!= '+' && *ptr
!= '-' && *ptr
!= '.')
5206 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5211 "\"%s\": Bad uriScheme value \"%s\" - bad "
5212 "characters (RFC 2911 section 4.1.6).",
5213 attr
->name
, attr
->values
[i
].string
.text
);
5216 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_URISCHEME
- 1))
5221 "\"%s\": Bad uriScheme value \"%s\" - bad "
5222 "length %d (RFC 2911 section 4.1.6).",
5223 attr
->name
, attr
->values
[i
].string
.text
,
5224 (int)strlen(attr
->values
[i
].string
.text
));
5229 case IPP_TAG_CHARSET
:
5230 for (i
= 0; i
< attr
->num_values
; i
++)
5232 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5233 if (!isprint(*ptr
& 255) || isupper(*ptr
& 255) ||
5234 isspace(*ptr
& 255))
5237 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5242 "\"%s\": Bad charset value \"%s\" - bad "
5243 "characters (RFC 2911 section 4.1.7).",
5244 attr
->name
, attr
->values
[i
].string
.text
);
5247 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_CHARSET
- 1))
5252 "\"%s\": Bad charset value \"%s\" - bad "
5253 "length %d (RFC 2911 section 4.1.7).",
5254 attr
->name
, attr
->values
[i
].string
.text
,
5255 (int)strlen(attr
->values
[i
].string
.text
));
5260 case IPP_TAG_LANGUAGE
:
5262 * The following regular expression is derived from the ABNF for
5263 * language tags in RFC 4646. All I can say is that this is the
5264 * easiest way to check the values...
5267 if ((i
= regcomp(&re
,
5269 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
5271 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
5272 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
5273 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
5274 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
5275 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
5277 "x(-[a-z0-9]{1,8})+" /* privateuse */
5279 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
5281 REG_NOSUB
| REG_EXTENDED
)) != 0)
5283 char temp
[256]; /* Temporary error string */
5285 regerror(i
, &re
, temp
, sizeof(temp
));
5286 print_fatal_error("Unable to compile naturalLanguage regular "
5287 "expression: %s.", temp
);
5291 for (i
= 0; i
< attr
->num_values
; i
++)
5293 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5298 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5299 "characters (RFC 2911 section 4.1.8).",
5300 attr
->name
, attr
->values
[i
].string
.text
);
5303 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_LANGUAGE
- 1))
5308 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5309 "length %d (RFC 2911 section 4.1.8).",
5310 attr
->name
, attr
->values
[i
].string
.text
,
5311 (int)strlen(attr
->values
[i
].string
.text
));
5318 case IPP_TAG_MIMETYPE
:
5320 * The following regular expression is derived from the ABNF for
5321 * language tags in RFC 2045 and 4288. All I can say is that this is
5322 * the easiest way to check the values...
5325 if ((i
= regcomp(&re
,
5327 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
5329 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
5330 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
5331 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
5334 REG_NOSUB
| REG_EXTENDED
)) != 0)
5336 char temp
[256]; /* Temporary error string */
5338 regerror(i
, &re
, temp
, sizeof(temp
));
5339 print_fatal_error("Unable to compile mimeMediaType regular "
5340 "expression: %s.", temp
);
5344 for (i
= 0; i
< attr
->num_values
; i
++)
5346 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5351 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5352 "characters (RFC 2911 section 4.1.9).",
5353 attr
->name
, attr
->values
[i
].string
.text
);
5356 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_MIMETYPE
- 1))
5361 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5362 "length %d (RFC 2911 section 4.1.9).",
5363 attr
->name
, attr
->values
[i
].string
.text
,
5364 (int)strlen(attr
->values
[i
].string
.text
));
5380 * 'with_value()' - Test a WITH-VALUE predicate.
5383 static int /* O - 1 on match, 0 on non-match */
5384 with_value(cups_array_t
*errors
, /* I - Errors array */
5385 char *value
, /* I - Value string */
5386 int flags
, /* I - Flags for match */
5387 ipp_attribute_t
*attr
, /* I - Attribute to compare */
5388 char *matchbuf
, /* I - Buffer to hold matching value */
5389 size_t matchlen
) /* I - Length of match buffer */
5391 int i
, /* Looping var */
5393 char temp
[256], /* Temporary value string */
5394 *valptr
; /* Pointer into value */
5398 match
= (flags
& _CUPS_WITH_ALL
) ? 1 : 0;
5401 * NULL matches everything.
5404 if (!value
|| !*value
)
5408 * Compare the value string to the attribute value.
5411 switch (attr
->value_tag
)
5413 case IPP_TAG_INTEGER
:
5415 for (i
= 0; i
< attr
->num_values
; i
++)
5417 char op
, /* Comparison operator */
5418 *nextptr
; /* Next pointer */
5419 int intvalue
, /* Integer value */
5420 valmatch
= 0; /* Does the current value match? */
5424 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5425 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5426 *valptr
== '=' || *valptr
== '>')
5429 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5431 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5439 intvalue
= strtol(valptr
, &nextptr
, 0);
5440 if (nextptr
== valptr
)
5444 if ((op
== '=' && attr
->values
[i
].integer
== intvalue
) ||
5445 (op
== '<' && attr
->values
[i
].integer
< intvalue
) ||
5446 (op
== '>' && attr
->values
[i
].integer
> intvalue
))
5449 snprintf(matchbuf
, matchlen
, "%d",
5450 attr
->values
[i
].integer
);
5457 if (flags
& _CUPS_WITH_ALL
)
5472 if (!match
&& errors
)
5474 for (i
= 0; i
< attr
->num_values
; i
++)
5475 add_stringf(errors
, "GOT: %s=%d", attr
->name
,
5476 attr
->values
[i
].integer
);
5480 case IPP_TAG_RANGE
:
5481 for (i
= 0; i
< attr
->num_values
; i
++)
5483 char op
, /* Comparison operator */
5484 *nextptr
; /* Next pointer */
5485 int intvalue
, /* Integer value */
5486 valmatch
= 0; /* Does the current value match? */
5490 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5491 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5492 *valptr
== '=' || *valptr
== '>')
5495 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5497 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5505 intvalue
= strtol(valptr
, &nextptr
, 0);
5506 if (nextptr
== valptr
)
5510 if ((op
== '=' && (attr
->values
[i
].range
.lower
== intvalue
||
5511 attr
->values
[i
].range
.upper
== intvalue
)) ||
5512 (op
== '<' && attr
->values
[i
].range
.upper
< intvalue
) ||
5513 (op
== '>' && attr
->values
[i
].range
.upper
> intvalue
))
5516 snprintf(matchbuf
, matchlen
, "%d-%d",
5517 attr
->values
[0].range
.lower
,
5518 attr
->values
[0].range
.upper
);
5525 if (flags
& _CUPS_WITH_ALL
)
5540 if (!match
&& errors
)
5542 for (i
= 0; i
< attr
->num_values
; i
++)
5543 add_stringf(errors
, "GOT: %s=%d-%d", attr
->name
,
5544 attr
->values
[i
].range
.lower
,
5545 attr
->values
[i
].range
.upper
);
5549 case IPP_TAG_BOOLEAN
:
5550 for (i
= 0; i
< attr
->num_values
; i
++)
5552 if (!strcmp(value
, "true") == attr
->values
[i
].boolean
)
5555 strlcpy(matchbuf
, value
, matchlen
);
5557 if (!(flags
& _CUPS_WITH_ALL
))
5563 else if (flags
& _CUPS_WITH_ALL
)
5570 if (!match
&& errors
)
5572 for (i
= 0; i
< attr
->num_values
; i
++)
5573 add_stringf(errors
, "GOT: %s=%s", attr
->name
,
5574 attr
->values
[i
].boolean
? "true" : "false");
5578 case IPP_TAG_RESOLUTION
:
5579 for (i
= 0; i
< attr
->num_values
; i
++)
5581 if (attr
->values
[i
].resolution
.xres
==
5582 attr
->values
[i
].resolution
.yres
)
5583 snprintf(temp
, sizeof(temp
), "%d%s",
5584 attr
->values
[i
].resolution
.xres
,
5585 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5588 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5589 attr
->values
[i
].resolution
.xres
,
5590 attr
->values
[i
].resolution
.yres
,
5591 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5594 if (!strcmp(value
, temp
))
5597 strlcpy(matchbuf
, value
, matchlen
);
5599 if (!(flags
& _CUPS_WITH_ALL
))
5605 else if (flags
& _CUPS_WITH_ALL
)
5612 if (!match
&& errors
)
5614 for (i
= 0; i
< attr
->num_values
; i
++)
5616 if (attr
->values
[i
].resolution
.xres
==
5617 attr
->values
[i
].resolution
.yres
)
5618 snprintf(temp
, sizeof(temp
), "%d%s",
5619 attr
->values
[i
].resolution
.xres
,
5620 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5623 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5624 attr
->values
[i
].resolution
.xres
,
5625 attr
->values
[i
].resolution
.yres
,
5626 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5629 if (strcmp(value
, temp
))
5630 add_stringf(errors
, "GOT: %s=%s", attr
->name
, temp
);
5635 case IPP_TAG_NOVALUE
:
5636 case IPP_TAG_UNKNOWN
:
5639 case IPP_TAG_CHARSET
:
5640 case IPP_TAG_KEYWORD
:
5641 case IPP_TAG_LANGUAGE
:
5642 case IPP_TAG_MIMETYPE
:
5644 case IPP_TAG_NAMELANG
:
5646 case IPP_TAG_TEXTLANG
:
5648 case IPP_TAG_URISCHEME
:
5649 if (flags
& _CUPS_WITH_REGEX
)
5652 * Value is an extended, case-sensitive POSIX regular expression...
5655 regex_t re
; /* Regular expression */
5657 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
5659 regerror(i
, &re
, temp
, sizeof(temp
));
5661 print_fatal_error("Unable to compile WITH-VALUE regular expression "
5662 "\"%s\" - %s", value
, temp
);
5667 * See if ALL of the values match the given regular expression.
5670 for (i
= 0; i
< attr
->num_values
; i
++)
5672 if (!regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5675 strlcpy(matchbuf
, attr
->values
[i
].string
.text
, matchlen
);
5677 if (!(flags
& _CUPS_WITH_ALL
))
5683 else if (flags
& _CUPS_WITH_ALL
)
5695 * Value is a literal string, see if the value(s) match...
5698 for (i
= 0; i
< attr
->num_values
; i
++)
5700 if (!strcmp(value
, attr
->values
[i
].string
.text
))
5703 strlcpy(matchbuf
, attr
->values
[i
].string
.text
, matchlen
);
5705 if (!(flags
& _CUPS_WITH_ALL
))
5711 else if (flags
& _CUPS_WITH_ALL
)
5719 if (!match
&& errors
)
5721 for (i
= 0; i
< attr
->num_values
; i
++)
5722 add_stringf(errors
, "GOT: %s=\"%s\"", attr
->name
,
5723 attr
->values
[i
].string
.text
);