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 char filename
[1024]; /* Filename */
812 _cups_transfer_t transfer
; /* To chunk or not to chunk */
813 int version
, /* IPP version number to use */
814 skip_test
; /* Skip this test? */
815 int num_statuses
= 0; /* Number of valid status codes */
816 _cups_status_t statuses
[100], /* Valid status codes */
817 *last_status
; /* Last STATUS (for predicates) */
818 int num_expects
= 0; /* Number of expected attributes */
819 _cups_expect_t expects
[200], /* Expected attributes */
820 *expect
, /* Current expected attribute */
821 *last_expect
; /* Last EXPECT (for predicates) */
822 int num_displayed
= 0; /* Number of displayed attributes */
823 char *displayed
[200]; /* Displayed attributes */
824 size_t widths
[200]; /* Width of columns */
825 cups_array_t
*a
, /* Duplicate attribute array */
826 *errors
= NULL
; /* Errors array */
827 const char *error
; /* Current error */
831 * Open the test file...
834 if ((fp
= fopen(testfile
, "r")) == NULL
)
836 print_fatal_error("Unable to open test file %s - %s", testfile
,
843 * Connect to the server...
846 if ((http
= httpConnect2(vars
->hostname
, vars
->port
, NULL
, vars
->family
,
847 vars
->encryption
, 1, 30000, NULL
)) == NULL
)
849 print_fatal_error("Unable to connect to %s on port %d - %s", vars
->hostname
,
850 vars
->port
, cupsLastErrorString());
856 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
,
857 "deflate, gzip, identity");
859 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
, "identity");
860 #endif /* HAVE_LIBZ */
862 if (vars
->timeout
> 0.0)
863 httpSetTimeout(http
, vars
->timeout
, timeout_cb
, NULL
);
869 CUPS_SRAND(time(NULL
));
871 errors
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
,
872 (cups_afree_func_t
)free
);
875 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
877 while (!Cancel
&& get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
880 * Expect an open brace...
883 if (!strcmp(token
, "DEFINE"))
889 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
890 get_token(fp
, temp
, sizeof(temp
), &linenum
))
892 expand_variables(vars
, token
, temp
, sizeof(token
));
893 set_variable(vars
, attr
, token
);
897 print_fatal_error("Missing DEFINE name and/or value on line %d.",
905 else if (!strcmp(token
, "DEFINE-DEFAULT"))
908 * DEFINE-DEFAULT name value
911 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
912 get_token(fp
, temp
, sizeof(temp
), &linenum
))
914 expand_variables(vars
, token
, temp
, sizeof(token
));
915 if (!get_variable(vars
, attr
))
916 set_variable(vars
, attr
, token
);
920 print_fatal_error("Missing DEFINE-DEFAULT name and/or value on line "
928 else if (!strcmp(token
, "IGNORE-ERRORS"))
935 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
936 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
938 IgnoreErrors
= !_cups_strcasecmp(temp
, "yes");
942 print_fatal_error("Missing IGNORE-ERRORS value on line %d.", linenum
);
949 else if (!strcmp(token
, "INCLUDE"))
956 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
959 * Map the filename to and then run the tests...
962 if (!do_tests(vars
, get_filename(testfile
, filename
, temp
,
967 if (StopAfterIncludeError
)
973 print_fatal_error("Missing INCLUDE filename on line %d.", linenum
);
981 else if (!strcmp(token
, "INCLUDE-IF-DEFINED"))
984 * INCLUDE-IF-DEFINED name "filename"
985 * INCLUDE-IF-DEFINED name <filename>
988 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
989 get_token(fp
, temp
, sizeof(temp
), &linenum
))
992 * Map the filename to and then run the tests...
995 if (get_variable(vars
, attr
) &&
996 !do_tests(vars
, get_filename(testfile
, filename
, temp
,
1001 if (StopAfterIncludeError
)
1007 print_fatal_error("Missing INCLUDE-IF-DEFINED name or filename on line "
1016 else if (!strcmp(token
, "INCLUDE-IF-NOT-DEFINED"))
1019 * INCLUDE-IF-NOT-DEFINED name "filename"
1020 * INCLUDE-IF-NOT-DEFINED name <filename>
1023 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1024 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1027 * Map the filename to and then run the tests...
1030 if (!get_variable(vars
, attr
) &&
1031 !do_tests(vars
, get_filename(testfile
, filename
, temp
,
1036 if (StopAfterIncludeError
)
1042 print_fatal_error("Missing INCLUDE-IF-NOT-DEFINED name or filename on "
1043 "line %d.", linenum
);
1051 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1054 * SKIP-IF-DEFINED variable
1057 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1059 if (get_variable(vars
, temp
))
1064 print_fatal_error("Missing SKIP-IF-DEFINED variable on line %d.",
1070 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1073 * SKIP-IF-NOT-DEFINED variable
1076 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1078 if (!get_variable(vars
, temp
))
1083 print_fatal_error("Missing SKIP-IF-NOT-DEFINED variable on line %d.",
1089 else if (!strcmp(token
, "STOP-AFTER-INCLUDE-ERROR"))
1092 * STOP-AFTER-INCLUDE-ERROR yes
1093 * STOP-AFTER-INCLUDE-ERROR no
1096 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1097 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1099 StopAfterIncludeError
= !_cups_strcasecmp(temp
, "yes");
1103 print_fatal_error("Missing STOP-AFTER-INCLUDE-ERROR value on line %d.",
1111 else if (!strcmp(token
, "TRANSFER"))
1119 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1121 if (!strcmp(temp
, "auto"))
1122 Transfer
= _CUPS_TRANSFER_AUTO
;
1123 else if (!strcmp(temp
, "chunked"))
1124 Transfer
= _CUPS_TRANSFER_CHUNKED
;
1125 else if (!strcmp(temp
, "length"))
1126 Transfer
= _CUPS_TRANSFER_LENGTH
;
1129 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp
,
1137 print_fatal_error("Missing TRANSFER value on line %d.", linenum
);
1144 else if (!strcmp(token
, "VERSION"))
1146 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1148 if (!strcmp(temp
, "1.0"))
1150 else if (!strcmp(temp
, "1.1"))
1152 else if (!strcmp(temp
, "2.0"))
1154 else if (!strcmp(temp
, "2.1"))
1156 else if (!strcmp(temp
, "2.2"))
1160 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1167 print_fatal_error("Missing VERSION number on line %d.", linenum
);
1174 else if (strcmp(token
, "{"))
1176 print_fatal_error("Unexpected token %s seen on line %d.", token
, linenum
);
1182 * Initialize things...
1187 if (Output
== _CUPS_OUTPUT_PLIST
)
1189 else if (Output
== _CUPS_OUTPUT_TEST
)
1190 printf("\"%s\":\n", testfile
);
1195 strlcpy(resource
, vars
->resource
, sizeof(resource
));
1200 group
= IPP_TAG_ZERO
;
1201 ignore_errors
= IgnoreErrors
;
1208 transfer
= Transfer
;
1209 compression
[0] = '\0';
1211 strlcpy(name
, testfile
, sizeof(name
));
1212 if (strrchr(name
, '.') != NULL
)
1213 *strrchr(name
, '.') = '\0';
1216 * Parse until we see a close brace...
1219 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
1221 if (_cups_strcasecmp(token
, "COUNT") &&
1222 _cups_strcasecmp(token
, "DEFINE-MATCH") &&
1223 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1224 _cups_strcasecmp(token
, "DEFINE-VALUE") &&
1225 _cups_strcasecmp(token
, "IF-DEFINED") &&
1226 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1227 _cups_strcasecmp(token
, "IN-GROUP") &&
1228 _cups_strcasecmp(token
, "OF-TYPE") &&
1229 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1230 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1231 _cups_strcasecmp(token
, "REPEAT-NO-MATCH") &&
1232 _cups_strcasecmp(token
, "SAME-COUNT-AS") &&
1233 _cups_strcasecmp(token
, "WITH-ALL-VALUES") &&
1234 _cups_strcasecmp(token
, "WITH-VALUE"))
1237 if (_cups_strcasecmp(token
, "DEFINE-MATCH") &&
1238 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1239 _cups_strcasecmp(token
, "IF-DEFINED") &&
1240 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1241 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1242 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1243 _cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
1246 if (!strcmp(token
, "}"))
1248 else if (!strcmp(token
, "{") && lastcol
)
1251 * Another collection value
1254 ipp_t
*col
= get_collection(vars
, fp
, &linenum
);
1255 /* Collection value */
1259 ipp_attribute_t
*tempcol
; /* Pointer to new buffer */
1263 * Reallocate memory...
1266 if ((tempcol
= realloc(lastcol
, sizeof(ipp_attribute_t
) +
1267 (lastcol
->num_values
+ 1) *
1268 sizeof(_ipp_value_t
))) == NULL
)
1270 print_fatal_error("Unable to allocate memory on line %d.", linenum
);
1275 if (tempcol
!= lastcol
)
1278 * Reset pointers in the list...
1282 request
->prev
->next
= tempcol
;
1284 request
->attrs
= tempcol
;
1286 lastcol
= request
->current
= request
->last
= tempcol
;
1289 lastcol
->values
[lastcol
->num_values
].collection
= col
;
1290 lastcol
->num_values
++;
1298 else if (!strcmp(token
, "COMPRESSION"))
1302 * COMPRESSION deflate
1306 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1308 expand_variables(vars
, compression
, temp
, sizeof(compression
));
1310 if (strcmp(compression
, "none") && strcmp(compression
, "deflate") &&
1311 strcmp(compression
, "gzip"))
1313 if (strcmp(compression
, "none"))
1314 #endif /* HAVE_LIBZ */
1316 print_fatal_error("Unsupported COMPRESSION value '%s' on line %d.",
1317 compression
, linenum
);
1322 if (!strcmp(compression
, "none"))
1323 compression
[0] = '\0';
1327 print_fatal_error("Missing COMPRESSION value on line %d.", linenum
);
1332 else if (!strcmp(token
, "DEFINE"))
1338 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1339 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1341 expand_variables(vars
, token
, temp
, sizeof(token
));
1342 set_variable(vars
, attr
, token
);
1346 print_fatal_error("Missing DEFINE name and/or value on line %d.",
1352 else if (!strcmp(token
, "IGNORE-ERRORS"))
1359 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1360 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1362 ignore_errors
= !_cups_strcasecmp(temp
, "yes");
1366 print_fatal_error("Missing IGNORE-ERRORS value on line %d.", linenum
);
1373 else if (!_cups_strcasecmp(token
, "NAME"))
1379 get_token(fp
, name
, sizeof(name
), &linenum
);
1381 else if (!strcmp(token
, "REQUEST-ID"))
1388 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1390 if (isdigit(temp
[0] & 255))
1391 request_id
= atoi(temp
);
1392 else if (!_cups_strcasecmp(temp
, "random"))
1393 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
1396 print_fatal_error("Bad REQUEST-ID value \"%s\" on line %d.", temp
,
1404 print_fatal_error("Missing REQUEST-ID value on line %d.", linenum
);
1409 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1412 * SKIP-IF-DEFINED variable
1415 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1417 if (get_variable(vars
, temp
))
1422 print_fatal_error("Missing SKIP-IF-DEFINED value on line %d.",
1428 else if (!strcmp(token
, "SKIP-IF-MISSING"))
1431 * SKIP-IF-MISSING filename
1434 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1436 expand_variables(vars
, token
, temp
, sizeof(token
));
1437 get_filename(testfile
, filename
, token
, sizeof(filename
));
1439 if (access(filename
, R_OK
))
1444 print_fatal_error("Missing SKIP-IF-MISSING filename on line %d.",
1450 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1453 * SKIP-IF-NOT-DEFINED variable
1456 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1458 if (!get_variable(vars
, temp
))
1463 print_fatal_error("Missing SKIP-IF-NOT-DEFINED value on line %d.",
1469 else if (!strcmp(token
, "SKIP-PREVIOUS-ERROR"))
1472 * SKIP-PREVIOUS-ERROR yes
1473 * SKIP-PREVIOUS-ERROR no
1476 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1477 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1479 skip_previous
= !_cups_strcasecmp(temp
, "yes");
1483 print_fatal_error("Missing SKIP-PREVIOUS-ERROR value on line %d.", linenum
);
1490 else if (!strcmp(token
, "TRANSFER"))
1498 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1500 if (!strcmp(temp
, "auto"))
1501 transfer
= _CUPS_TRANSFER_AUTO
;
1502 else if (!strcmp(temp
, "chunked"))
1503 transfer
= _CUPS_TRANSFER_CHUNKED
;
1504 else if (!strcmp(temp
, "length"))
1505 transfer
= _CUPS_TRANSFER_LENGTH
;
1508 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp
,
1516 print_fatal_error("Missing TRANSFER value on line %d.", linenum
);
1521 else if (!_cups_strcasecmp(token
, "VERSION"))
1523 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1525 if (!strcmp(temp
, "0.0"))
1527 else if (!strcmp(temp
, "1.0"))
1529 else if (!strcmp(temp
, "1.1"))
1531 else if (!strcmp(temp
, "2.0"))
1533 else if (!strcmp(temp
, "2.1"))
1535 else if (!strcmp(temp
, "2.2"))
1539 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1546 print_fatal_error("Missing VERSION number on line %d.", linenum
);
1551 else if (!_cups_strcasecmp(token
, "RESOURCE"))
1557 if (!get_token(fp
, resource
, sizeof(resource
), &linenum
))
1559 print_fatal_error("Missing RESOURCE path on line %d.", linenum
);
1564 else if (!_cups_strcasecmp(token
, "OPERATION"))
1570 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1572 print_fatal_error("Missing OPERATION code on line %d.", linenum
);
1577 expand_variables(vars
, token
, temp
, sizeof(token
));
1579 if ((op
= ippOpValue(token
)) == (ipp_op_t
)-1 &&
1580 (op
= strtol(token
, NULL
, 0)) == 0)
1582 print_fatal_error("Bad OPERATION code \"%s\" on line %d.", token
,
1588 else if (!_cups_strcasecmp(token
, "GROUP"))
1591 * Attribute group...
1594 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1596 print_fatal_error("Missing GROUP tag on line %d.", linenum
);
1601 if ((value
= ippTagValue(token
)) < 0)
1603 print_fatal_error("Bad GROUP tag \"%s\" on line %d.", token
, linenum
);
1609 ippAddSeparator(request
);
1613 else if (!_cups_strcasecmp(token
, "DELAY"))
1616 * Delay before operation...
1621 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1623 print_fatal_error("Missing DELAY value on line %d.", linenum
);
1628 expand_variables(vars
, token
, temp
, sizeof(token
));
1630 if ((delay
= _cupsStrScand(token
, NULL
, localeconv())) <= 0.0)
1632 print_fatal_error("Bad DELAY value \"%s\" on line %d.", token
,
1639 if (Output
== _CUPS_OUTPUT_TEST
)
1640 printf(" [%g second delay]\n", delay
);
1642 usleep((int)(1000000.0 * delay
));
1645 else if (!_cups_strcasecmp(token
, "ATTR"))
1651 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1653 print_fatal_error("Missing ATTR value tag on line %d.", linenum
);
1658 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
1660 print_fatal_error("Bad ATTR value tag \"%s\" on line %d.", token
,
1666 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
1668 print_fatal_error("Missing ATTR name on line %d.", linenum
);
1673 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1675 print_fatal_error("Missing ATTR value on line %d.", linenum
);
1680 expand_variables(vars
, token
, temp
, sizeof(token
));
1685 case IPP_TAG_BOOLEAN
:
1686 if (!_cups_strcasecmp(token
, "true"))
1687 attrptr
= ippAddBoolean(request
, group
, attr
, 1);
1689 attrptr
= ippAddBoolean(request
, group
, attr
, atoi(token
));
1692 case IPP_TAG_INTEGER
:
1694 if (!strchr(token
, ','))
1695 attrptr
= ippAddInteger(request
, group
, value
, attr
,
1696 strtol(token
, &tokenptr
, 0));
1699 int values
[100], /* Values */
1700 num_values
= 1; /* Number of values */
1702 values
[0] = strtol(token
, &tokenptr
, 10);
1703 while (tokenptr
&& *tokenptr
&&
1704 num_values
< (int)(sizeof(values
) / sizeof(values
[0])))
1706 if (*tokenptr
== ',')
1708 else if (!isdigit(*tokenptr
& 255) && *tokenptr
!= '-')
1711 values
[num_values
] = strtol(tokenptr
, &tokenptr
, 0);
1715 attrptr
= ippAddIntegers(request
, group
, value
, attr
, num_values
, values
);
1718 if (!tokenptr
|| *tokenptr
)
1720 print_fatal_error("Bad %s value \"%s\" on line %d.",
1721 ippTagString(value
), token
, linenum
);
1727 case IPP_TAG_RESOLUTION
:
1729 int xres
, /* X resolution */
1730 yres
; /* Y resolution */
1731 char *ptr
; /* Pointer into value */
1733 xres
= yres
= strtol(token
, (char **)&ptr
, 10);
1734 if (ptr
> token
&& xres
> 0)
1737 yres
= strtol(ptr
+ 1, (char **)&ptr
, 10);
1740 if (ptr
<= token
|| xres
<= 0 || yres
<= 0 || !ptr
||
1741 (_cups_strcasecmp(ptr
, "dpi") &&
1742 _cups_strcasecmp(ptr
, "dpc") &&
1743 _cups_strcasecmp(ptr
, "dpcm") &&
1744 _cups_strcasecmp(ptr
, "other")))
1746 print_fatal_error("Bad resolution value \"%s\" on line %d.",
1752 if (!_cups_strcasecmp(ptr
, "dpi"))
1753 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_INCH
,
1755 else if (!_cups_strcasecmp(ptr
, "dpc") ||
1756 !_cups_strcasecmp(ptr
, "dpcm"))
1757 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_CM
,
1760 attrptr
= ippAddResolution(request
, group
, attr
, (ipp_res_t
)0,
1765 case IPP_TAG_RANGE
:
1767 int lowers
[4], /* Lower value */
1768 uppers
[4], /* Upper values */
1769 num_vals
; /* Number of values */
1772 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
1773 lowers
+ 0, uppers
+ 0,
1774 lowers
+ 1, uppers
+ 1,
1775 lowers
+ 2, uppers
+ 2,
1776 lowers
+ 3, uppers
+ 3);
1778 if ((num_vals
& 1) || num_vals
== 0)
1780 print_fatal_error("Bad rangeOfInteger value \"%s\" on line "
1781 "%d.", token
, linenum
);
1786 attrptr
= ippAddRanges(request
, group
, attr
, num_vals
/ 2, lowers
,
1791 case IPP_TAG_BEGIN_COLLECTION
:
1792 if (!strcmp(token
, "{"))
1794 ipp_t
*col
= get_collection(vars
, fp
, &linenum
);
1795 /* Collection value */
1799 attrptr
= lastcol
= ippAddCollection(request
, group
, attr
, col
);
1810 print_fatal_error("Bad ATTR collection value on line %d.",
1817 case IPP_TAG_STRING
:
1818 attrptr
= ippAddOctetString(request
, group
, attr
, token
,
1823 print_fatal_error("Unsupported ATTR value tag %s on line %d.",
1824 ippTagString(value
), linenum
);
1828 case IPP_TAG_TEXTLANG
:
1829 case IPP_TAG_NAMELANG
:
1832 case IPP_TAG_KEYWORD
:
1834 case IPP_TAG_URISCHEME
:
1835 case IPP_TAG_CHARSET
:
1836 case IPP_TAG_LANGUAGE
:
1837 case IPP_TAG_MIMETYPE
:
1838 if (!strchr(token
, ','))
1839 attrptr
= ippAddString(request
, group
, value
, attr
, NULL
, token
);
1843 * Multiple string values...
1846 int num_values
; /* Number of values */
1847 char *values
[100], /* Values */
1848 *ptr
; /* Pointer to next value */
1854 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
1856 if (ptr
> token
&& ptr
[-1] == '\\')
1857 _cups_strcpy(ptr
- 1, ptr
);
1861 values
[num_values
] = ptr
;
1866 attrptr
= ippAddStrings(request
, group
, value
, attr
, num_values
,
1867 NULL
, (const char **)values
);
1874 print_fatal_error("Unable to add attribute on line %d: %s", linenum
,
1875 cupsLastErrorString());
1880 else if (!_cups_strcasecmp(token
, "FILE"))
1886 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1888 print_fatal_error("Missing FILE filename on line %d.", linenum
);
1893 expand_variables(vars
, token
, temp
, sizeof(token
));
1894 get_filename(testfile
, filename
, token
, sizeof(filename
));
1896 if (access(filename
, R_OK
))
1898 print_fatal_error("Filename \"%s\" on line %d cannot be read.",
1900 print_fatal_error("Filename mapped to \"%s\".", filename
);
1905 else if (!_cups_strcasecmp(token
, "STATUS"))
1911 if (num_statuses
>= (int)(sizeof(statuses
) / sizeof(statuses
[0])))
1913 print_fatal_error("Too many STATUS's on line %d.", linenum
);
1918 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1920 print_fatal_error("Missing STATUS code on line %d.", linenum
);
1925 if ((statuses
[num_statuses
].status
= ippErrorValue(token
))
1926 == (ipp_status_t
)-1 &&
1927 (statuses
[num_statuses
].status
= strtol(token
, NULL
, 0)) == 0)
1929 print_fatal_error("Bad STATUS code \"%s\" on line %d.", token
,
1935 last_status
= statuses
+ num_statuses
;
1938 last_status
->define_match
= NULL
;
1939 last_status
->define_no_match
= NULL
;
1940 last_status
->if_defined
= NULL
;
1941 last_status
->if_not_defined
= NULL
;
1942 last_status
->repeat_limit
= 1000;
1943 last_status
->repeat_match
= 0;
1944 last_status
->repeat_no_match
= 0;
1946 else if (!_cups_strcasecmp(token
, "EXPECT"))
1949 * Expected attributes...
1952 if (num_expects
>= (int)(sizeof(expects
) / sizeof(expects
[0])))
1954 print_fatal_error("Too many EXPECT's on line %d.", linenum
);
1959 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1961 print_fatal_error("Missing EXPECT name on line %d.", linenum
);
1966 last_expect
= expects
+ num_expects
;
1969 memset(last_expect
, 0, sizeof(_cups_expect_t
));
1970 last_expect
->repeat_limit
= 1000;
1972 if (token
[0] == '!')
1974 last_expect
->not_expect
= 1;
1975 last_expect
->name
= strdup(token
+ 1);
1977 else if (token
[0] == '?')
1979 last_expect
->optional
= 1;
1980 last_expect
->name
= strdup(token
+ 1);
1983 last_expect
->name
= strdup(token
);
1985 else if (!_cups_strcasecmp(token
, "COUNT"))
1987 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1989 print_fatal_error("Missing COUNT number on line %d.", linenum
);
1994 if ((i
= atoi(token
)) <= 0)
1996 print_fatal_error("Bad COUNT \"%s\" on line %d.", token
, linenum
);
2002 last_expect
->count
= i
;
2005 print_fatal_error("COUNT without a preceding EXPECT on line %d.",
2011 else if (!_cups_strcasecmp(token
, "DEFINE-MATCH"))
2013 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2015 print_fatal_error("Missing DEFINE-MATCH variable on line %d.",
2022 last_expect
->define_match
= strdup(token
);
2023 else if (last_status
)
2024 last_status
->define_match
= strdup(token
);
2027 print_fatal_error("DEFINE-MATCH without a preceding EXPECT or STATUS "
2028 "on line %d.", linenum
);
2033 else if (!_cups_strcasecmp(token
, "DEFINE-NO-MATCH"))
2035 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2037 print_fatal_error("Missing DEFINE-NO-MATCH variable on line %d.",
2044 last_expect
->define_no_match
= strdup(token
);
2045 else if (last_status
)
2046 last_status
->define_no_match
= strdup(token
);
2049 print_fatal_error("DEFINE-NO-MATCH without a preceding EXPECT or "
2050 "STATUS on line %d.", linenum
);
2055 else if (!_cups_strcasecmp(token
, "DEFINE-VALUE"))
2057 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2059 print_fatal_error("Missing DEFINE-VALUE variable on line %d.",
2066 last_expect
->define_value
= strdup(token
);
2069 print_fatal_error("DEFINE-VALUE without a preceding EXPECT on "
2070 "line %d.", linenum
);
2075 else if (!_cups_strcasecmp(token
, "OF-TYPE"))
2077 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2079 print_fatal_error("Missing OF-TYPE value tag(s) on line %d.",
2086 last_expect
->of_type
= strdup(token
);
2089 print_fatal_error("OF-TYPE without a preceding EXPECT on line %d.",
2095 else if (!_cups_strcasecmp(token
, "IN-GROUP"))
2097 ipp_tag_t in_group
; /* IN-GROUP value */
2100 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2102 print_fatal_error("Missing IN-GROUP group tag on line %d.", linenum
);
2107 if ((in_group
= ippTagValue(token
)) == (ipp_tag_t
)-1)
2110 else if (last_expect
)
2111 last_expect
->in_group
= in_group
;
2114 print_fatal_error("IN-GROUP without a preceding EXPECT on line %d.",
2120 else if (!_cups_strcasecmp(token
, "REPEAT-LIMIT"))
2122 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2124 print_fatal_error("Missing REPEAT-LIMIT value on line %d.", linenum
);
2128 else if (atoi(token
) <= 0)
2130 print_fatal_error("Bad REPEAT-LIMIT value on line %d.", linenum
);
2136 last_status
->repeat_limit
= atoi(token
);
2137 else if (last_expect
)
2138 last_expect
->repeat_limit
= atoi(token
);
2141 print_fatal_error("REPEAT-LIMIT without a preceding EXPECT or STATUS "
2142 "on line %d.", linenum
);
2147 else if (!_cups_strcasecmp(token
, "REPEAT-MATCH"))
2150 last_status
->repeat_match
= 1;
2151 else if (last_expect
)
2152 last_expect
->repeat_match
= 1;
2155 print_fatal_error("REPEAT-MATCH without a preceding EXPECT or STATUS "
2156 "on line %d.", linenum
);
2161 else if (!_cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
2164 last_status
->repeat_no_match
= 1;
2165 else if (last_expect
)
2166 last_expect
->repeat_no_match
= 1;
2169 print_fatal_error("REPEAT-NO-MATCH without a preceding EXPECT or "
2170 "STATUS on ine %d.", linenum
);
2175 else if (!_cups_strcasecmp(token
, "SAME-COUNT-AS"))
2177 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2179 print_fatal_error("Missing SAME-COUNT-AS name on line %d.", linenum
);
2185 last_expect
->same_count_as
= strdup(token
);
2188 print_fatal_error("SAME-COUNT-AS without a preceding EXPECT on line "
2194 else if (!_cups_strcasecmp(token
, "IF-DEFINED"))
2196 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2198 print_fatal_error("Missing IF-DEFINED name on line %d.", linenum
);
2204 last_expect
->if_defined
= strdup(token
);
2205 else if (last_status
)
2206 last_status
->if_defined
= strdup(token
);
2209 print_fatal_error("IF-DEFINED without a preceding EXPECT or STATUS "
2210 "on line %d.", linenum
);
2215 else if (!_cups_strcasecmp(token
, "IF-NOT-DEFINED"))
2217 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2219 print_fatal_error("Missing IF-NOT-DEFINED name on line %d.", linenum
);
2225 last_expect
->if_not_defined
= strdup(token
);
2226 else if (last_status
)
2227 last_status
->if_not_defined
= strdup(token
);
2230 print_fatal_error("IF-NOT-DEFINED without a preceding EXPECT or STATUS "
2231 "on line %d.", linenum
);
2236 else if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") ||
2237 !_cups_strcasecmp(token
, "WITH-VALUE"))
2239 if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") && last_expect
)
2240 last_expect
->with_flags
= _CUPS_WITH_ALL
;
2242 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
2244 print_fatal_error("Missing %s value on line %d.", token
, linenum
);
2252 * Expand any variables in the value and then save it.
2255 expand_variables(vars
, token
, temp
, sizeof(token
));
2257 tokenptr
= token
+ strlen(token
) - 1;
2259 if (token
[0] == '/' && tokenptr
> token
&& *tokenptr
== '/')
2262 * WITH-VALUE is a POSIX extended regular expression.
2265 last_expect
->with_value
= calloc(1, tokenptr
- token
);
2266 last_expect
->with_flags
|= _CUPS_WITH_REGEX
;
2268 if (last_expect
->with_value
)
2269 memcpy(last_expect
->with_value
, token
+ 1, tokenptr
- token
- 1);
2274 * WITH-VALUE is a literal value...
2277 char *ptr
; /* Pointer into value */
2279 for (ptr
= token
; *ptr
; ptr
++)
2281 if (*ptr
== '\\' && ptr
[1])
2284 * Remove \ from \foo...
2287 _cups_strcpy(ptr
, ptr
+ 1);
2291 last_expect
->with_value
= strdup(token
);
2292 last_expect
->with_flags
|= _CUPS_WITH_LITERAL
;
2297 print_fatal_error("%s without a preceding EXPECT on line %d.", token
,
2303 else if (!_cups_strcasecmp(token
, "DISPLAY"))
2306 * Display attributes...
2309 if (num_displayed
>= (int)(sizeof(displayed
) / sizeof(displayed
[0])))
2311 print_fatal_error("Too many DISPLAY's on line %d", linenum
);
2316 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2318 print_fatal_error("Missing DISPLAY name on line %d.", linenum
);
2323 displayed
[num_displayed
] = strdup(token
);
2328 print_fatal_error("Unexpected token %s seen on line %d.", token
,
2336 * Submit the IPP request...
2341 request
->request
.op
.version
[0] = version
/ 10;
2342 request
->request
.op
.version
[1] = version
% 10;
2343 request
->request
.op
.operation_id
= op
;
2344 request
->request
.op
.request_id
= request_id
;
2346 if (Output
== _CUPS_OUTPUT_PLIST
)
2349 puts("<key>Name</key>");
2350 print_xml_string("string", name
);
2351 puts("<key>Operation</key>");
2352 print_xml_string("string", ippOpString(op
));
2353 puts("<key>RequestAttributes</key>");
2358 for (attrptr
= request
->attrs
,
2359 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2361 attrptr
= attrptr
->next
)
2362 print_attr(attrptr
, &group
);
2367 else if (Output
== _CUPS_OUTPUT_TEST
)
2371 printf(" %s:\n", ippOpString(op
));
2373 for (attrptr
= request
->attrs
; attrptr
; attrptr
= attrptr
->next
)
2374 print_attr(attrptr
, NULL
);
2377 printf(" %-68.68s [", name
);
2381 if ((skip_previous
&& !prev_pass
) || skip_test
)
2388 if (Output
== _CUPS_OUTPUT_PLIST
)
2390 puts("<key>Successful</key>");
2392 puts("<key>StatusCode</key>");
2393 print_xml_string("string", "skip");
2394 puts("<key>ResponseAttributes</key>");
2397 else if (Output
== _CUPS_OUTPUT_TEST
)
2404 repeat_interval
= 1;
2413 if (transfer
== _CUPS_TRANSFER_CHUNKED
||
2414 (transfer
== _CUPS_TRANSFER_AUTO
&& filename
[0]))
2417 * Send request using chunking - a 0 length means "chunk".
2425 * Send request using content length...
2428 length
= ippLength(request
);
2430 if (filename
[0] && (reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2433 * Read the file to get the uncompressed file size...
2436 while ((bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
2439 cupsFileClose(reqfile
);
2444 * Send the request...
2451 if (status
!= HTTP_ERROR
)
2453 while (!response
&& !Cancel
&& prev_pass
)
2455 status
= cupsSendRequest(http
, request
, resource
, length
);
2459 httpSetField(http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
2460 #endif /* HAVE_LIBZ */
2462 if (!Cancel
&& status
== HTTP_CONTINUE
&&
2463 request
->state
== IPP_DATA
&& filename
[0])
2465 if ((reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2468 (bytes
= cupsFileRead(reqfile
, buffer
,
2469 sizeof(buffer
))) > 0)
2470 if ((status
= cupsWriteRequestData(http
, buffer
,
2471 bytes
)) != HTTP_CONTINUE
)
2474 cupsFileClose(reqfile
);
2478 snprintf(buffer
, sizeof(buffer
), "%s: %s", filename
,
2480 _cupsSetError(IPP_INTERNAL_ERROR
, buffer
, 0);
2482 status
= HTTP_ERROR
;
2487 * Get the server's response...
2490 if (!Cancel
&& status
!= HTTP_ERROR
)
2492 response
= cupsGetResponse(http
, resource
);
2493 status
= httpGetStatus(http
);
2496 if (!Cancel
&& status
== HTTP_ERROR
&& http
->error
!= EINVAL
&&
2498 http
->error
!= WSAETIMEDOUT
)
2500 http
->error
!= ETIMEDOUT
)
2503 if (httpReconnect(http
))
2506 else if (status
== HTTP_ERROR
)
2511 else if (status
!= HTTP_OK
)
2515 if (status
== HTTP_STATUS_UNAUTHORIZED
)
2523 if (!Cancel
&& status
== HTTP_ERROR
&& http
->error
!= EINVAL
&&
2525 http
->error
!= WSAETIMEDOUT
)
2527 http
->error
!= ETIMEDOUT
)
2530 if (httpReconnect(http
))
2533 else if (status
== HTTP_ERROR
)
2536 httpReconnect(http
);
2540 else if (status
!= HTTP_OK
)
2547 * Check results of request...
2550 cupsArrayClear(errors
);
2552 if (http
->version
!= HTTP_1_1
)
2553 add_stringf(errors
, "Bad HTTP version (%d.%d)", http
->version
/ 100,
2554 http
->version
% 100);
2559 * No response, log error...
2562 add_stringf(errors
, "IPP request failed with status %s (%s)",
2563 ippErrorString(cupsLastError()),
2564 cupsLastErrorString());
2569 * Collect common attribute values...
2572 if ((attrptr
= ippFindAttribute(response
, "job-id",
2573 IPP_TAG_INTEGER
)) != NULL
)
2575 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2576 set_variable(vars
, "job-id", temp
);
2579 if ((attrptr
= ippFindAttribute(response
, "job-uri",
2580 IPP_TAG_URI
)) != NULL
)
2581 set_variable(vars
, "job-uri", attrptr
->values
[0].string
.text
);
2583 if ((attrptr
= ippFindAttribute(response
, "notify-subscription-id",
2584 IPP_TAG_INTEGER
)) != NULL
)
2586 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2587 set_variable(vars
, "notify-subscription-id", temp
);
2591 * Check response, validating groups and attributes and logging errors
2595 if (response
->state
!= IPP_DATA
)
2597 "Missing end-of-attributes-tag in response "
2598 "(RFC 2910 section 3.5.1)");
2601 (response
->request
.status
.version
[0] != (version
/ 10) ||
2602 response
->request
.status
.version
[1] != (version
% 10)))
2604 "Bad version %d.%d in response - expected %d.%d "
2605 "(RFC 2911 section 3.1.8).",
2606 response
->request
.status
.version
[0],
2607 response
->request
.status
.version
[1],
2608 version
/ 10, version
% 10);
2610 if (response
->request
.status
.request_id
!= request_id
)
2612 "Bad request ID %d in response - expected %d "
2613 "(RFC 2911 section 3.1.1)",
2614 response
->request
.status
.request_id
, request_id
);
2616 attrptr
= response
->attrs
;
2619 "Missing first attribute \"attributes-charset "
2620 "(charset)\" in group operation-attributes-tag "
2621 "(RFC 2911 section 3.1.4).");
2624 if (!attrptr
->name
||
2625 attrptr
->value_tag
!= IPP_TAG_CHARSET
||
2626 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2627 attrptr
->num_values
!= 1 ||
2628 strcmp(attrptr
->name
, "attributes-charset"))
2630 "Bad first attribute \"%s (%s%s)\" in group %s, "
2631 "expected \"attributes-charset (charset)\" in "
2632 "group operation-attributes-tag (RFC 2911 section "
2634 attrptr
->name
? attrptr
->name
: "(null)",
2635 attrptr
->num_values
> 1 ? "1setOf " : "",
2636 ippTagString(attrptr
->value_tag
),
2637 ippTagString(attrptr
->group_tag
));
2639 attrptr
= attrptr
->next
;
2642 "Missing second attribute \"attributes-natural-"
2643 "language (naturalLanguage)\" in group "
2644 "operation-attributes-tag (RFC 2911 section "
2646 else if (!attrptr
->name
||
2647 attrptr
->value_tag
!= IPP_TAG_LANGUAGE
||
2648 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2649 attrptr
->num_values
!= 1 ||
2650 strcmp(attrptr
->name
, "attributes-natural-language"))
2652 "Bad first attribute \"%s (%s%s)\" in group %s, "
2653 "expected \"attributes-natural-language "
2654 "(naturalLanguage)\" in group "
2655 "operation-attributes-tag (RFC 2911 section "
2657 attrptr
->name
? attrptr
->name
: "(null)",
2658 attrptr
->num_values
> 1 ? "1setOf " : "",
2659 ippTagString(attrptr
->value_tag
),
2660 ippTagString(attrptr
->group_tag
));
2663 if ((attrptr
= ippFindAttribute(response
, "status-message",
2664 IPP_TAG_ZERO
)) != NULL
)
2666 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2668 "status-message (text(255)) has wrong value tag "
2669 "%s (RFC 2911 section 3.1.6.2).",
2670 ippTagString(attrptr
->value_tag
));
2671 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2673 "status-message (text(255)) has wrong group tag "
2674 "%s (RFC 2911 section 3.1.6.2).",
2675 ippTagString(attrptr
->group_tag
));
2676 if (attrptr
->num_values
!= 1)
2678 "status-message (text(255)) has %d values "
2679 "(RFC 2911 section 3.1.6.2).",
2680 attrptr
->num_values
);
2681 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2682 strlen(attrptr
->values
[0].string
.text
) > 255)
2684 "status-message (text(255)) has bad length %d"
2685 " (RFC 2911 section 3.1.6.2).",
2686 (int)strlen(attrptr
->values
[0].string
.text
));
2689 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
2690 IPP_TAG_ZERO
)) != NULL
)
2692 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2694 "detailed-status-message (text(MAX)) has wrong "
2695 "value tag %s (RFC 2911 section 3.1.6.3).",
2696 ippTagString(attrptr
->value_tag
));
2697 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2699 "detailed-status-message (text(MAX)) has wrong "
2700 "group tag %s (RFC 2911 section 3.1.6.3).",
2701 ippTagString(attrptr
->group_tag
));
2702 if (attrptr
->num_values
!= 1)
2704 "detailed-status-message (text(MAX)) has %d values"
2705 " (RFC 2911 section 3.1.6.3).",
2706 attrptr
->num_values
);
2707 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2708 strlen(attrptr
->values
[0].string
.text
) > 1023)
2710 "detailed-status-message (text(MAX)) has bad "
2711 "length %d (RFC 2911 section 3.1.6.3).",
2712 (int)strlen(attrptr
->values
[0].string
.text
));
2715 a
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2717 for (attrptr
= response
->attrs
,
2718 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2720 attrptr
= attrptr
->next
)
2722 if (attrptr
->group_tag
!= group
)
2724 int out_of_order
= 0; /* Are attribute groups out-of-order? */
2727 switch (attrptr
->group_tag
)
2732 case IPP_TAG_OPERATION
:
2736 case IPP_TAG_UNSUPPORTED_GROUP
:
2737 if (group
!= IPP_TAG_OPERATION
)
2742 case IPP_TAG_PRINTER
:
2743 if (group
!= IPP_TAG_OPERATION
&&
2744 group
!= IPP_TAG_UNSUPPORTED_GROUP
)
2748 case IPP_TAG_SUBSCRIPTION
:
2749 if (group
> attrptr
->group_tag
&&
2750 group
!= IPP_TAG_DOCUMENT
)
2755 if (group
> attrptr
->group_tag
)
2761 add_stringf(errors
, "Attribute groups out of order (%s < %s)",
2762 ippTagString(attrptr
->group_tag
),
2763 ippTagString(group
));
2765 if (attrptr
->group_tag
!= IPP_TAG_ZERO
)
2766 group
= attrptr
->group_tag
;
2769 validate_attr(errors
, attrptr
);
2773 if (cupsArrayFind(a
, attrptr
->name
))
2774 add_stringf(errors
, "Duplicate \"%s\" attribute in %s group",
2775 attrptr
->name
, ippTagString(group
));
2777 cupsArrayAdd(a
, attrptr
->name
);
2784 * Now check the test-defined expected status-code and attribute
2788 for (i
= 0; i
< num_statuses
; i
++)
2790 if (statuses
[i
].if_defined
&&
2791 !get_variable(vars
, statuses
[i
].if_defined
))
2794 if (statuses
[i
].if_not_defined
&&
2795 get_variable(vars
, statuses
[i
].if_not_defined
))
2798 if (response
->request
.status
.status_code
== statuses
[i
].status
)
2800 if (statuses
[i
].repeat_match
&&
2801 repeat_count
< statuses
[i
].repeat_limit
)
2804 if (statuses
[i
].define_match
)
2805 set_variable(vars
, statuses
[i
].define_match
, "1");
2811 if (statuses
[i
].repeat_no_match
&&
2812 repeat_count
< statuses
[i
].repeat_limit
)
2815 if (statuses
[i
].define_no_match
)
2817 set_variable(vars
, statuses
[i
].define_no_match
, "1");
2823 if (i
== num_statuses
&& num_statuses
> 0)
2825 for (i
= 0; i
< num_statuses
; i
++)
2827 if (statuses
[i
].if_defined
&&
2828 !get_variable(vars
, statuses
[i
].if_defined
))
2831 if (statuses
[i
].if_not_defined
&&
2832 get_variable(vars
, statuses
[i
].if_not_defined
))
2835 if (!statuses
[i
].repeat_match
)
2836 add_stringf(errors
, "EXPECTED: STATUS %s (got %s)",
2837 ippErrorString(statuses
[i
].status
),
2838 ippErrorString(cupsLastError()));
2841 if ((attrptr
= ippFindAttribute(response
, "status-message",
2842 IPP_TAG_TEXT
)) != NULL
)
2843 add_stringf(errors
, "status-message=\"%s\"",
2844 attrptr
->values
[0].string
.text
);
2847 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
2849 if (expect
->if_defined
&& !get_variable(vars
, expect
->if_defined
))
2852 if (expect
->if_not_defined
&&
2853 get_variable(vars
, expect
->if_not_defined
))
2856 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
2858 if ((found
&& expect
->not_expect
) ||
2859 (!found
&& !(expect
->not_expect
|| expect
->optional
)) ||
2860 (found
&& !expect_matches(expect
, found
->value_tag
)) ||
2861 (found
&& expect
->in_group
&&
2862 found
->group_tag
!= expect
->in_group
))
2864 if (expect
->define_no_match
)
2865 set_variable(vars
, expect
->define_no_match
, "1");
2866 else if (!expect
->define_match
&& !expect
->define_value
)
2868 if (found
&& expect
->not_expect
)
2869 add_stringf(errors
, "NOT EXPECTED: %s", expect
->name
);
2870 else if (!found
&& !(expect
->not_expect
|| expect
->optional
))
2871 add_stringf(errors
, "EXPECTED: %s", expect
->name
);
2874 if (!expect_matches(expect
, found
->value_tag
))
2875 add_stringf(errors
, "EXPECTED: %s OF-TYPE %s (got %s)",
2876 expect
->name
, expect
->of_type
,
2877 ippTagString(found
->value_tag
));
2879 if (expect
->in_group
&& found
->group_tag
!= expect
->in_group
)
2880 add_stringf(errors
, "EXPECTED: %s IN-GROUP %s (got %s).",
2881 expect
->name
, ippTagString(expect
->in_group
),
2882 ippTagString(found
->group_tag
));
2886 if (expect
->repeat_no_match
&&
2887 repeat_count
< expect
->repeat_limit
)
2894 ippAttributeString(found
, buffer
, sizeof(buffer
));
2897 !with_value(NULL
, expect
->with_value
, expect
->with_flags
, found
,
2898 buffer
, sizeof(buffer
)))
2900 if (expect
->define_no_match
)
2901 set_variable(vars
, expect
->define_no_match
, "1");
2902 else if (!expect
->define_match
&& !expect
->define_value
&&
2903 !expect
->repeat_match
&& !expect
->repeat_no_match
)
2905 if (expect
->with_flags
& _CUPS_WITH_REGEX
)
2906 add_stringf(errors
, "EXPECTED: %s %s /%s/",
2908 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
2909 "WITH-ALL-VALUES" : "WITH-VALUE",
2910 expect
->with_value
);
2912 add_stringf(errors
, "EXPECTED: %s %s \"%s\"",
2914 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
2915 "WITH-ALL-VALUES" : "WITH-VALUE",
2916 expect
->with_value
);
2918 with_value(errors
, expect
->with_value
, expect
->with_flags
, found
,
2919 buffer
, sizeof(buffer
));
2922 if (expect
->repeat_no_match
&&
2923 repeat_count
< expect
->repeat_limit
)
2929 if (found
&& expect
->count
> 0 &&
2930 found
->num_values
!= expect
->count
)
2932 if (expect
->define_no_match
)
2933 set_variable(vars
, expect
->define_no_match
, "1");
2934 else if (!expect
->define_match
&& !expect
->define_value
)
2936 add_stringf(errors
, "EXPECTED: %s COUNT %d (got %d)", expect
->name
,
2937 expect
->count
, found
->num_values
);
2940 if (expect
->repeat_no_match
&&
2941 repeat_count
< expect
->repeat_limit
)
2947 if (found
&& expect
->same_count_as
)
2949 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
2952 if (!attrptr
|| attrptr
->num_values
!= found
->num_values
)
2954 if (expect
->define_no_match
)
2955 set_variable(vars
, expect
->define_no_match
, "1");
2956 else if (!expect
->define_match
&& !expect
->define_value
)
2960 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2961 "(not returned)", expect
->name
,
2962 found
->num_values
, expect
->same_count_as
);
2963 else if (attrptr
->num_values
!= found
->num_values
)
2965 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2966 "(%d values)", expect
->name
, found
->num_values
,
2967 expect
->same_count_as
, attrptr
->num_values
);
2970 if (expect
->repeat_no_match
&&
2971 repeat_count
< expect
->repeat_limit
)
2978 if (found
&& expect
->define_match
)
2979 set_variable(vars
, expect
->define_match
, "1");
2981 if (found
&& expect
->define_value
)
2982 set_variable(vars
, expect
->define_value
, buffer
);
2984 if (found
&& expect
->repeat_match
&&
2985 repeat_count
< expect
->repeat_limit
)
2991 * If we are going to repeat this test, sleep 1 second so we don't flood
2992 * the printer with requests...
2997 if (Output
== _CUPS_OUTPUT_TEST
)
2999 printf("%04d]\n", repeat_count
);
3003 sleep(repeat_interval
);
3004 repeat_interval
= _cupsNextDelay(repeat_interval
, &repeat_prev
);
3006 if (Output
== _CUPS_OUTPUT_TEST
)
3008 printf(" %-68.68s [", name
);
3013 while (repeat_test
);
3019 if (cupsArrayCount(errors
) > 0)
3020 prev_pass
= pass
= 0;
3027 if (Output
== _CUPS_OUTPUT_PLIST
)
3029 puts("<key>Successful</key>");
3030 puts(prev_pass
? "<true />" : "<false />");
3031 puts("<key>StatusCode</key>");
3032 print_xml_string("string", ippErrorString(cupsLastError()));
3033 puts("<key>ResponseAttributes</key>");
3036 for (attrptr
= response
? response
->attrs
: NULL
,
3037 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
3039 attrptr
= attrptr
->next
)
3040 print_attr(attrptr
, &group
);
3044 else if (Output
== _CUPS_OUTPUT_TEST
)
3046 puts(prev_pass
? "PASS]" : "FAIL]");
3048 if (!prev_pass
|| (Verbosity
&& response
))
3050 printf(" RECEIVED: %lu bytes in response\n",
3051 (unsigned long)ippLength(response
));
3052 printf(" status-code = %s (%s)\n", ippErrorString(cupsLastError()),
3053 cupsLastErrorString());
3057 for (attrptr
= response
->attrs
;
3059 attrptr
= attrptr
->next
)
3060 print_attr(attrptr
, NULL
);
3064 else if (!prev_pass
)
3065 fprintf(stderr
, "%s\n", cupsLastErrorString());
3067 if (prev_pass
&& Output
>= _CUPS_OUTPUT_LIST
&& !Verbosity
&&
3070 size_t width
; /* Length of value */
3072 for (i
= 0; i
< num_displayed
; i
++)
3074 widths
[i
] = strlen(displayed
[i
]);
3076 for (attrptr
= ippFindAttribute(response
, displayed
[i
], IPP_TAG_ZERO
);
3078 attrptr
= ippFindNextAttribute(response
, displayed
[i
],
3081 width
= ippAttributeString(attrptr
, NULL
, 0);
3082 if (width
> widths
[i
])
3087 if (Output
== _CUPS_OUTPUT_CSV
)
3088 print_csv(NULL
, num_displayed
, displayed
, widths
);
3090 print_line(NULL
, num_displayed
, displayed
, widths
);
3092 attrptr
= response
->attrs
;
3096 while (attrptr
&& attrptr
->group_tag
<= IPP_TAG_OPERATION
)
3097 attrptr
= attrptr
->next
;
3101 if (Output
== _CUPS_OUTPUT_CSV
)
3102 print_csv(attrptr
, num_displayed
, displayed
, widths
);
3104 print_line(attrptr
, num_displayed
, displayed
, widths
);
3106 while (attrptr
&& attrptr
->group_tag
> IPP_TAG_OPERATION
)
3107 attrptr
= attrptr
->next
;
3111 else if (!prev_pass
)
3113 if (Output
== _CUPS_OUTPUT_PLIST
)
3115 puts("<key>Errors</key>");
3118 for (error
= (char *)cupsArrayFirst(errors
);
3120 error
= (char *)cupsArrayNext(errors
))
3121 print_xml_string("string", error
);
3127 for (error
= (char *)cupsArrayFirst(errors
);
3129 error
= (char *)cupsArrayNext(errors
))
3130 printf(" %s\n", error
);
3134 if (num_displayed
> 0 && !Verbosity
&& response
&&
3135 Output
== _CUPS_OUTPUT_TEST
)
3137 for (attrptr
= response
->attrs
;
3139 attrptr
= attrptr
->next
)
3143 for (i
= 0; i
< num_displayed
; i
++)
3145 if (!strcmp(displayed
[i
], attrptr
->name
))
3147 print_attr(attrptr
, NULL
);
3157 if (Output
== _CUPS_OUTPUT_PLIST
)
3162 ippDelete(response
);
3165 for (i
= 0; i
< num_statuses
; i
++)
3167 if (statuses
[i
].if_defined
)
3168 free(statuses
[i
].if_defined
);
3169 if (statuses
[i
].if_not_defined
)
3170 free(statuses
[i
].if_not_defined
);
3171 if (statuses
[i
].define_match
)
3172 free(statuses
[i
].define_match
);
3173 if (statuses
[i
].define_no_match
)
3174 free(statuses
[i
].define_no_match
);
3178 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3181 if (expect
->of_type
)
3182 free(expect
->of_type
);
3183 if (expect
->same_count_as
)
3184 free(expect
->same_count_as
);
3185 if (expect
->if_defined
)
3186 free(expect
->if_defined
);
3187 if (expect
->if_not_defined
)
3188 free(expect
->if_not_defined
);
3189 if (expect
->with_value
)
3190 free(expect
->with_value
);
3191 if (expect
->define_match
)
3192 free(expect
->define_match
);
3193 if (expect
->define_no_match
)
3194 free(expect
->define_no_match
);
3195 if (expect
->define_value
)
3196 free(expect
->define_value
);
3200 for (i
= 0; i
< num_displayed
; i
++)
3204 if (!ignore_errors
&& !prev_pass
)
3210 cupsArrayDelete(errors
);
3217 ippDelete(response
);
3219 for (i
= 0; i
< num_statuses
; i
++)
3221 if (statuses
[i
].if_defined
)
3222 free(statuses
[i
].if_defined
);
3223 if (statuses
[i
].if_not_defined
)
3224 free(statuses
[i
].if_not_defined
);
3225 if (statuses
[i
].define_match
)
3226 free(statuses
[i
].define_match
);
3227 if (statuses
[i
].define_no_match
)
3228 free(statuses
[i
].define_no_match
);
3231 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3234 if (expect
->of_type
)
3235 free(expect
->of_type
);
3236 if (expect
->same_count_as
)
3237 free(expect
->same_count_as
);
3238 if (expect
->if_defined
)
3239 free(expect
->if_defined
);
3240 if (expect
->if_not_defined
)
3241 free(expect
->if_not_defined
);
3242 if (expect
->with_value
)
3243 free(expect
->with_value
);
3244 if (expect
->define_match
)
3245 free(expect
->define_match
);
3246 if (expect
->define_no_match
)
3247 free(expect
->define_no_match
);
3248 if (expect
->define_value
)
3249 free(expect
->define_value
);
3252 for (i
= 0; i
< num_displayed
; i
++)
3260 * 'expand_variables()' - Expand variables in a string.
3264 expand_variables(_cups_vars_t
*vars
, /* I - Variables */
3265 char *dst
, /* I - Destination string buffer */
3266 const char *src
, /* I - Source string */
3267 size_t dstsize
) /* I - Size of destination buffer */
3269 char *dstptr
, /* Pointer into destination */
3270 *dstend
, /* End of destination */
3271 temp
[256], /* Temporary string */
3272 *tempptr
; /* Pointer into temporary string */
3273 const char *value
; /* Value to substitute */
3277 dstend
= dst
+ dstsize
- 1;
3279 while (*src
&& dstptr
< dstend
)
3284 * Substitute a string/number...
3287 if (!strncmp(src
, "$$", 2))
3292 else if (!strncmp(src
, "$ENV[", 5))
3294 strlcpy(temp
, src
+ 5, sizeof(temp
));
3296 for (tempptr
= temp
; *tempptr
; tempptr
++)
3297 if (*tempptr
== ']')
3303 value
= getenv(temp
);
3304 src
+= tempptr
- temp
+ 5;
3308 strlcpy(temp
, src
+ 1, sizeof(temp
));
3310 for (tempptr
= temp
; *tempptr
; tempptr
++)
3311 if (!isalnum(*tempptr
& 255) && *tempptr
!= '-' && *tempptr
!= '_')
3317 if (!strcmp(temp
, "uri"))
3319 else if (!strcmp(temp
, "filename"))
3320 value
= vars
->filename
;
3321 else if (!strcmp(temp
, "scheme") || !strcmp(temp
, "method"))
3322 value
= vars
->scheme
;
3323 else if (!strcmp(temp
, "username"))
3324 value
= vars
->userpass
;
3325 else if (!strcmp(temp
, "hostname"))
3326 value
= vars
->hostname
;
3327 else if (!strcmp(temp
, "port"))
3329 snprintf(temp
, sizeof(temp
), "%d", vars
->port
);
3332 else if (!strcmp(temp
, "resource"))
3333 value
= vars
->resource
;
3334 else if (!strcmp(temp
, "user"))
3337 value
= get_variable(vars
, temp
);
3339 src
+= tempptr
- temp
+ 1;
3349 strlcpy(dstptr
, value
, dstend
- dstptr
+ 1);
3350 dstptr
+= strlen(dstptr
);
3362 * 'expect_matches()' - Return true if the tag matches the specification.
3365 static int /* O - 1 if matches, 0 otherwise */
3367 _cups_expect_t
*expect
, /* I - Expected attribute */
3368 ipp_tag_t value_tag
) /* I - Value tag for attribute */
3370 int match
; /* Match? */
3371 char *of_type
, /* Type name to match */
3372 *next
, /* Next name to match */
3373 sep
; /* Separator character */
3377 * If we don't expect a particular type, return immediately...
3380 if (!expect
->of_type
)
3384 * Parse the "of_type" value since the string can contain multiple attribute
3385 * types separated by "," or "|"...
3388 for (of_type
= expect
->of_type
, match
= 0; !match
&& *of_type
; of_type
= next
)
3391 * Find the next separator, and set it (temporarily) to nul if present.
3394 for (next
= of_type
; *next
&& *next
!= '|' && *next
!= ','; next
++);
3396 if ((sep
= *next
) != '\0')
3400 * Support some meta-types to make it easier to write the test file.
3403 if (!strcmp(of_type
, "text"))
3404 match
= value_tag
== IPP_TAG_TEXTLANG
|| value_tag
== IPP_TAG_TEXT
;
3405 else if (!strcmp(of_type
, "name"))
3406 match
= value_tag
== IPP_TAG_NAMELANG
|| value_tag
== IPP_TAG_NAME
;
3407 else if (!strcmp(of_type
, "collection"))
3408 match
= value_tag
== IPP_TAG_BEGIN_COLLECTION
;
3410 match
= value_tag
== ippTagValue(of_type
);
3413 * Restore the separator if we have one...
3425 * 'get_collection()' - Get a collection value from the current test file.
3428 static ipp_t
* /* O - Collection value */
3429 get_collection(_cups_vars_t
*vars
, /* I - Variables */
3430 FILE *fp
, /* I - File to read from */
3431 int *linenum
) /* IO - Line number */
3433 char token
[1024], /* Token from file */
3434 temp
[1024], /* Temporary string */
3435 attr
[128]; /* Attribute name */
3436 ipp_tag_t value
; /* Current value type */
3437 ipp_t
*col
= ippNew(); /* Collection value */
3438 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
3441 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
3443 if (!strcmp(token
, "}"))
3445 else if (!strcmp(token
, "{") && lastcol
)
3448 * Another collection value
3451 ipp_t
*subcol
= get_collection(vars
, fp
, linenum
);
3452 /* Collection value */
3456 ipp_attribute_t
*tempcol
; /* Pointer to new buffer */
3460 * Reallocate memory...
3463 if ((tempcol
= realloc(lastcol
, sizeof(ipp_attribute_t
) +
3464 (lastcol
->num_values
+ 1) *
3465 sizeof(_ipp_value_t
))) == NULL
)
3467 print_fatal_error("Unable to allocate memory on line %d.", *linenum
);
3471 if (tempcol
!= lastcol
)
3474 * Reset pointers in the list...
3478 col
->prev
->next
= tempcol
;
3480 col
->attrs
= tempcol
;
3482 lastcol
= col
->current
= col
->last
= tempcol
;
3485 lastcol
->values
[lastcol
->num_values
].collection
= subcol
;
3486 lastcol
->num_values
++;
3491 else if (!_cups_strcasecmp(token
, "MEMBER"))
3499 if (!get_token(fp
, token
, sizeof(token
), linenum
))
3501 print_fatal_error("Missing MEMBER value tag on line %d.", *linenum
);
3505 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
3507 print_fatal_error("Bad MEMBER value tag \"%s\" on line %d.", token
,
3512 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
3514 print_fatal_error("Missing MEMBER name on line %d.", *linenum
);
3518 if (!get_token(fp
, temp
, sizeof(temp
), linenum
))
3520 print_fatal_error("Missing MEMBER value on line %d.", *linenum
);
3524 expand_variables(vars
, token
, temp
, sizeof(token
));
3528 case IPP_TAG_BOOLEAN
:
3529 if (!_cups_strcasecmp(token
, "true"))
3530 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
3532 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, atoi(token
));
3535 case IPP_TAG_INTEGER
:
3537 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
3540 case IPP_TAG_RESOLUTION
:
3542 int xres
, /* X resolution */
3543 yres
; /* Y resolution */
3544 char units
[6]; /* Units */
3546 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
3547 (_cups_strcasecmp(units
, "dpi") &&
3548 _cups_strcasecmp(units
, "dpc") &&
3549 _cups_strcasecmp(units
, "dpcm") &&
3550 _cups_strcasecmp(units
, "other")))
3552 print_fatal_error("Bad resolution value \"%s\" on line %d.",
3557 if (!_cups_strcasecmp(units
, "dpi"))
3558 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
3560 else if (!_cups_strcasecmp(units
, "dpc") ||
3561 !_cups_strcasecmp(units
, "dpcm"))
3562 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
3565 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
3570 case IPP_TAG_RANGE
:
3572 int lowers
[4], /* Lower value */
3573 uppers
[4], /* Upper values */
3574 num_vals
; /* Number of values */
3577 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
3578 lowers
+ 0, uppers
+ 0,
3579 lowers
+ 1, uppers
+ 1,
3580 lowers
+ 2, uppers
+ 2,
3581 lowers
+ 3, uppers
+ 3);
3583 if ((num_vals
& 1) || num_vals
== 0)
3585 print_fatal_error("Bad rangeOfInteger value \"%s\" on line %d.",
3590 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
3595 case IPP_TAG_BEGIN_COLLECTION
:
3596 if (!strcmp(token
, "{"))
3598 ipp_t
*subcol
= get_collection(vars
, fp
, linenum
);
3599 /* Collection value */
3603 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
3611 print_fatal_error("Bad collection value on line %d.", *linenum
);
3615 case IPP_TAG_STRING
:
3616 ippAddOctetString(col
, IPP_TAG_ZERO
, attr
, token
, strlen(token
));
3620 if (!strchr(token
, ','))
3621 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
3625 * Multiple string values...
3628 int num_values
; /* Number of values */
3629 char *values
[100], /* Values */
3630 *ptr
; /* Pointer to next value */
3636 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
3639 values
[num_values
] = ptr
;
3643 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
3644 NULL
, (const char **)values
);
3654 * If we get here there was a parse error; free memory and return.
3666 * 'get_filename()' - Get a filename based on the current test file.
3669 static char * /* O - Filename */
3670 get_filename(const char *testfile
, /* I - Current test file */
3671 char *dst
, /* I - Destination filename */
3672 const char *src
, /* I - Source filename */
3673 size_t dstsize
) /* I - Size of destination buffer */
3675 char *dstptr
; /* Pointer into destination */
3676 _cups_globals_t
*cg
= _cupsGlobals();
3680 if (*src
== '<' && src
[strlen(src
) - 1] == '>')
3683 * Map <filename> to CUPS_DATADIR/ipptool/filename...
3686 snprintf(dst
, dstsize
, "%s/ipptool/%s", cg
->cups_datadir
, src
+ 1);
3687 dstptr
= dst
+ strlen(dst
) - 1;
3691 else if (*src
== '/' || !strchr(testfile
, '/')
3693 || (isalpha(*src
& 255) && src
[1] == ':')
3698 * Use the path as-is...
3701 strlcpy(dst
, src
, dstsize
);
3706 * Make path relative to testfile...
3709 strlcpy(dst
, testfile
, dstsize
);
3710 if ((dstptr
= strrchr(dst
, '/')) != NULL
)
3713 dstptr
= dst
; /* Should never happen */
3715 strlcpy(dstptr
, src
, dstsize
- (dstptr
- dst
));
3723 * 'get_token()' - Get a token from a file.
3726 static char * /* O - Token from file or NULL on EOF */
3727 get_token(FILE *fp
, /* I - File to read from */
3728 char *buf
, /* I - Buffer to read into */
3729 int buflen
, /* I - Length of buffer */
3730 int *linenum
) /* IO - Current line number */
3732 int ch
, /* Character from file */
3733 quote
; /* Quoting character */
3734 char *bufptr
, /* Pointer into buffer */
3735 *bufend
; /* End of buffer */
3741 * Skip whitespace...
3744 while (isspace(ch
= getc(fp
)))
3756 else if (ch
== '\'' || ch
== '\"')
3759 * Quoted text or regular expression...
3764 bufend
= buf
+ buflen
- 1;
3766 while ((ch
= getc(fp
)) != EOF
)
3771 * Escape next character...
3774 if (bufptr
< bufend
)
3777 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
3780 else if (ch
== quote
)
3782 else if (bufptr
< bufend
)
3796 while ((ch
= getc(fp
)) != EOF
)
3805 * Whitespace delimited text...
3811 bufend
= buf
+ buflen
- 1;
3813 while ((ch
= getc(fp
)) != EOF
)
3814 if (isspace(ch
) || ch
== '#')
3816 else if (bufptr
< bufend
)
3821 else if (ch
== '\n')
3833 * 'get_variable()' - Get the value of a variable.
3836 static char * /* O - Value or NULL */
3837 get_variable(_cups_vars_t
*vars
, /* I - Variables */
3838 const char *name
) /* I - Variable name */
3840 _cups_var_t key
, /* Search key */
3841 *match
; /* Matching variable, if any */
3844 key
.name
= (char *)name
;
3845 match
= cupsArrayFind(vars
->vars
, &key
);
3847 return (match
? match
->value
: NULL
);
3852 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
3856 static char * /* O - ISO 8601 date/time string */
3857 iso_date(ipp_uchar_t
*date
) /* I - IPP (RFC 1903) date/time value */
3859 time_t utctime
; /* UTC time since 1970 */
3860 struct tm
*utcdate
; /* UTC date/time */
3861 static char buffer
[255]; /* String buffer */
3864 utctime
= ippDateToTime(date
);
3865 utcdate
= gmtime(&utctime
);
3867 snprintf(buffer
, sizeof(buffer
), "%04d-%02d-%02dT%02d:%02d:%02dZ",
3868 utcdate
->tm_year
+ 1900, utcdate
->tm_mon
+ 1, utcdate
->tm_mday
,
3869 utcdate
->tm_hour
, utcdate
->tm_min
, utcdate
->tm_sec
);
3876 * 'password_cb()' - Password callback for authenticated tests.
3879 static const char * /* O - Password */
3880 password_cb(const char *prompt
) /* I - Prompt (unused) */
3889 * 'print_attr()' - Print an attribute on the screen.
3893 print_attr(ipp_attribute_t
*attr
, /* I - Attribute to print */
3894 ipp_tag_t
*group
) /* IO - Current group */
3896 int i
; /* Looping var */
3897 ipp_attribute_t
*colattr
; /* Collection attribute */
3900 if (Output
== _CUPS_OUTPUT_PLIST
)
3902 if (!attr
->name
|| (group
&& *group
!= attr
->group_tag
))
3904 if (attr
->group_tag
!= IPP_TAG_ZERO
)
3911 *group
= attr
->group_tag
;
3917 print_xml_string("key", attr
->name
);
3918 if (attr
->num_values
> 1)
3921 switch (attr
->value_tag
)
3923 case IPP_TAG_INTEGER
:
3925 for (i
= 0; i
< attr
->num_values
; i
++)
3926 if (Output
== _CUPS_OUTPUT_PLIST
)
3927 printf("<integer>%d</integer>\n", attr
->values
[i
].integer
);
3929 printf("%d ", attr
->values
[i
].integer
);
3932 case IPP_TAG_BOOLEAN
:
3933 for (i
= 0; i
< attr
->num_values
; i
++)
3934 if (Output
== _CUPS_OUTPUT_PLIST
)
3935 puts(attr
->values
[i
].boolean
? "<true />" : "<false />");
3936 else if (attr
->values
[i
].boolean
)
3937 fputs("true ", stdout
);
3939 fputs("false ", stdout
);
3942 case IPP_TAG_RANGE
:
3943 for (i
= 0; i
< attr
->num_values
; i
++)
3944 if (Output
== _CUPS_OUTPUT_PLIST
)
3945 printf("<dict><key>lower</key><integer>%d</integer>"
3946 "<key>upper</key><integer>%d</integer></dict>\n",
3947 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
3949 printf("%d-%d ", attr
->values
[i
].range
.lower
,
3950 attr
->values
[i
].range
.upper
);
3953 case IPP_TAG_RESOLUTION
:
3954 for (i
= 0; i
< attr
->num_values
; i
++)
3955 if (Output
== _CUPS_OUTPUT_PLIST
)
3956 printf("<dict><key>xres</key><integer>%d</integer>"
3957 "<key>yres</key><integer>%d</integer>"
3958 "<key>units</key><string>%s</string></dict>\n",
3959 attr
->values
[i
].resolution
.xres
,
3960 attr
->values
[i
].resolution
.yres
,
3961 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
3964 printf("%dx%d%s ", attr
->values
[i
].resolution
.xres
,
3965 attr
->values
[i
].resolution
.yres
,
3966 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
3971 for (i
= 0; i
< attr
->num_values
; i
++)
3972 if (Output
== _CUPS_OUTPUT_PLIST
)
3973 printf("<date>%s</date>\n", iso_date(attr
->values
[i
].date
));
3975 printf("%s ", iso_date(attr
->values
[i
].date
));
3978 case IPP_TAG_STRING
:
3979 for (i
= 0; i
< attr
->num_values
; i
++)
3981 if (Output
== _CUPS_OUTPUT_PLIST
)
3983 char buffer
[IPP_MAX_LENGTH
* 5 / 4 + 1];
3986 printf("<data>%s</data>\n",
3987 httpEncode64_2(buffer
, sizeof(buffer
),
3988 attr
->values
[i
].unknown
.data
,
3989 attr
->values
[i
].unknown
.length
));
3993 char *ptr
, /* Pointer into data */
3994 *end
; /* End of data */
3997 for (ptr
= attr
->values
[i
].unknown
.data
,
3998 end
= ptr
+ attr
->values
[i
].unknown
.length
;
4002 if (*ptr
== '\\' || *ptr
== '\"')
4003 printf("\\%c", *ptr
);
4004 else if (!isprint(*ptr
& 255))
4005 printf("\\%03o", *ptr
& 255);
4016 case IPP_TAG_KEYWORD
:
4017 case IPP_TAG_CHARSET
:
4019 case IPP_TAG_MIMETYPE
:
4020 case IPP_TAG_LANGUAGE
:
4021 for (i
= 0; i
< attr
->num_values
; i
++)
4022 if (Output
== _CUPS_OUTPUT_PLIST
)
4023 print_xml_string("string", attr
->values
[i
].string
.text
);
4025 printf("\"%s\" ", attr
->values
[i
].string
.text
);
4028 case IPP_TAG_TEXTLANG
:
4029 case IPP_TAG_NAMELANG
:
4030 for (i
= 0; i
< attr
->num_values
; i
++)
4031 if (Output
== _CUPS_OUTPUT_PLIST
)
4033 fputs("<dict><key>language</key><string>", stdout
);
4034 print_xml_string(NULL
, attr
->values
[i
].string
.language
);
4035 fputs("</string><key>string</key><string>", stdout
);
4036 print_xml_string(NULL
, attr
->values
[i
].string
.text
);
4037 puts("</string></dict>");
4040 printf("\"%s\"[%s] ", attr
->values
[i
].string
.text
,
4041 attr
->values
[i
].string
.language
);
4044 case IPP_TAG_BEGIN_COLLECTION
:
4045 for (i
= 0; i
< attr
->num_values
; i
++)
4047 if (Output
== _CUPS_OUTPUT_PLIST
)
4050 for (colattr
= attr
->values
[i
].collection
->attrs
;
4052 colattr
= colattr
->next
)
4053 print_attr(colattr
, NULL
);
4061 print_col(attr
->values
[i
].collection
);
4067 if (Output
== _CUPS_OUTPUT_PLIST
)
4068 printf("<string><<%s>></string>\n",
4069 ippTagString(attr
->value_tag
));
4071 fputs(ippTagString(attr
->value_tag
), stdout
);
4075 if (attr
->num_values
> 1)
4080 char buffer
[8192]; /* Value buffer */
4082 if (Output
== _CUPS_OUTPUT_TEST
)
4086 puts(" -- separator --");
4090 printf(" %s (%s%s) = ", attr
->name
,
4091 attr
->num_values
> 1 ? "1setOf " : "",
4092 ippTagString(attr
->value_tag
));
4095 ippAttributeString(attr
, buffer
, sizeof(buffer
));
4102 * 'print_col()' - Print a collection attribute on the screen.
4106 print_col(ipp_t
*col
) /* I - Collection attribute to print */
4108 int i
; /* Looping var */
4109 ipp_attribute_t
*attr
; /* Current attribute in collection */
4112 fputs("{ ", stdout
);
4113 for (attr
= col
->attrs
; attr
; attr
= attr
->next
)
4115 printf("%s (%s%s) = ", attr
->name
, attr
->num_values
> 1 ? "1setOf " : "",
4116 ippTagString(attr
->value_tag
));
4118 switch (attr
->value_tag
)
4120 case IPP_TAG_INTEGER
:
4122 for (i
= 0; i
< attr
->num_values
; i
++)
4123 printf("%d ", attr
->values
[i
].integer
);
4126 case IPP_TAG_BOOLEAN
:
4127 for (i
= 0; i
< attr
->num_values
; i
++)
4128 if (attr
->values
[i
].boolean
)
4134 case IPP_TAG_NOVALUE
:
4138 case IPP_TAG_RANGE
:
4139 for (i
= 0; i
< attr
->num_values
; i
++)
4140 printf("%d-%d ", attr
->values
[i
].range
.lower
,
4141 attr
->values
[i
].range
.upper
);
4144 case IPP_TAG_RESOLUTION
:
4145 for (i
= 0; i
< attr
->num_values
; i
++)
4146 printf("%dx%d%s ", attr
->values
[i
].resolution
.xres
,
4147 attr
->values
[i
].resolution
.yres
,
4148 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
4152 case IPP_TAG_STRING
:
4155 case IPP_TAG_KEYWORD
:
4156 case IPP_TAG_CHARSET
:
4158 case IPP_TAG_MIMETYPE
:
4159 case IPP_TAG_LANGUAGE
:
4160 for (i
= 0; i
< attr
->num_values
; i
++)
4161 printf("\"%s\" ", attr
->values
[i
].string
.text
);
4164 case IPP_TAG_TEXTLANG
:
4165 case IPP_TAG_NAMELANG
:
4166 for (i
= 0; i
< attr
->num_values
; i
++)
4167 printf("\"%s\"[%s] ", attr
->values
[i
].string
.text
,
4168 attr
->values
[i
].string
.language
);
4171 case IPP_TAG_BEGIN_COLLECTION
:
4172 for (i
= 0; i
< attr
->num_values
; i
++)
4174 print_col(attr
->values
[i
].collection
);
4180 break; /* anti-compiler-warning-code */
4189 * 'print_csv()' - Print a line of CSV text.
4194 ipp_attribute_t
*attr
, /* I - First attribute for line */
4195 int num_displayed
, /* I - Number of attributes to display */
4196 char **displayed
, /* I - Attributes to display */
4197 size_t *widths
) /* I - Column widths */
4199 int i
; /* Looping var */
4200 size_t maxlength
; /* Max length of all columns */
4201 char *buffer
, /* String buffer */
4202 *bufptr
; /* Pointer into buffer */
4203 ipp_attribute_t
*current
; /* Current attribute */
4207 * Get the maximum string length we have to show and allocate...
4210 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4211 if (widths
[i
] > maxlength
)
4212 maxlength
= widths
[i
];
4216 if ((buffer
= malloc(maxlength
)) == NULL
)
4220 * Loop through the attributes to display...
4225 for (i
= 0; i
< num_displayed
; i
++)
4232 for (current
= attr
; current
; current
= current
->next
)
4236 else if (!strcmp(current
->name
, displayed
[i
]))
4238 ippAttributeString(current
, buffer
, maxlength
);
4243 if (strchr(buffer
, ',') != NULL
|| strchr(buffer
, '\"') != NULL
||
4244 strchr(buffer
, '\\') != NULL
)
4247 for (bufptr
= buffer
; *bufptr
; bufptr
++)
4249 if (*bufptr
== '\\' || *bufptr
== '\"')
4256 fputs(buffer
, stdout
);
4262 for (i
= 0; i
< num_displayed
; i
++)
4267 fputs(displayed
[i
], stdout
);
4277 * 'print_fatal_error()' - Print a fatal error message.
4281 print_fatal_error(const char *s
, /* I - Printf-style format string */
4282 ...) /* I - Additional arguments as needed */
4284 char buffer
[10240]; /* Format buffer */
4285 va_list ap
; /* Pointer to arguments */
4289 * Format the error message...
4293 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
4300 if (Output
== _CUPS_OUTPUT_PLIST
)
4303 print_xml_trailer(0, buffer
);
4306 _cupsLangPrintf(stderr
, "ipptool: %s", buffer
);
4311 * 'print_line()' - Print a line of formatted or CSV text.
4316 ipp_attribute_t
*attr
, /* I - First attribute for line */
4317 int num_displayed
, /* I - Number of attributes to display */
4318 char **displayed
, /* I - Attributes to display */
4319 size_t *widths
) /* I - Column widths */
4321 int i
; /* Looping var */
4322 size_t maxlength
; /* Max length of all columns */
4323 char *buffer
; /* String buffer */
4324 ipp_attribute_t
*current
; /* Current attribute */
4328 * Get the maximum string length we have to show and allocate...
4331 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4332 if (widths
[i
] > maxlength
)
4333 maxlength
= widths
[i
];
4337 if ((buffer
= malloc(maxlength
)) == NULL
)
4341 * Loop through the attributes to display...
4346 for (i
= 0; i
< num_displayed
; i
++)
4353 for (current
= attr
; current
; current
= current
->next
)
4357 else if (!strcmp(current
->name
, displayed
[i
]))
4359 ippAttributeString(current
, buffer
, maxlength
);
4364 printf("%*s", (int)-widths
[i
], buffer
);
4370 for (i
= 0; i
< num_displayed
; i
++)
4375 printf("%*s", (int)-widths
[i
], displayed
[i
]);
4379 for (i
= 0; i
< num_displayed
; i
++)
4384 memset(buffer
, '-', widths
[i
]);
4385 buffer
[widths
[i
]] = '\0';
4386 fputs(buffer
, stdout
);
4396 * 'print_xml_header()' - Print a standard XML plist header.
4400 print_xml_header(void)
4404 puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
4405 puts("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
4406 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
4407 puts("<plist version=\"1.0\">");
4409 puts("<key>Transfer</key>");
4410 printf("<string>%s</string>\n",
4411 Transfer
== _CUPS_TRANSFER_AUTO
? "auto" :
4412 Transfer
== _CUPS_TRANSFER_CHUNKED
? "chunked" : "length");
4413 puts("<key>Tests</key>");
4422 * 'print_xml_string()' - Print an XML string with escaping.
4426 print_xml_string(const char *element
, /* I - Element name or NULL */
4427 const char *s
) /* I - String to print */
4430 printf("<%s>", element
);
4435 fputs("&", stdout
);
4437 fputs("<", stdout
);
4439 fputs(">", stdout
);
4440 else if ((*s
& 0xe0) == 0xc0)
4443 * Validate UTF-8 two-byte sequence...
4446 if ((s
[1] & 0xc0) != 0x80)
4457 else if ((*s
& 0xf0) == 0xe0)
4460 * Validate UTF-8 three-byte sequence...
4463 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80)
4475 else if ((*s
& 0xf8) == 0xf0)
4478 * Validate UTF-8 four-byte sequence...
4481 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80 ||
4482 (s
[3] & 0xc0) != 0x80)
4495 else if ((*s
& 0x80) || (*s
< ' ' && !isspace(*s
& 255)))
4498 * Invalid control character...
4510 printf("</%s>\n", element
);
4515 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
4519 print_xml_trailer(int success
, /* I - 1 on success, 0 on failure */
4520 const char *message
) /* I - Error message or NULL */
4525 puts("<key>Successful</key>");
4526 puts(success
? "<true />" : "<false />");
4529 puts("<key>ErrorMessage</key>");
4530 print_xml_string("string", message
);
4541 * 'set_variable()' - Set a variable value.
4545 set_variable(_cups_vars_t
*vars
, /* I - Variables */
4546 const char *name
, /* I - Variable name */
4547 const char *value
) /* I - Value string */
4549 _cups_var_t key
, /* Search key */
4550 *var
; /* New variable */
4553 if (!_cups_strcasecmp(name
, "filename"))
4556 free(vars
->filename
);
4558 vars
->filename
= strdup(value
);
4562 key
.name
= (char *)name
;
4563 if ((var
= cupsArrayFind(vars
->vars
, &key
)) != NULL
)
4566 var
->value
= strdup(value
);
4568 else if ((var
= malloc(sizeof(_cups_var_t
))) == NULL
)
4570 print_fatal_error("Unable to allocate memory for variable \"%s\".", name
);
4575 var
->name
= strdup(name
);
4576 var
->value
= strdup(value
);
4578 cupsArrayAdd(vars
->vars
, var
);
4585 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
4589 sigterm_handler(int sig
) /* I - Signal number (unused) */
4595 signal(SIGINT
, SIG_DFL
);
4596 signal(SIGTERM
, SIG_DFL
);
4602 * 'timeout_cb()' - Handle HTTP timeouts.
4605 static int /* O - 1 to continue, 0 to cancel */
4606 timeout_cb(http_t
*http
, /* I - Connection to server (unused) */
4607 void *user_data
) /* I - User data (unused) */
4612 /* Always cancel on timeout */
4618 * 'usage()' - Show program usage.
4624 _cupsLangPuts(stderr
, _("Usage: ipptool [options] URI filename [ ... "
4626 _cupsLangPuts(stderr
, _("Options:"));
4627 _cupsLangPuts(stderr
, _(" -4 Connect using IPv4."));
4628 _cupsLangPuts(stderr
, _(" -6 Connect using IPv6."));
4629 _cupsLangPuts(stderr
, _(" -C Send requests using "
4630 "chunking (default)."));
4631 _cupsLangPuts(stdout
, _(" -E Test with HTTP Upgrade to "
4633 _cupsLangPuts(stderr
, _(" -I Ignore errors."));
4634 _cupsLangPuts(stderr
, _(" -L Send requests using "
4635 "content-length."));
4636 _cupsLangPuts(stderr
, _(" -S Test with SSL "
4638 _cupsLangPuts(stderr
, _(" -T seconds Set the receive/send "
4639 "timeout in seconds."));
4640 _cupsLangPuts(stderr
, _(" -V version Set default IPP "
4642 _cupsLangPuts(stderr
, _(" -X Produce XML plist instead "
4644 _cupsLangPuts(stderr
, _(" -d name=value Set named variable to "
4646 _cupsLangPuts(stderr
, _(" -f filename Set default request "
4648 _cupsLangPuts(stderr
, _(" -i seconds Repeat the last file with "
4649 "the given time interval."));
4650 _cupsLangPuts(stderr
, _(" -n count Repeat the last file the "
4651 "given number of times."));
4652 _cupsLangPuts(stderr
, _(" -q Run silently."));
4653 _cupsLangPuts(stderr
, _(" -t Produce a test report."));
4654 _cupsLangPuts(stderr
, _(" -v Be verbose."));
4661 * 'validate_attr()' - Determine whether an attribute is valid.
4664 static int /* O - 1 if valid, 0 otherwise */
4665 validate_attr(cups_array_t
*errors
, /* I - Errors array */
4666 ipp_attribute_t
*attr
) /* I - Attribute to validate */
4668 int i
; /* Looping var */
4669 char scheme
[64], /* Scheme from URI */
4670 userpass
[256], /* Username/password from URI */
4671 hostname
[256], /* Hostname from URI */
4672 resource
[1024]; /* Resource from URI */
4673 int port
, /* Port number from URI */
4674 uri_status
, /* URI separation status */
4675 valid
= 1; /* Is the attribute valid? */
4676 const char *ptr
; /* Pointer into string */
4677 ipp_attribute_t
*colattr
; /* Collection attribute */
4678 regex_t re
; /* Regular expression */
4679 ipp_uchar_t
*date
; /* Current date value */
4690 * Validate the attribute name.
4693 for (ptr
= attr
->name
; *ptr
; ptr
++)
4694 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' && *ptr
!= '_')
4697 if (*ptr
|| ptr
== attr
->name
)
4702 "\"%s\": Bad attribute name - invalid character "
4703 "(RFC 2911 section 4.1.3).", attr
->name
);
4706 if ((ptr
- attr
->name
) > 255)
4711 "\"%s\": Bad attribute name - bad length "
4712 "(RFC 2911 section 4.1.3).", attr
->name
);
4715 switch (attr
->value_tag
)
4717 case IPP_TAG_INTEGER
:
4720 case IPP_TAG_BOOLEAN
:
4721 for (i
= 0; i
< attr
->num_values
; i
++)
4723 if (attr
->values
[i
].boolean
!= 0 &&
4724 attr
->values
[i
].boolean
!= 1)
4729 "\"%s\": Bad boolen value %d "
4730 "(RFC 2911 section 4.1.11).", attr
->name
,
4731 attr
->values
[i
].boolean
);
4737 for (i
= 0; i
< attr
->num_values
; i
++)
4739 if (attr
->values
[i
].integer
< 1)
4744 "\"%s\": Bad enum value %d - out of range "
4745 "(RFC 2911 section 4.1.4).", attr
->name
,
4746 attr
->values
[i
].integer
);
4751 case IPP_TAG_STRING
:
4752 for (i
= 0; i
< attr
->num_values
; i
++)
4754 if (attr
->values
[i
].unknown
.length
> IPP_MAX_OCTETSTRING
)
4759 "\"%s\": Bad octetString value - bad length %d "
4760 "(RFC 2911 section 4.1.10).", attr
->name
,
4761 attr
->values
[i
].unknown
.length
);
4767 for (i
= 0; i
< attr
->num_values
; i
++)
4769 date
= attr
->values
[i
].date
;
4771 if (date
[2] < 1 || date
[2] > 12)
4776 "\"%s\": Bad dateTime month %u "
4777 "(RFC 2911 section 4.1.14).", attr
->name
, date
[2]);
4780 if (date
[3] < 1 || date
[3] > 31)
4785 "\"%s\": Bad dateTime day %u "
4786 "(RFC 2911 section 4.1.14).", attr
->name
, date
[3]);
4794 "\"%s\": Bad dateTime hours %u "
4795 "(RFC 2911 section 4.1.14).", attr
->name
, date
[4]);
4803 "\"%s\": Bad dateTime minutes %u "
4804 "(RFC 2911 section 4.1.14).", attr
->name
, date
[5]);
4812 "\"%s\": Bad dateTime seconds %u "
4813 "(RFC 2911 section 4.1.14).", attr
->name
, date
[6]);
4821 "\"%s\": Bad dateTime deciseconds %u "
4822 "(RFC 2911 section 4.1.14).", attr
->name
, date
[7]);
4825 if (date
[8] != '-' && date
[8] != '+')
4830 "\"%s\": Bad dateTime UTC sign '%c' "
4831 "(RFC 2911 section 4.1.14).", attr
->name
, date
[8]);
4839 "\"%s\": Bad dateTime UTC hours %u "
4840 "(RFC 2911 section 4.1.14).", attr
->name
, date
[9]);
4848 "\"%s\": Bad dateTime UTC minutes %u "
4849 "(RFC 2911 section 4.1.14).", attr
->name
, date
[10]);
4854 case IPP_TAG_RESOLUTION
:
4855 for (i
= 0; i
< attr
->num_values
; i
++)
4857 if (attr
->values
[i
].resolution
.xres
<= 0)
4862 "\"%s\": Bad resolution value %dx%d%s - cross "
4863 "feed resolution must be positive "
4864 "(RFC 2911 section 4.1.15).", attr
->name
,
4865 attr
->values
[i
].resolution
.xres
,
4866 attr
->values
[i
].resolution
.yres
,
4867 attr
->values
[i
].resolution
.units
==
4868 IPP_RES_PER_INCH
? "dpi" :
4869 attr
->values
[i
].resolution
.units
==
4870 IPP_RES_PER_CM
? "dpcm" : "unknown");
4873 if (attr
->values
[i
].resolution
.yres
<= 0)
4878 "\"%s\": Bad resolution value %dx%d%s - feed "
4879 "resolution must be positive "
4880 "(RFC 2911 section 4.1.15).", attr
->name
,
4881 attr
->values
[i
].resolution
.xres
,
4882 attr
->values
[i
].resolution
.yres
,
4883 attr
->values
[i
].resolution
.units
==
4884 IPP_RES_PER_INCH
? "dpi" :
4885 attr
->values
[i
].resolution
.units
==
4886 IPP_RES_PER_CM
? "dpcm" : "unknown");
4889 if (attr
->values
[i
].resolution
.units
!= IPP_RES_PER_INCH
&&
4890 attr
->values
[i
].resolution
.units
!= IPP_RES_PER_CM
)
4895 "\"%s\": Bad resolution value %dx%d%s - bad "
4896 "units value (RFC 2911 section 4.1.15).",
4897 attr
->name
, attr
->values
[i
].resolution
.xres
,
4898 attr
->values
[i
].resolution
.yres
,
4899 attr
->values
[i
].resolution
.units
==
4900 IPP_RES_PER_INCH
? "dpi" :
4901 attr
->values
[i
].resolution
.units
==
4902 IPP_RES_PER_CM
? "dpcm" : "unknown");
4907 case IPP_TAG_RANGE
:
4908 for (i
= 0; i
< attr
->num_values
; i
++)
4910 if (attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
4915 "\"%s\": Bad rangeOfInteger value %d-%d - lower "
4916 "greater than upper (RFC 2911 section 4.1.13).",
4917 attr
->name
, attr
->values
[i
].range
.lower
,
4918 attr
->values
[i
].range
.upper
);
4923 case IPP_TAG_BEGIN_COLLECTION
:
4924 for (i
= 0; i
< attr
->num_values
; i
++)
4926 for (colattr
= attr
->values
[i
].collection
->attrs
;
4928 colattr
= colattr
->next
)
4930 if (!validate_attr(NULL
, colattr
))
4937 if (colattr
&& errors
)
4939 add_stringf(errors
, "\"%s\": Bad collection value.", attr
->name
);
4943 validate_attr(errors
, colattr
);
4944 colattr
= colattr
->next
;
4951 case IPP_TAG_TEXTLANG
:
4952 for (i
= 0; i
< attr
->num_values
; i
++)
4954 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4956 if ((*ptr
& 0xe0) == 0xc0)
4959 if ((*ptr
& 0xc0) != 0x80)
4962 else if ((*ptr
& 0xf0) == 0xe0)
4965 if ((*ptr
& 0xc0) != 0x80)
4968 if ((*ptr
& 0xc0) != 0x80)
4971 else if ((*ptr
& 0xf8) == 0xf0)
4974 if ((*ptr
& 0xc0) != 0x80)
4977 if ((*ptr
& 0xc0) != 0x80)
4980 if ((*ptr
& 0xc0) != 0x80)
4983 else if (*ptr
& 0x80)
4992 "\"%s\": Bad text value \"%s\" - bad UTF-8 "
4993 "sequence (RFC 2911 section 4.1.1).", attr
->name
,
4994 attr
->values
[i
].string
.text
);
4997 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_TEXT
- 1))
5002 "\"%s\": Bad text value \"%s\" - bad length %d "
5003 "(RFC 2911 section 4.1.1).", attr
->name
,
5004 attr
->values
[i
].string
.text
,
5005 (int)strlen(attr
->values
[i
].string
.text
));
5011 case IPP_TAG_NAMELANG
:
5012 for (i
= 0; i
< attr
->num_values
; i
++)
5014 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5016 if ((*ptr
& 0xe0) == 0xc0)
5019 if ((*ptr
& 0xc0) != 0x80)
5022 else if ((*ptr
& 0xf0) == 0xe0)
5025 if ((*ptr
& 0xc0) != 0x80)
5028 if ((*ptr
& 0xc0) != 0x80)
5031 else if ((*ptr
& 0xf8) == 0xf0)
5034 if ((*ptr
& 0xc0) != 0x80)
5037 if ((*ptr
& 0xc0) != 0x80)
5040 if ((*ptr
& 0xc0) != 0x80)
5043 else if (*ptr
& 0x80)
5052 "\"%s\": Bad name value \"%s\" - bad UTF-8 "
5053 "sequence (RFC 2911 section 4.1.2).", attr
->name
,
5054 attr
->values
[i
].string
.text
);
5057 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_NAME
- 1))
5062 "\"%s\": Bad name value \"%s\" - bad length %d "
5063 "(RFC 2911 section 4.1.2).", attr
->name
,
5064 attr
->values
[i
].string
.text
,
5065 (int)strlen(attr
->values
[i
].string
.text
));
5070 case IPP_TAG_KEYWORD
:
5071 for (i
= 0; i
< attr
->num_values
; i
++)
5073 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5074 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' &&
5078 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5083 "\"%s\": Bad keyword value \"%s\" - invalid "
5084 "character (RFC 2911 section 4.1.3).",
5085 attr
->name
, attr
->values
[i
].string
.text
);
5088 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_KEYWORD
- 1))
5093 "\"%s\": Bad keyword value \"%s\" - bad "
5094 "length %d (RFC 2911 section 4.1.3).",
5095 attr
->name
, attr
->values
[i
].string
.text
,
5096 (int)strlen(attr
->values
[i
].string
.text
));
5102 for (i
= 0; i
< attr
->num_values
; i
++)
5104 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
,
5105 attr
->values
[i
].string
.text
,
5106 scheme
, sizeof(scheme
),
5107 userpass
, sizeof(userpass
),
5108 hostname
, sizeof(hostname
),
5109 &port
, resource
, sizeof(resource
));
5111 if (uri_status
< HTTP_URI_OK
)
5116 "\"%s\": Bad URI value \"%s\" - %s "
5117 "(RFC 2911 section 4.1.5).", attr
->name
,
5118 attr
->values
[i
].string
.text
,
5119 URIStatusStrings
[uri_status
-
5120 HTTP_URI_OVERFLOW
]);
5123 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_URI
- 1))
5128 "\"%s\": Bad URI value \"%s\" - bad length %d "
5129 "(RFC 2911 section 4.1.5).", attr
->name
,
5130 attr
->values
[i
].string
.text
,
5131 (int)strlen(attr
->values
[i
].string
.text
));
5136 case IPP_TAG_URISCHEME
:
5137 for (i
= 0; i
< attr
->num_values
; i
++)
5139 ptr
= attr
->values
[i
].string
.text
;
5140 if (islower(*ptr
& 255))
5142 for (ptr
++; *ptr
; ptr
++)
5143 if (!islower(*ptr
& 255) && !isdigit(*ptr
& 255) &&
5144 *ptr
!= '+' && *ptr
!= '-' && *ptr
!= '.')
5148 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5153 "\"%s\": Bad uriScheme value \"%s\" - bad "
5154 "characters (RFC 2911 section 4.1.6).",
5155 attr
->name
, attr
->values
[i
].string
.text
);
5158 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_URISCHEME
- 1))
5163 "\"%s\": Bad uriScheme value \"%s\" - bad "
5164 "length %d (RFC 2911 section 4.1.6).",
5165 attr
->name
, attr
->values
[i
].string
.text
,
5166 (int)strlen(attr
->values
[i
].string
.text
));
5171 case IPP_TAG_CHARSET
:
5172 for (i
= 0; i
< attr
->num_values
; i
++)
5174 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5175 if (!isprint(*ptr
& 255) || isupper(*ptr
& 255) ||
5176 isspace(*ptr
& 255))
5179 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5184 "\"%s\": Bad charset value \"%s\" - bad "
5185 "characters (RFC 2911 section 4.1.7).",
5186 attr
->name
, attr
->values
[i
].string
.text
);
5189 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_CHARSET
- 1))
5194 "\"%s\": Bad charset value \"%s\" - bad "
5195 "length %d (RFC 2911 section 4.1.7).",
5196 attr
->name
, attr
->values
[i
].string
.text
,
5197 (int)strlen(attr
->values
[i
].string
.text
));
5202 case IPP_TAG_LANGUAGE
:
5204 * The following regular expression is derived from the ABNF for
5205 * language tags in RFC 4646. All I can say is that this is the
5206 * easiest way to check the values...
5209 if ((i
= regcomp(&re
,
5211 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
5213 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
5214 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
5215 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
5216 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
5217 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
5219 "x(-[a-z0-9]{1,8})+" /* privateuse */
5221 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
5223 REG_NOSUB
| REG_EXTENDED
)) != 0)
5225 char temp
[256]; /* Temporary error string */
5227 regerror(i
, &re
, temp
, sizeof(temp
));
5228 print_fatal_error("Unable to compile naturalLanguage regular "
5229 "expression: %s.", temp
);
5233 for (i
= 0; i
< attr
->num_values
; i
++)
5235 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5240 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5241 "characters (RFC 2911 section 4.1.8).",
5242 attr
->name
, attr
->values
[i
].string
.text
);
5245 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_LANGUAGE
- 1))
5250 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5251 "length %d (RFC 2911 section 4.1.8).",
5252 attr
->name
, attr
->values
[i
].string
.text
,
5253 (int)strlen(attr
->values
[i
].string
.text
));
5260 case IPP_TAG_MIMETYPE
:
5262 * The following regular expression is derived from the ABNF for
5263 * language tags in RFC 2045 and 4288. All I can say is that this is
5264 * the easiest way to check the values...
5267 if ((i
= regcomp(&re
,
5269 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
5271 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
5272 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
5273 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
5276 REG_NOSUB
| REG_EXTENDED
)) != 0)
5278 char temp
[256]; /* Temporary error string */
5280 regerror(i
, &re
, temp
, sizeof(temp
));
5281 print_fatal_error("Unable to compile mimeMediaType regular "
5282 "expression: %s.", temp
);
5286 for (i
= 0; i
< attr
->num_values
; i
++)
5288 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5293 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5294 "characters (RFC 2911 section 4.1.9).",
5295 attr
->name
, attr
->values
[i
].string
.text
);
5298 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_MIMETYPE
- 1))
5303 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5304 "length %d (RFC 2911 section 4.1.9).",
5305 attr
->name
, attr
->values
[i
].string
.text
,
5306 (int)strlen(attr
->values
[i
].string
.text
));
5322 * 'with_value()' - Test a WITH-VALUE predicate.
5325 static int /* O - 1 on match, 0 on non-match */
5326 with_value(cups_array_t
*errors
, /* I - Errors array */
5327 char *value
, /* I - Value string */
5328 int flags
, /* I - Flags for match */
5329 ipp_attribute_t
*attr
, /* I - Attribute to compare */
5330 char *matchbuf
, /* I - Buffer to hold matching value */
5331 size_t matchlen
) /* I - Length of match buffer */
5333 int i
, /* Looping var */
5335 char temp
[256], /* Temporary value string */
5336 *valptr
; /* Pointer into value */
5340 match
= (flags
& _CUPS_WITH_ALL
) ? 1 : 0;
5343 * NULL matches everything.
5346 if (!value
|| !*value
)
5350 * Compare the value string to the attribute value.
5353 switch (attr
->value_tag
)
5355 case IPP_TAG_INTEGER
:
5357 for (i
= 0; i
< attr
->num_values
; i
++)
5359 char op
, /* Comparison operator */
5360 *nextptr
; /* Next pointer */
5361 int intvalue
, /* Integer value */
5362 valmatch
= 0; /* Does the current value match? */
5366 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5367 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5368 *valptr
== '=' || *valptr
== '>')
5371 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5373 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5381 intvalue
= strtol(valptr
, &nextptr
, 0);
5382 if (nextptr
== valptr
)
5386 if ((op
== '=' && attr
->values
[i
].integer
== intvalue
) ||
5387 (op
== '<' && attr
->values
[i
].integer
< intvalue
) ||
5388 (op
== '>' && attr
->values
[i
].integer
> intvalue
))
5391 snprintf(matchbuf
, matchlen
, "%d",
5392 attr
->values
[i
].integer
);
5399 if (flags
& _CUPS_WITH_ALL
)
5414 if (!match
&& errors
)
5416 for (i
= 0; i
< attr
->num_values
; i
++)
5417 add_stringf(errors
, "GOT: %s=%d", attr
->name
,
5418 attr
->values
[i
].integer
);
5422 case IPP_TAG_RANGE
:
5423 for (i
= 0; i
< attr
->num_values
; i
++)
5425 char op
, /* Comparison operator */
5426 *nextptr
; /* Next pointer */
5427 int intvalue
, /* Integer value */
5428 valmatch
= 0; /* Does the current value match? */
5432 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5433 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5434 *valptr
== '=' || *valptr
== '>')
5437 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5439 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5447 intvalue
= strtol(valptr
, &nextptr
, 0);
5448 if (nextptr
== valptr
)
5452 if ((op
== '=' && (attr
->values
[i
].range
.lower
== intvalue
||
5453 attr
->values
[i
].range
.upper
== intvalue
)) ||
5454 (op
== '<' && attr
->values
[i
].range
.upper
< intvalue
) ||
5455 (op
== '>' && attr
->values
[i
].range
.upper
> intvalue
))
5458 snprintf(matchbuf
, matchlen
, "%d-%d",
5459 attr
->values
[0].range
.lower
,
5460 attr
->values
[0].range
.upper
);
5467 if (flags
& _CUPS_WITH_ALL
)
5482 if (!match
&& errors
)
5484 for (i
= 0; i
< attr
->num_values
; i
++)
5485 add_stringf(errors
, "GOT: %s=%d-%d", attr
->name
,
5486 attr
->values
[i
].range
.lower
,
5487 attr
->values
[i
].range
.upper
);
5491 case IPP_TAG_BOOLEAN
:
5492 for (i
= 0; i
< attr
->num_values
; i
++)
5494 if (!strcmp(value
, "true") == attr
->values
[i
].boolean
)
5497 strlcpy(matchbuf
, value
, matchlen
);
5499 if (!(flags
& _CUPS_WITH_ALL
))
5505 else if (flags
& _CUPS_WITH_ALL
)
5512 if (!match
&& errors
)
5514 for (i
= 0; i
< attr
->num_values
; i
++)
5515 add_stringf(errors
, "GOT: %s=%s", attr
->name
,
5516 attr
->values
[i
].boolean
? "true" : "false");
5520 case IPP_TAG_RESOLUTION
:
5521 for (i
= 0; i
< attr
->num_values
; i
++)
5523 if (attr
->values
[i
].resolution
.xres
==
5524 attr
->values
[i
].resolution
.yres
)
5525 snprintf(temp
, sizeof(temp
), "%d%s",
5526 attr
->values
[i
].resolution
.xres
,
5527 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5530 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5531 attr
->values
[i
].resolution
.xres
,
5532 attr
->values
[i
].resolution
.yres
,
5533 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5536 if (!strcmp(value
, temp
))
5539 strlcpy(matchbuf
, value
, matchlen
);
5541 if (!(flags
& _CUPS_WITH_ALL
))
5547 else if (flags
& _CUPS_WITH_ALL
)
5554 if (!match
&& errors
)
5556 for (i
= 0; i
< attr
->num_values
; i
++)
5558 if (attr
->values
[i
].resolution
.xres
==
5559 attr
->values
[i
].resolution
.yres
)
5560 snprintf(temp
, sizeof(temp
), "%d%s",
5561 attr
->values
[i
].resolution
.xres
,
5562 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5565 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5566 attr
->values
[i
].resolution
.xres
,
5567 attr
->values
[i
].resolution
.yres
,
5568 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5571 if (strcmp(value
, temp
))
5572 add_stringf(errors
, "GOT: %s=%s", attr
->name
, temp
);
5577 case IPP_TAG_NOVALUE
:
5578 case IPP_TAG_UNKNOWN
:
5581 case IPP_TAG_CHARSET
:
5582 case IPP_TAG_KEYWORD
:
5583 case IPP_TAG_LANGUAGE
:
5584 case IPP_TAG_MIMETYPE
:
5586 case IPP_TAG_NAMELANG
:
5588 case IPP_TAG_TEXTLANG
:
5590 case IPP_TAG_URISCHEME
:
5591 if (flags
& _CUPS_WITH_REGEX
)
5594 * Value is an extended, case-sensitive POSIX regular expression...
5597 regex_t re
; /* Regular expression */
5599 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
5601 regerror(i
, &re
, temp
, sizeof(temp
));
5603 print_fatal_error("Unable to compile WITH-VALUE regular expression "
5604 "\"%s\" - %s", value
, temp
);
5609 * See if ALL of the values match the given regular expression.
5612 for (i
= 0; i
< attr
->num_values
; i
++)
5614 if (!regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5617 strlcpy(matchbuf
, attr
->values
[i
].string
.text
, matchlen
);
5619 if (!(flags
& _CUPS_WITH_ALL
))
5625 else if (flags
& _CUPS_WITH_ALL
)
5637 * Value is a literal string, see if the value(s) match...
5640 for (i
= 0; i
< attr
->num_values
; i
++)
5642 if (!strcmp(value
, attr
->values
[i
].string
.text
))
5645 strlcpy(matchbuf
, attr
->values
[i
].string
.text
, matchlen
);
5647 if (!(flags
& _CUPS_WITH_ALL
))
5653 else if (flags
& _CUPS_WITH_ALL
)
5661 if (!match
&& errors
)
5663 for (i
= 0; i
< attr
->num_values
; i
++)
5664 add_stringf(errors
, "GOT: %s=\"%s\"", attr
->name
,
5665 attr
->values
[i
].string
.text
);