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 Verbosity
= 0, /* Show all attributes? */
160 Version
= 11, /* Default IPP version */
161 XMLHeader
= 0, /* 1 if header is written */
162 TestCount
= 0, /* Number of tests run */
163 PassCount
= 0, /* Number of passing tests */
164 FailCount
= 0, /* Number of failing tests */
165 SkipCount
= 0; /* Number of skipped tests */
166 char *Password
= NULL
; /* Password from URI */
167 const char * const URIStatusStrings
[] = /* URI status strings */
170 "Bad arguments to function",
171 "Bad resource in URI",
172 "Bad port number in URI",
173 "Bad hostname/address in URI",
174 "Bad username in URI",
178 "Missing scheme in URI",
179 "Unknown scheme in URI",
180 "Missing resource in URI"
188 static void add_stringf(cups_array_t
*a
, const char *s
, ...)
189 __attribute__ ((__format__ (__printf__
, 2, 3)));
190 static int compare_vars(_cups_var_t
*a
, _cups_var_t
*b
);
191 static int do_tests(_cups_vars_t
*vars
, const char *testfile
);
192 static void expand_variables(_cups_vars_t
*vars
, char *dst
, const char *src
,
193 size_t dstsize
) __attribute__((nonnull(1,2,3)));
194 static int expect_matches(_cups_expect_t
*expect
, ipp_tag_t value_tag
);
195 static ipp_t
*get_collection(_cups_vars_t
*vars
, FILE *fp
, int *linenum
);
196 static char *get_filename(const char *testfile
, char *dst
, const char *src
,
198 static char *get_token(FILE *fp
, char *buf
, int buflen
,
200 static char *get_variable(_cups_vars_t
*vars
, const char *name
);
201 static char *iso_date(ipp_uchar_t
*date
);
202 static const char *password_cb(const char *prompt
);
203 static void print_attr(ipp_attribute_t
*attr
, ipp_tag_t
*group
);
204 static void print_col(ipp_t
*col
);
205 static void print_csv(ipp_attribute_t
*attr
, int num_displayed
,
206 char **displayed
, size_t *widths
);
207 static void print_fatal_error(const char *s
, ...)
208 __attribute__ ((__format__ (__printf__
, 1, 2)));
209 static void print_line(ipp_attribute_t
*attr
, int num_displayed
,
210 char **displayed
, size_t *widths
);
211 static void print_xml_header(void);
212 static void print_xml_string(const char *element
, const char *s
);
213 static void print_xml_trailer(int success
, const char *message
);
214 static void set_variable(_cups_vars_t
*vars
, const char *name
,
217 static void sigterm_handler(int sig
);
219 static int timeout_cb(http_t
*http
, void *user_data
);
220 static void usage(void) __attribute__((noreturn
));
221 static int validate_attr(cups_array_t
*errors
, ipp_attribute_t
*attr
);
222 static int with_value(cups_array_t
*errors
, char *value
, int flags
,
223 ipp_attribute_t
*attr
, char *matchbuf
,
228 * 'main()' - Parse options and do tests.
231 int /* O - Exit status */
232 main(int argc
, /* I - Number of command-line args */
233 char *argv
[]) /* I - Command-line arguments */
235 int i
; /* Looping var */
236 int status
; /* Status of tests... */
237 char *opt
, /* Current option */
238 name
[1024], /* Name/value buffer */
239 *value
, /* Pointer to value */
240 filename
[1024], /* Real filename */
241 testname
[1024], /* Real test filename */
242 uri
[1024]; /* Copy of printer URI */
243 const char *ext
, /* Extension on filename */
244 *testfile
; /* Test file to use */
245 int interval
, /* Test interval in microseconds */
246 repeat
; /* Repeat count */
247 _cups_vars_t vars
; /* Variables */
248 http_uri_status_t uri_status
; /* URI separation status */
249 _cups_globals_t
*cg
= _cupsGlobals();
255 * Catch SIGINT and SIGTERM...
258 signal(SIGINT
, sigterm_handler
);
259 signal(SIGTERM
, sigterm_handler
);
263 * Initialize the locale and variables...
266 _cupsSetLocale(argv
);
268 memset(&vars
, 0, sizeof(vars
));
269 vars
.family
= AF_UNSPEC
;
270 vars
.vars
= cupsArrayNew((cups_array_func_t
)compare_vars
, NULL
);
275 * ipptool URI testfile
283 for (i
= 1; i
< argc
; i
++)
285 if (argv
[i
][0] == '-')
287 for (opt
= argv
[i
] + 1; *opt
; opt
++)
291 case '4' : /* Connect using IPv4 only */
292 vars
.family
= AF_INET
;
296 case '6' : /* Connect using IPv6 only */
297 vars
.family
= AF_INET6
;
299 #endif /* AF_INET6 */
301 case 'C' : /* Enable HTTP chunking */
302 Transfer
= _CUPS_TRANSFER_CHUNKED
;
305 case 'E' : /* Encrypt with TLS */
307 vars
.encryption
= HTTP_ENCRYPT_REQUIRED
;
309 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
311 #endif /* HAVE_SSL */
314 case 'I' : /* Ignore errors */
318 case 'L' : /* Disable HTTP chunking */
319 Transfer
= _CUPS_TRANSFER_LENGTH
;
322 case 'S' : /* Encrypt with SSL */
324 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
326 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
328 #endif /* HAVE_SSL */
331 case 'T' : /* Set timeout */
336 _cupsLangPuts(stderr
,
337 _("ipptool: Missing timeout for \"-T\"."));
341 vars
.timeout
= _cupsStrScand(argv
[i
], NULL
, localeconv());
344 case 'V' : /* Set IPP version */
349 _cupsLangPuts(stderr
,
350 _("ipptool: Missing version for \"-V\"."));
354 if (!strcmp(argv
[i
], "1.0"))
356 else if (!strcmp(argv
[i
], "1.1"))
358 else if (!strcmp(argv
[i
], "2.0"))
360 else if (!strcmp(argv
[i
], "2.1"))
362 else if (!strcmp(argv
[i
], "2.2"))
366 _cupsLangPrintf(stderr
,
367 _("ipptool: Bad version %s for \"-V\"."),
373 case 'X' : /* Produce XML output */
374 Output
= _CUPS_OUTPUT_PLIST
;
376 if (interval
|| repeat
)
378 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are "
379 "incompatible with -X\"."));
384 case 'c' : /* CSV output */
385 Output
= _CUPS_OUTPUT_CSV
;
388 case 'd' : /* Define a variable */
393 _cupsLangPuts(stderr
,
394 _("ipptool: Missing name=value for \"-d\"."));
398 strlcpy(name
, argv
[i
], sizeof(name
));
399 if ((value
= strchr(name
, '=')) != NULL
)
402 value
= name
+ strlen(name
);
404 set_variable(&vars
, name
, value
);
407 case 'f' : /* Set the default test filename */
412 _cupsLangPuts(stderr
,
413 _("ipptool: Missing filename for \"-f\"."));
420 vars
.filename
= NULL
;
423 if (access(argv
[i
], 0))
429 snprintf(filename
, sizeof(filename
), "%s.gz", argv
[i
]);
430 if (access(filename
, 0) && filename
[0] != '/'
432 && (!isalpha(filename
[0] & 255) || filename
[1] != ':')
436 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s",
437 cg
->cups_datadir
, argv
[i
]);
438 if (access(filename
, 0))
440 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s.gz",
441 cg
->cups_datadir
, argv
[i
]);
442 if (access(filename
, 0))
443 vars
.filename
= strdup(argv
[i
]);
445 vars
.filename
= strdup(filename
);
448 vars
.filename
= strdup(filename
);
451 vars
.filename
= strdup(filename
);
454 vars
.filename
= strdup(argv
[i
]);
456 if ((ext
= strrchr(vars
.filename
, '.')) != NULL
)
459 * Guess the MIME media type based on the extension...
462 if (!_cups_strcasecmp(ext
, ".gif"))
463 set_variable(&vars
, "filetype", "image/gif");
464 else if (!_cups_strcasecmp(ext
, ".htm") ||
465 !_cups_strcasecmp(ext
, ".htm.gz") ||
466 !_cups_strcasecmp(ext
, ".html") ||
467 !_cups_strcasecmp(ext
, ".html.gz"))
468 set_variable(&vars
, "filetype", "text/html");
469 else if (!_cups_strcasecmp(ext
, ".jpg"))
470 set_variable(&vars
, "filetype", "image/jpeg");
471 else if (!_cups_strcasecmp(ext
, ".pcl") ||
472 !_cups_strcasecmp(ext
, ".pcl.gz"))
473 set_variable(&vars
, "filetype", "application/vnd.hp-PCL");
474 else if (!_cups_strcasecmp(ext
, ".pdf"))
475 set_variable(&vars
, "filetype", "application/pdf");
476 else if (!_cups_strcasecmp(ext
, ".png"))
477 set_variable(&vars
, "filetype", "image/png");
478 else if (!_cups_strcasecmp(ext
, ".ps") ||
479 !_cups_strcasecmp(ext
, ".ps.gz"))
480 set_variable(&vars
, "filetype", "application/postscript");
481 else if (!_cups_strcasecmp(ext
, ".pwg") ||
482 !_cups_strcasecmp(ext
, ".pwg.gz") ||
483 !_cups_strcasecmp(ext
, ".ras") ||
484 !_cups_strcasecmp(ext
, ".ras.gz"))
485 set_variable(&vars
, "filetype", "image/pwg-raster");
486 else if (!_cups_strcasecmp(ext
, ".txt") ||
487 !_cups_strcasecmp(ext
, ".txt.gz"))
488 set_variable(&vars
, "filetype", "text/plain");
489 else if (!_cups_strcasecmp(ext
, ".xps"))
490 set_variable(&vars
, "filetype", "application/openxps");
492 set_variable(&vars
, "filetype", "application/octet-stream");
497 * Use the "auto-type" MIME media type...
500 set_variable(&vars
, "filetype", "application/octet-stream");
504 case 'i' : /* Test every N seconds */
509 _cupsLangPuts(stderr
,
510 _("ipptool: Missing seconds for \"-i\"."));
515 interval
= (int)(_cupsStrScand(argv
[i
], NULL
, localeconv()) *
519 _cupsLangPuts(stderr
,
520 _("ipptool: Invalid seconds for \"-i\"."));
525 if (Output
== _CUPS_OUTPUT_PLIST
&& interval
)
527 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are "
528 "incompatible with -X\"."));
533 case 'l' : /* List as a table */
534 Output
= _CUPS_OUTPUT_LIST
;
537 case 'n' : /* Repeat count */
542 _cupsLangPuts(stderr
,
543 _("ipptool: Missing count for \"-n\"."));
547 repeat
= atoi(argv
[i
]);
549 if (Output
== _CUPS_OUTPUT_PLIST
&& repeat
)
551 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are "
552 "incompatible with -X\"."));
557 case 'q' : /* Be quiet */
558 Output
= _CUPS_OUTPUT_QUIET
;
561 case 't' : /* CUPS test output */
562 Output
= _CUPS_OUTPUT_TEST
;
565 case 'v' : /* Be verbose */
570 _cupsLangPrintf(stderr
, _("ipptool: Unknown option \"-%c\"."),
577 else if (!strncmp(argv
[i
], "ipp://", 6) || !strncmp(argv
[i
], "http://", 7)
579 || !strncmp(argv
[i
], "ipps://", 7)
580 || !strncmp(argv
[i
], "https://", 8)
581 #endif /* HAVE_SSL */
590 _cupsLangPuts(stderr
, _("ipptool: May only specify a single URI."));
595 if (!strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8))
596 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
597 #endif /* HAVE_SSL */
600 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, vars
.uri
,
601 vars
.scheme
, sizeof(vars
.scheme
),
602 vars
.userpass
, sizeof(vars
.userpass
),
603 vars
.hostname
, sizeof(vars
.hostname
),
605 vars
.resource
, sizeof(vars
.resource
));
607 if (uri_status
!= HTTP_URI_OK
)
609 _cupsLangPrintf(stderr
, _("ipptool: Bad URI - %s."),
610 URIStatusStrings
[uri_status
- HTTP_URI_OVERFLOW
]);
614 if (vars
.userpass
[0])
616 if ((Password
= strchr(vars
.userpass
, ':')) != NULL
)
619 cupsSetUser(vars
.userpass
);
620 cupsSetPasswordCB(password_cb
);
621 set_variable(&vars
, "uriuser", vars
.userpass
);
624 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), vars
.scheme
, NULL
,
625 vars
.hostname
, vars
.port
, vars
.resource
);
636 _cupsLangPuts(stderr
, _("ipptool: URI required before test file."));
640 if (access(argv
[i
], 0) && argv
[i
][0] != '/'
642 && (!isalpha(argv
[i
][0] & 255) || argv
[i
][1] != ':')
646 snprintf(testname
, sizeof(testname
), "%s/ipptool/%s", cg
->cups_datadir
,
648 if (access(testname
, 0))
656 if (!do_tests(&vars
, testfile
))
661 if (!vars
.uri
|| !testfile
)
665 * Loop if the interval is set...
668 if (Output
== _CUPS_OUTPUT_PLIST
)
669 print_xml_trailer(!status
, NULL
);
670 else if (interval
> 0 && repeat
> 0)
675 do_tests(&vars
, testfile
);
679 else if (interval
> 0)
684 do_tests(&vars
, testfile
);
687 else if (Output
== _CUPS_OUTPUT_TEST
&& TestCount
> 1)
690 * Show a summary report if there were multiple tests...
693 printf("\nSummary: %d tests, %d passed, %d failed, %d skipped\n"
694 "Score: %d%%\n", TestCount
, PassCount
, FailCount
, SkipCount
,
695 100 * (PassCount
+ SkipCount
) / TestCount
);
707 * 'add_stringf()' - Add a formatted string to an array.
711 add_stringf(cups_array_t
*a
, /* I - Array */
712 const char *s
, /* I - Printf-style format string */
713 ...) /* I - Additional args as needed */
715 char buffer
[10240]; /* Format buffer */
716 va_list ap
; /* Argument pointer */
720 * Don't bother is the array is NULL...
727 * Format the message...
731 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
735 * Add it to the array...
738 cupsArrayAdd(a
, buffer
);
743 * 'compare_vars()' - Compare two variables.
746 static int /* O - Result of comparison */
747 compare_vars(_cups_var_t
*a
, /* I - First variable */
748 _cups_var_t
*b
) /* I - Second variable */
750 return (_cups_strcasecmp(a
->name
, b
->name
));
755 * 'do_tests()' - Do tests as specified in the test file.
758 static int /* 1 = success, 0 = failure */
759 do_tests(_cups_vars_t
*vars
, /* I - Variables */
760 const char *testfile
) /* I - Test file to use */
762 int i
, /* Looping var */
763 linenum
, /* Current line number */
764 pass
, /* Did we pass the test? */
765 prev_pass
= 1, /* Did we pass the previous test? */
766 request_id
, /* Current request ID */
767 show_header
= 1, /* Show the test header? */
768 ignore_errors
, /* Ignore test failures? */
769 skip_previous
= 0, /* Skip on previous test failure? */
770 repeat_count
, /* Repeat count */
771 repeat_interval
, /* Repeat interval */
772 repeat_prev
, /* Previous repeat interval */
773 repeat_test
; /* Repeat a test? */
774 http_t
*http
= NULL
; /* HTTP connection to server */
775 FILE *fp
= NULL
; /* Test file */
776 char resource
[512], /* Resource for request */
777 token
[1024], /* Token from file */
778 *tokenptr
, /* Pointer into token */
779 temp
[1024], /* Temporary string */
780 buffer
[8192], /* Copy buffer */
781 compression
[16]; /* COMPRESSION value */
782 ipp_t
*request
= NULL
, /* IPP request */
783 *response
= NULL
; /* IPP response */
784 size_t length
; /* Length of IPP request */
785 http_status_t status
; /* HTTP status */
786 cups_file_t
*reqfile
; /* File to send */
787 ssize_t bytes
; /* Bytes read/written */
788 char attr
[128]; /* Attribute name */
789 ipp_op_t op
; /* Operation */
790 ipp_tag_t group
; /* Current group */
791 ipp_tag_t value
; /* Current value type */
792 ipp_attribute_t
*attrptr
, /* Attribute pointer */
793 *found
, /* Found attribute */
794 *lastcol
= NULL
; /* Last collection attribute */
795 char name
[1024]; /* Name of test */
796 char filename
[1024]; /* Filename */
797 _cups_transfer_t transfer
; /* To chunk or not to chunk */
798 int version
, /* IPP version number to use */
799 skip_test
; /* Skip this test? */
800 int num_statuses
= 0; /* Number of valid status codes */
801 _cups_status_t statuses
[100], /* Valid status codes */
802 *last_status
; /* Last STATUS (for predicates) */
803 int num_expects
= 0; /* Number of expected attributes */
804 _cups_expect_t expects
[200], /* Expected attributes */
805 *expect
, /* Current expected attribute */
806 *last_expect
; /* Last EXPECT (for predicates) */
807 int num_displayed
= 0; /* Number of displayed attributes */
808 char *displayed
[200]; /* Displayed attributes */
809 size_t widths
[200]; /* Width of columns */
810 cups_array_t
*a
, /* Duplicate attribute array */
811 *errors
= NULL
; /* Errors array */
812 const char *error
; /* Current error */
816 * Open the test file...
819 if ((fp
= fopen(testfile
, "r")) == NULL
)
821 print_fatal_error("Unable to open test file %s - %s", testfile
,
828 * Connect to the server...
831 if ((http
= httpConnect2(vars
->hostname
, vars
->port
, NULL
, vars
->family
,
832 vars
->encryption
, 1, 30000, NULL
)) == NULL
)
834 print_fatal_error("Unable to connect to %s on port %d - %s", vars
->hostname
,
835 vars
->port
, cupsLastErrorString());
841 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
,
842 "deflate, gzip, identity");
844 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
, "identity");
845 #endif /* HAVE_LIBZ */
847 if (vars
->timeout
> 0.0)
848 httpSetTimeout(http
, vars
->timeout
, timeout_cb
, NULL
);
854 CUPS_SRAND(time(NULL
));
856 errors
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
,
857 (cups_afree_func_t
)free
);
860 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
862 while (!Cancel
&& get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
865 * Expect an open brace...
868 if (!strcmp(token
, "DEFINE"))
874 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
875 get_token(fp
, temp
, sizeof(temp
), &linenum
))
877 expand_variables(vars
, token
, temp
, sizeof(token
));
878 set_variable(vars
, attr
, token
);
882 print_fatal_error("Missing DEFINE name and/or value on line %d.",
890 else if (!strcmp(token
, "DEFINE-DEFAULT"))
893 * DEFINE-DEFAULT name value
896 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
897 get_token(fp
, temp
, sizeof(temp
), &linenum
))
899 expand_variables(vars
, token
, temp
, sizeof(token
));
900 if (!get_variable(vars
, attr
))
901 set_variable(vars
, attr
, token
);
905 print_fatal_error("Missing DEFINE-DEFAULT name and/or value on line "
913 else if (!strcmp(token
, "IGNORE-ERRORS"))
920 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
921 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
923 IgnoreErrors
= !_cups_strcasecmp(temp
, "yes");
927 print_fatal_error("Missing IGNORE-ERRORS value on line %d.", linenum
);
934 else if (!strcmp(token
, "INCLUDE"))
941 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
944 * Map the filename to and then run the tests...
947 if (!do_tests(vars
, get_filename(testfile
, filename
, temp
,
958 print_fatal_error("Missing INCLUDE filename on line %d.", linenum
);
966 else if (!strcmp(token
, "INCLUDE-IF-DEFINED"))
969 * INCLUDE-IF-DEFINED name "filename"
970 * INCLUDE-IF-DEFINED name <filename>
973 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
974 get_token(fp
, temp
, sizeof(temp
), &linenum
))
977 * Map the filename to and then run the tests...
980 if (get_variable(vars
, attr
) &&
981 !do_tests(vars
, get_filename(testfile
, filename
, temp
,
992 print_fatal_error("Missing INCLUDE-IF-DEFINED name or filename on line "
1001 else if (!strcmp(token
, "INCLUDE-IF-NOT-DEFINED"))
1004 * INCLUDE-IF-NOT-DEFINED name "filename"
1005 * INCLUDE-IF-NOT-DEFINED name <filename>
1008 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1009 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1012 * Map the filename to and then run the tests...
1015 if (!get_variable(vars
, attr
) &&
1016 !do_tests(vars
, get_filename(testfile
, filename
, temp
,
1027 print_fatal_error("Missing INCLUDE-IF-NOT-DEFINED name or filename on "
1028 "line %d.", linenum
);
1036 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1039 * SKIP-IF-DEFINED variable
1042 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1044 if (get_variable(vars
, temp
))
1049 print_fatal_error("Missing SKIP-IF-DEFINED variable on line %d.",
1055 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1058 * SKIP-IF-NOT-DEFINED variable
1061 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1063 if (!get_variable(vars
, temp
))
1068 print_fatal_error("Missing SKIP-IF-NOT-DEFINED variable on line %d.",
1074 else if (!strcmp(token
, "TRANSFER"))
1082 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1084 if (!strcmp(temp
, "auto"))
1085 Transfer
= _CUPS_TRANSFER_AUTO
;
1086 else if (!strcmp(temp
, "chunked"))
1087 Transfer
= _CUPS_TRANSFER_CHUNKED
;
1088 else if (!strcmp(temp
, "length"))
1089 Transfer
= _CUPS_TRANSFER_LENGTH
;
1092 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp
,
1100 print_fatal_error("Missing TRANSFER value on line %d.", linenum
);
1107 else if (!strcmp(token
, "VERSION"))
1109 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1111 if (!strcmp(temp
, "1.0"))
1113 else if (!strcmp(temp
, "1.1"))
1115 else if (!strcmp(temp
, "2.0"))
1117 else if (!strcmp(temp
, "2.1"))
1119 else if (!strcmp(temp
, "2.2"))
1123 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1130 print_fatal_error("Missing VERSION number on line %d.", linenum
);
1137 else if (strcmp(token
, "{"))
1139 print_fatal_error("Unexpected token %s seen on line %d.", token
, linenum
);
1145 * Initialize things...
1150 if (Output
== _CUPS_OUTPUT_PLIST
)
1152 else if (Output
== _CUPS_OUTPUT_TEST
)
1153 printf("\"%s\":\n", testfile
);
1158 strlcpy(resource
, vars
->resource
, sizeof(resource
));
1163 group
= IPP_TAG_ZERO
;
1164 ignore_errors
= IgnoreErrors
;
1171 transfer
= Transfer
;
1172 compression
[0] = '\0';
1174 strlcpy(name
, testfile
, sizeof(name
));
1175 if (strrchr(name
, '.') != NULL
)
1176 *strrchr(name
, '.') = '\0';
1179 * Parse until we see a close brace...
1182 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
1184 if (_cups_strcasecmp(token
, "COUNT") &&
1185 _cups_strcasecmp(token
, "DEFINE-MATCH") &&
1186 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1187 _cups_strcasecmp(token
, "DEFINE-VALUE") &&
1188 _cups_strcasecmp(token
, "IF-DEFINED") &&
1189 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1190 _cups_strcasecmp(token
, "IN-GROUP") &&
1191 _cups_strcasecmp(token
, "OF-TYPE") &&
1192 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1193 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1194 _cups_strcasecmp(token
, "REPEAT-NO-MATCH") &&
1195 _cups_strcasecmp(token
, "SAME-COUNT-AS") &&
1196 _cups_strcasecmp(token
, "WITH-ALL-VALUES") &&
1197 _cups_strcasecmp(token
, "WITH-VALUE"))
1200 if (_cups_strcasecmp(token
, "DEFINE-MATCH") &&
1201 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1202 _cups_strcasecmp(token
, "IF-DEFINED") &&
1203 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1204 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1205 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1206 _cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
1209 if (!strcmp(token
, "}"))
1211 else if (!strcmp(token
, "{") && lastcol
)
1214 * Another collection value
1217 ipp_t
*col
= get_collection(vars
, fp
, &linenum
);
1218 /* Collection value */
1222 ipp_attribute_t
*tempcol
; /* Pointer to new buffer */
1226 * Reallocate memory...
1229 if ((tempcol
= realloc(lastcol
, sizeof(ipp_attribute_t
) +
1230 (lastcol
->num_values
+ 1) *
1231 sizeof(_ipp_value_t
))) == NULL
)
1233 print_fatal_error("Unable to allocate memory on line %d.", linenum
);
1238 if (tempcol
!= lastcol
)
1241 * Reset pointers in the list...
1245 request
->prev
->next
= tempcol
;
1247 request
->attrs
= tempcol
;
1249 lastcol
= request
->current
= request
->last
= tempcol
;
1252 lastcol
->values
[lastcol
->num_values
].collection
= col
;
1253 lastcol
->num_values
++;
1261 else if (!strcmp(token
, "COMPRESSION"))
1265 * COMPRESSION deflate
1269 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1271 expand_variables(vars
, compression
, temp
, sizeof(compression
));
1273 if (strcmp(compression
, "none") && strcmp(compression
, "deflate") &&
1274 strcmp(compression
, "gzip"))
1276 if (strcmp(compression
, "none"))
1277 #endif /* HAVE_LIBZ */
1279 print_fatal_error("Unsupported COMPRESSION value '%s' on line %d.",
1280 compression
, linenum
);
1285 if (!strcmp(compression
, "none"))
1286 compression
[0] = '\0';
1290 print_fatal_error("Missing COMPRESSION value on line %d.", linenum
);
1295 else if (!strcmp(token
, "DEFINE"))
1301 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1302 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1304 expand_variables(vars
, token
, temp
, sizeof(token
));
1305 set_variable(vars
, attr
, token
);
1309 print_fatal_error("Missing DEFINE name and/or value on line %d.",
1315 else if (!strcmp(token
, "IGNORE-ERRORS"))
1322 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1323 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1325 ignore_errors
= !_cups_strcasecmp(temp
, "yes");
1329 print_fatal_error("Missing IGNORE-ERRORS value on line %d.", linenum
);
1336 else if (!_cups_strcasecmp(token
, "NAME"))
1342 get_token(fp
, name
, sizeof(name
), &linenum
);
1344 else if (!strcmp(token
, "REQUEST-ID"))
1351 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1353 if (isdigit(temp
[0] & 255))
1354 request_id
= atoi(temp
);
1355 else if (!_cups_strcasecmp(temp
, "random"))
1356 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
1359 print_fatal_error("Bad REQUEST-ID value \"%s\" on line %d.", temp
,
1367 print_fatal_error("Missing REQUEST-ID value on line %d.", linenum
);
1372 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1375 * SKIP-IF-DEFINED variable
1378 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1380 if (get_variable(vars
, temp
))
1385 print_fatal_error("Missing SKIP-IF-DEFINED value on line %d.",
1391 else if (!strcmp(token
, "SKIP-IF-MISSING"))
1394 * SKIP-IF-MISSING filename
1397 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1399 expand_variables(vars
, token
, temp
, sizeof(token
));
1400 get_filename(testfile
, filename
, token
, sizeof(filename
));
1402 if (access(filename
, R_OK
))
1407 print_fatal_error("Missing SKIP-IF-MISSING filename on line %d.",
1413 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1416 * SKIP-IF-NOT-DEFINED variable
1419 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1421 if (!get_variable(vars
, temp
))
1426 print_fatal_error("Missing SKIP-IF-NOT-DEFINED value on line %d.",
1432 else if (!strcmp(token
, "SKIP-PREVIOUS-ERROR"))
1435 * SKIP-PREVIOUS-ERROR yes
1436 * SKIP-PREVIOUS-ERROR no
1439 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1440 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1442 skip_previous
= !_cups_strcasecmp(temp
, "yes");
1446 print_fatal_error("Missing SKIP-PREVIOUS-ERROR value on line %d.", linenum
);
1453 else if (!strcmp(token
, "TRANSFER"))
1461 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1463 if (!strcmp(temp
, "auto"))
1464 transfer
= _CUPS_TRANSFER_AUTO
;
1465 else if (!strcmp(temp
, "chunked"))
1466 transfer
= _CUPS_TRANSFER_CHUNKED
;
1467 else if (!strcmp(temp
, "length"))
1468 transfer
= _CUPS_TRANSFER_LENGTH
;
1471 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp
,
1479 print_fatal_error("Missing TRANSFER value on line %d.", linenum
);
1484 else if (!_cups_strcasecmp(token
, "VERSION"))
1486 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1488 if (!strcmp(temp
, "0.0"))
1490 else if (!strcmp(temp
, "1.0"))
1492 else if (!strcmp(temp
, "1.1"))
1494 else if (!strcmp(temp
, "2.0"))
1496 else if (!strcmp(temp
, "2.1"))
1498 else if (!strcmp(temp
, "2.2"))
1502 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1509 print_fatal_error("Missing VERSION number on line %d.", linenum
);
1514 else if (!_cups_strcasecmp(token
, "RESOURCE"))
1520 if (!get_token(fp
, resource
, sizeof(resource
), &linenum
))
1522 print_fatal_error("Missing RESOURCE path on line %d.", linenum
);
1527 else if (!_cups_strcasecmp(token
, "OPERATION"))
1533 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1535 print_fatal_error("Missing OPERATION code on line %d.", linenum
);
1540 expand_variables(vars
, token
, temp
, sizeof(token
));
1542 if ((op
= ippOpValue(token
)) == (ipp_op_t
)-1 &&
1543 (op
= strtol(token
, NULL
, 0)) == 0)
1545 print_fatal_error("Bad OPERATION code \"%s\" on line %d.", token
,
1551 else if (!_cups_strcasecmp(token
, "GROUP"))
1554 * Attribute group...
1557 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1559 print_fatal_error("Missing GROUP tag on line %d.", linenum
);
1564 if ((value
= ippTagValue(token
)) < 0)
1566 print_fatal_error("Bad GROUP tag \"%s\" on line %d.", token
, linenum
);
1572 ippAddSeparator(request
);
1576 else if (!_cups_strcasecmp(token
, "DELAY"))
1579 * Delay before operation...
1584 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1586 print_fatal_error("Missing DELAY value on line %d.", linenum
);
1591 expand_variables(vars
, token
, temp
, sizeof(token
));
1593 if ((delay
= _cupsStrScand(token
, NULL
, localeconv())) <= 0.0)
1595 print_fatal_error("Bad DELAY value \"%s\" on line %d.", token
,
1602 if (Output
== _CUPS_OUTPUT_TEST
)
1603 printf(" [%g second delay]\n", delay
);
1605 usleep((int)(1000000.0 * delay
));
1608 else if (!_cups_strcasecmp(token
, "ATTR"))
1614 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1616 print_fatal_error("Missing ATTR value tag on line %d.", linenum
);
1621 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
1623 print_fatal_error("Bad ATTR value tag \"%s\" on line %d.", token
,
1629 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
1631 print_fatal_error("Missing ATTR name on line %d.", linenum
);
1636 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1638 print_fatal_error("Missing ATTR value on line %d.", linenum
);
1643 expand_variables(vars
, token
, temp
, sizeof(token
));
1648 case IPP_TAG_BOOLEAN
:
1649 if (!_cups_strcasecmp(token
, "true"))
1650 attrptr
= ippAddBoolean(request
, group
, attr
, 1);
1652 attrptr
= ippAddBoolean(request
, group
, attr
, atoi(token
));
1655 case IPP_TAG_INTEGER
:
1657 if (!strchr(token
, ','))
1658 attrptr
= ippAddInteger(request
, group
, value
, attr
,
1659 strtol(token
, &tokenptr
, 0));
1662 int values
[100], /* Values */
1663 num_values
= 1; /* Number of values */
1665 values
[0] = strtol(token
, &tokenptr
, 10);
1666 while (tokenptr
&& *tokenptr
&&
1667 num_values
< (int)(sizeof(values
) / sizeof(values
[0])))
1669 if (*tokenptr
== ',')
1671 else if (!isdigit(*tokenptr
& 255) && *tokenptr
!= '-')
1674 values
[num_values
] = strtol(tokenptr
, &tokenptr
, 0);
1678 attrptr
= ippAddIntegers(request
, group
, value
, attr
, num_values
, values
);
1681 if (!tokenptr
|| *tokenptr
)
1683 print_fatal_error("Bad %s value \"%s\" on line %d.",
1684 ippTagString(value
), token
, linenum
);
1690 case IPP_TAG_RESOLUTION
:
1692 int xres
, /* X resolution */
1693 yres
; /* Y resolution */
1694 char *ptr
; /* Pointer into value */
1696 xres
= yres
= strtol(token
, (char **)&ptr
, 10);
1697 if (ptr
> token
&& xres
> 0)
1700 yres
= strtol(ptr
+ 1, (char **)&ptr
, 10);
1703 if (ptr
<= token
|| xres
<= 0 || yres
<= 0 || !ptr
||
1704 (_cups_strcasecmp(ptr
, "dpi") &&
1705 _cups_strcasecmp(ptr
, "dpc") &&
1706 _cups_strcasecmp(ptr
, "dpcm") &&
1707 _cups_strcasecmp(ptr
, "other")))
1709 print_fatal_error("Bad resolution value \"%s\" on line %d.",
1715 if (!_cups_strcasecmp(ptr
, "dpi"))
1716 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_INCH
,
1718 else if (!_cups_strcasecmp(ptr
, "dpc") ||
1719 !_cups_strcasecmp(ptr
, "dpcm"))
1720 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_CM
,
1723 attrptr
= ippAddResolution(request
, group
, attr
, (ipp_res_t
)0,
1728 case IPP_TAG_RANGE
:
1730 int lowers
[4], /* Lower value */
1731 uppers
[4], /* Upper values */
1732 num_vals
; /* Number of values */
1735 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
1736 lowers
+ 0, uppers
+ 0,
1737 lowers
+ 1, uppers
+ 1,
1738 lowers
+ 2, uppers
+ 2,
1739 lowers
+ 3, uppers
+ 3);
1741 if ((num_vals
& 1) || num_vals
== 0)
1743 print_fatal_error("Bad rangeOfInteger value \"%s\" on line "
1744 "%d.", token
, linenum
);
1749 attrptr
= ippAddRanges(request
, group
, attr
, num_vals
/ 2, lowers
,
1754 case IPP_TAG_BEGIN_COLLECTION
:
1755 if (!strcmp(token
, "{"))
1757 ipp_t
*col
= get_collection(vars
, fp
, &linenum
);
1758 /* Collection value */
1762 attrptr
= lastcol
= ippAddCollection(request
, group
, attr
, col
);
1773 print_fatal_error("Bad ATTR collection value on line %d.",
1780 case IPP_TAG_STRING
:
1781 attrptr
= ippAddOctetString(request
, group
, attr
, token
,
1786 print_fatal_error("Unsupported ATTR value tag %s on line %d.",
1787 ippTagString(value
), linenum
);
1791 case IPP_TAG_TEXTLANG
:
1792 case IPP_TAG_NAMELANG
:
1795 case IPP_TAG_KEYWORD
:
1797 case IPP_TAG_URISCHEME
:
1798 case IPP_TAG_CHARSET
:
1799 case IPP_TAG_LANGUAGE
:
1800 case IPP_TAG_MIMETYPE
:
1801 if (!strchr(token
, ','))
1802 attrptr
= ippAddString(request
, group
, value
, attr
, NULL
, token
);
1806 * Multiple string values...
1809 int num_values
; /* Number of values */
1810 char *values
[100], /* Values */
1811 *ptr
; /* Pointer to next value */
1817 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
1819 if (ptr
> token
&& ptr
[-1] == '\\')
1820 _cups_strcpy(ptr
- 1, ptr
);
1824 values
[num_values
] = ptr
;
1829 attrptr
= ippAddStrings(request
, group
, value
, attr
, num_values
,
1830 NULL
, (const char **)values
);
1837 print_fatal_error("Unable to add attribute on line %d: %s", linenum
,
1838 cupsLastErrorString());
1843 else if (!_cups_strcasecmp(token
, "FILE"))
1849 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1851 print_fatal_error("Missing FILE filename on line %d.", linenum
);
1856 expand_variables(vars
, token
, temp
, sizeof(token
));
1857 get_filename(testfile
, filename
, token
, sizeof(filename
));
1859 if (access(filename
, R_OK
))
1861 print_fatal_error("Filename \"%s\" on line %d cannot be read.",
1863 print_fatal_error("Filename mapped to \"%s\".", filename
);
1868 else if (!_cups_strcasecmp(token
, "STATUS"))
1874 if (num_statuses
>= (int)(sizeof(statuses
) / sizeof(statuses
[0])))
1876 print_fatal_error("Too many STATUS's on line %d.", linenum
);
1881 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1883 print_fatal_error("Missing STATUS code on line %d.", linenum
);
1888 if ((statuses
[num_statuses
].status
= ippErrorValue(token
))
1889 == (ipp_status_t
)-1 &&
1890 (statuses
[num_statuses
].status
= strtol(token
, NULL
, 0)) == 0)
1892 print_fatal_error("Bad STATUS code \"%s\" on line %d.", token
,
1898 last_status
= statuses
+ num_statuses
;
1901 last_status
->define_match
= NULL
;
1902 last_status
->define_no_match
= NULL
;
1903 last_status
->if_defined
= NULL
;
1904 last_status
->if_not_defined
= NULL
;
1905 last_status
->repeat_limit
= 1000;
1906 last_status
->repeat_match
= 0;
1907 last_status
->repeat_no_match
= 0;
1909 else if (!_cups_strcasecmp(token
, "EXPECT"))
1912 * Expected attributes...
1915 if (num_expects
>= (int)(sizeof(expects
) / sizeof(expects
[0])))
1917 print_fatal_error("Too many EXPECT's on line %d.", linenum
);
1922 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1924 print_fatal_error("Missing EXPECT name on line %d.", linenum
);
1929 last_expect
= expects
+ num_expects
;
1932 memset(last_expect
, 0, sizeof(_cups_expect_t
));
1933 last_expect
->repeat_limit
= 1000;
1935 if (token
[0] == '!')
1937 last_expect
->not_expect
= 1;
1938 last_expect
->name
= strdup(token
+ 1);
1940 else if (token
[0] == '?')
1942 last_expect
->optional
= 1;
1943 last_expect
->name
= strdup(token
+ 1);
1946 last_expect
->name
= strdup(token
);
1948 else if (!_cups_strcasecmp(token
, "COUNT"))
1950 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1952 print_fatal_error("Missing COUNT number on line %d.", linenum
);
1957 if ((i
= atoi(token
)) <= 0)
1959 print_fatal_error("Bad COUNT \"%s\" on line %d.", token
, linenum
);
1965 last_expect
->count
= i
;
1968 print_fatal_error("COUNT without a preceding EXPECT on line %d.",
1974 else if (!_cups_strcasecmp(token
, "DEFINE-MATCH"))
1976 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1978 print_fatal_error("Missing DEFINE-MATCH variable on line %d.",
1985 last_expect
->define_match
= strdup(token
);
1986 else if (last_status
)
1987 last_status
->define_match
= strdup(token
);
1990 print_fatal_error("DEFINE-MATCH without a preceding EXPECT or STATUS "
1991 "on line %d.", linenum
);
1996 else if (!_cups_strcasecmp(token
, "DEFINE-NO-MATCH"))
1998 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2000 print_fatal_error("Missing DEFINE-NO-MATCH variable on line %d.",
2007 last_expect
->define_no_match
= strdup(token
);
2008 else if (last_status
)
2009 last_status
->define_no_match
= strdup(token
);
2012 print_fatal_error("DEFINE-NO-MATCH without a preceding EXPECT or "
2013 "STATUS on line %d.", linenum
);
2018 else if (!_cups_strcasecmp(token
, "DEFINE-VALUE"))
2020 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2022 print_fatal_error("Missing DEFINE-VALUE variable on line %d.",
2029 last_expect
->define_value
= strdup(token
);
2032 print_fatal_error("DEFINE-VALUE without a preceding EXPECT on "
2033 "line %d.", linenum
);
2038 else if (!_cups_strcasecmp(token
, "OF-TYPE"))
2040 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2042 print_fatal_error("Missing OF-TYPE value tag(s) on line %d.",
2049 last_expect
->of_type
= strdup(token
);
2052 print_fatal_error("OF-TYPE without a preceding EXPECT on line %d.",
2058 else if (!_cups_strcasecmp(token
, "IN-GROUP"))
2060 ipp_tag_t in_group
; /* IN-GROUP value */
2063 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2065 print_fatal_error("Missing IN-GROUP group tag on line %d.", linenum
);
2070 if ((in_group
= ippTagValue(token
)) == (ipp_tag_t
)-1)
2073 else if (last_expect
)
2074 last_expect
->in_group
= in_group
;
2077 print_fatal_error("IN-GROUP without a preceding EXPECT on line %d.",
2083 else if (!_cups_strcasecmp(token
, "REPEAT-LIMIT"))
2085 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2087 print_fatal_error("Missing REPEAT-LIMIT value on line %d.", linenum
);
2091 else if (atoi(token
) <= 0)
2093 print_fatal_error("Bad REPEAT-LIMIT value on line %d.", linenum
);
2099 last_status
->repeat_limit
= atoi(token
);
2100 else if (last_expect
)
2101 last_expect
->repeat_limit
= atoi(token
);
2104 print_fatal_error("REPEAT-LIMIT without a preceding EXPECT or STATUS "
2105 "on line %d.", linenum
);
2110 else if (!_cups_strcasecmp(token
, "REPEAT-MATCH"))
2113 last_status
->repeat_match
= 1;
2114 else if (last_expect
)
2115 last_expect
->repeat_match
= 1;
2118 print_fatal_error("REPEAT-MATCH without a preceding EXPECT or STATUS "
2119 "on line %d.", linenum
);
2124 else if (!_cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
2127 last_status
->repeat_no_match
= 1;
2128 else if (last_expect
)
2129 last_expect
->repeat_no_match
= 1;
2132 print_fatal_error("REPEAT-NO-MATCH without a preceding EXPECT or "
2133 "STATUS on ine %d.", linenum
);
2138 else if (!_cups_strcasecmp(token
, "SAME-COUNT-AS"))
2140 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2142 print_fatal_error("Missing SAME-COUNT-AS name on line %d.", linenum
);
2148 last_expect
->same_count_as
= strdup(token
);
2151 print_fatal_error("SAME-COUNT-AS without a preceding EXPECT on line "
2157 else if (!_cups_strcasecmp(token
, "IF-DEFINED"))
2159 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2161 print_fatal_error("Missing IF-DEFINED name on line %d.", linenum
);
2167 last_expect
->if_defined
= strdup(token
);
2168 else if (last_status
)
2169 last_status
->if_defined
= strdup(token
);
2172 print_fatal_error("IF-DEFINED without a preceding EXPECT or STATUS "
2173 "on line %d.", linenum
);
2178 else if (!_cups_strcasecmp(token
, "IF-NOT-DEFINED"))
2180 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2182 print_fatal_error("Missing IF-NOT-DEFINED name on line %d.", linenum
);
2188 last_expect
->if_not_defined
= strdup(token
);
2189 else if (last_status
)
2190 last_status
->if_not_defined
= strdup(token
);
2193 print_fatal_error("IF-NOT-DEFINED without a preceding EXPECT or STATUS "
2194 "on line %d.", linenum
);
2199 else if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") ||
2200 !_cups_strcasecmp(token
, "WITH-VALUE"))
2202 if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") && last_expect
)
2203 last_expect
->with_flags
= _CUPS_WITH_ALL
;
2205 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
2207 print_fatal_error("Missing %s value on line %d.", token
, linenum
);
2215 * Expand any variables in the value and then save it.
2218 expand_variables(vars
, token
, temp
, sizeof(token
));
2220 tokenptr
= token
+ strlen(token
) - 1;
2222 if (token
[0] == '/' && tokenptr
> token
&& *tokenptr
== '/')
2225 * WITH-VALUE is a POSIX extended regular expression.
2228 last_expect
->with_value
= calloc(1, tokenptr
- token
);
2229 last_expect
->with_flags
|= _CUPS_WITH_REGEX
;
2231 if (last_expect
->with_value
)
2232 memcpy(last_expect
->with_value
, token
+ 1, tokenptr
- token
- 1);
2237 * WITH-VALUE is a literal value...
2240 char *ptr
; /* Pointer into value */
2242 for (ptr
= token
; *ptr
; ptr
++)
2244 if (*ptr
== '\\' && ptr
[1])
2247 * Remove \ from \foo...
2250 _cups_strcpy(ptr
, ptr
+ 1);
2254 last_expect
->with_value
= strdup(token
);
2255 last_expect
->with_flags
|= _CUPS_WITH_LITERAL
;
2260 print_fatal_error("%s without a preceding EXPECT on line %d.", token
,
2266 else if (!_cups_strcasecmp(token
, "DISPLAY"))
2269 * Display attributes...
2272 if (num_displayed
>= (int)(sizeof(displayed
) / sizeof(displayed
[0])))
2274 print_fatal_error("Too many DISPLAY's on line %d", linenum
);
2279 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2281 print_fatal_error("Missing DISPLAY name on line %d.", linenum
);
2286 displayed
[num_displayed
] = strdup(token
);
2291 print_fatal_error("Unexpected token %s seen on line %d.", token
,
2299 * Submit the IPP request...
2304 request
->request
.op
.version
[0] = version
/ 10;
2305 request
->request
.op
.version
[1] = version
% 10;
2306 request
->request
.op
.operation_id
= op
;
2307 request
->request
.op
.request_id
= request_id
;
2309 if (Output
== _CUPS_OUTPUT_PLIST
)
2312 puts("<key>Name</key>");
2313 print_xml_string("string", name
);
2314 puts("<key>Operation</key>");
2315 print_xml_string("string", ippOpString(op
));
2316 puts("<key>RequestAttributes</key>");
2321 for (attrptr
= request
->attrs
,
2322 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2324 attrptr
= attrptr
->next
)
2325 print_attr(attrptr
, &group
);
2330 else if (Output
== _CUPS_OUTPUT_TEST
)
2334 printf(" %s:\n", ippOpString(op
));
2336 for (attrptr
= request
->attrs
; attrptr
; attrptr
= attrptr
->next
)
2337 print_attr(attrptr
, NULL
);
2340 printf(" %-68.68s [", name
);
2344 if ((skip_previous
&& !prev_pass
) || skip_test
)
2351 if (Output
== _CUPS_OUTPUT_PLIST
)
2353 puts("<key>Successful</key>");
2355 puts("<key>StatusCode</key>");
2356 print_xml_string("string", "skip");
2357 puts("<key>ResponseAttributes</key>");
2360 else if (Output
== _CUPS_OUTPUT_TEST
)
2367 repeat_interval
= 1;
2376 if (transfer
== _CUPS_TRANSFER_CHUNKED
||
2377 (transfer
== _CUPS_TRANSFER_AUTO
&& filename
[0]))
2380 * Send request using chunking - a 0 length means "chunk".
2388 * Send request using content length...
2391 length
= ippLength(request
);
2393 if (filename
[0] && (reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2396 * Read the file to get the uncompressed file size...
2399 while ((bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
2402 cupsFileClose(reqfile
);
2407 * Send the request...
2414 if (status
!= HTTP_ERROR
)
2416 while (!response
&& !Cancel
&& prev_pass
)
2418 status
= cupsSendRequest(http
, request
, resource
, length
);
2422 httpSetField(http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
2423 #endif /* HAVE_LIBZ */
2425 if (!Cancel
&& status
== HTTP_CONTINUE
&&
2426 request
->state
== IPP_DATA
&& filename
[0])
2428 if ((reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2431 (bytes
= cupsFileRead(reqfile
, buffer
,
2432 sizeof(buffer
))) > 0)
2433 if ((status
= cupsWriteRequestData(http
, buffer
,
2434 bytes
)) != HTTP_CONTINUE
)
2437 cupsFileClose(reqfile
);
2441 snprintf(buffer
, sizeof(buffer
), "%s: %s", filename
,
2443 _cupsSetError(IPP_INTERNAL_ERROR
, buffer
, 0);
2445 status
= HTTP_ERROR
;
2450 * Get the server's response...
2453 if (!Cancel
&& status
!= HTTP_ERROR
)
2455 response
= cupsGetResponse(http
, resource
);
2456 status
= httpGetStatus(http
);
2459 if (!Cancel
&& status
== HTTP_ERROR
&& http
->error
!= EINVAL
&&
2461 http
->error
!= WSAETIMEDOUT
)
2463 http
->error
!= ETIMEDOUT
)
2466 if (httpReconnect(http
))
2469 else if (status
== HTTP_ERROR
)
2474 else if (status
!= HTTP_OK
)
2482 if (!Cancel
&& status
== HTTP_ERROR
&& http
->error
!= EINVAL
&&
2484 http
->error
!= WSAETIMEDOUT
)
2486 http
->error
!= ETIMEDOUT
)
2489 if (httpReconnect(http
))
2492 else if (status
== HTTP_ERROR
)
2495 httpReconnect(http
);
2499 else if (status
!= HTTP_OK
)
2506 * Check results of request...
2509 cupsArrayClear(errors
);
2511 if (http
->version
!= HTTP_1_1
)
2512 add_stringf(errors
, "Bad HTTP version (%d.%d)", http
->version
/ 100,
2513 http
->version
% 100);
2518 * No response, log error...
2521 add_stringf(errors
, "IPP request failed with status %s (%s)",
2522 ippErrorString(cupsLastError()),
2523 cupsLastErrorString());
2528 * Collect common attribute values...
2531 if ((attrptr
= ippFindAttribute(response
, "job-id",
2532 IPP_TAG_INTEGER
)) != NULL
)
2534 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2535 set_variable(vars
, "job-id", temp
);
2538 if ((attrptr
= ippFindAttribute(response
, "job-uri",
2539 IPP_TAG_URI
)) != NULL
)
2540 set_variable(vars
, "job-uri", attrptr
->values
[0].string
.text
);
2542 if ((attrptr
= ippFindAttribute(response
, "notify-subscription-id",
2543 IPP_TAG_INTEGER
)) != NULL
)
2545 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2546 set_variable(vars
, "notify-subscription-id", temp
);
2550 * Check response, validating groups and attributes and logging errors
2554 if (response
->state
!= IPP_DATA
)
2556 "Missing end-of-attributes-tag in response "
2557 "(RFC 2910 section 3.5.1)");
2560 (response
->request
.status
.version
[0] != (version
/ 10) ||
2561 response
->request
.status
.version
[1] != (version
% 10)))
2563 "Bad version %d.%d in response - expected %d.%d "
2564 "(RFC 2911 section 3.1.8).",
2565 response
->request
.status
.version
[0],
2566 response
->request
.status
.version
[1],
2567 version
/ 10, version
% 10);
2569 if (response
->request
.status
.request_id
!= request_id
)
2571 "Bad request ID %d in response - expected %d "
2572 "(RFC 2911 section 3.1.1)",
2573 response
->request
.status
.request_id
, request_id
);
2575 attrptr
= response
->attrs
;
2578 "Missing first attribute \"attributes-charset "
2579 "(charset)\" in group operation-attributes-tag "
2580 "(RFC 2911 section 3.1.4).");
2583 if (!attrptr
->name
||
2584 attrptr
->value_tag
!= IPP_TAG_CHARSET
||
2585 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2586 attrptr
->num_values
!= 1 ||
2587 strcmp(attrptr
->name
, "attributes-charset"))
2589 "Bad first attribute \"%s (%s%s)\" in group %s, "
2590 "expected \"attributes-charset (charset)\" in "
2591 "group operation-attributes-tag (RFC 2911 section "
2593 attrptr
->name
? attrptr
->name
: "(null)",
2594 attrptr
->num_values
> 1 ? "1setOf " : "",
2595 ippTagString(attrptr
->value_tag
),
2596 ippTagString(attrptr
->group_tag
));
2598 attrptr
= attrptr
->next
;
2601 "Missing second attribute \"attributes-natural-"
2602 "language (naturalLanguage)\" in group "
2603 "operation-attributes-tag (RFC 2911 section "
2605 else if (!attrptr
->name
||
2606 attrptr
->value_tag
!= IPP_TAG_LANGUAGE
||
2607 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2608 attrptr
->num_values
!= 1 ||
2609 strcmp(attrptr
->name
, "attributes-natural-language"))
2611 "Bad first attribute \"%s (%s%s)\" in group %s, "
2612 "expected \"attributes-natural-language "
2613 "(naturalLanguage)\" in group "
2614 "operation-attributes-tag (RFC 2911 section "
2616 attrptr
->name
? attrptr
->name
: "(null)",
2617 attrptr
->num_values
> 1 ? "1setOf " : "",
2618 ippTagString(attrptr
->value_tag
),
2619 ippTagString(attrptr
->group_tag
));
2622 if ((attrptr
= ippFindAttribute(response
, "status-message",
2623 IPP_TAG_ZERO
)) != NULL
)
2625 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2627 "status-message (text(255)) has wrong value tag "
2628 "%s (RFC 2911 section 3.1.6.2).",
2629 ippTagString(attrptr
->value_tag
));
2630 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2632 "status-message (text(255)) has wrong group tag "
2633 "%s (RFC 2911 section 3.1.6.2).",
2634 ippTagString(attrptr
->group_tag
));
2635 if (attrptr
->num_values
!= 1)
2637 "status-message (text(255)) has %d values "
2638 "(RFC 2911 section 3.1.6.2).",
2639 attrptr
->num_values
);
2640 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2641 strlen(attrptr
->values
[0].string
.text
) > 255)
2643 "status-message (text(255)) has bad length %d"
2644 " (RFC 2911 section 3.1.6.2).",
2645 (int)strlen(attrptr
->values
[0].string
.text
));
2648 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
2649 IPP_TAG_ZERO
)) != NULL
)
2651 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2653 "detailed-status-message (text(MAX)) has wrong "
2654 "value tag %s (RFC 2911 section 3.1.6.3).",
2655 ippTagString(attrptr
->value_tag
));
2656 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2658 "detailed-status-message (text(MAX)) has wrong "
2659 "group tag %s (RFC 2911 section 3.1.6.3).",
2660 ippTagString(attrptr
->group_tag
));
2661 if (attrptr
->num_values
!= 1)
2663 "detailed-status-message (text(MAX)) has %d values"
2664 " (RFC 2911 section 3.1.6.3).",
2665 attrptr
->num_values
);
2666 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2667 strlen(attrptr
->values
[0].string
.text
) > 1023)
2669 "detailed-status-message (text(MAX)) has bad "
2670 "length %d (RFC 2911 section 3.1.6.3).",
2671 (int)strlen(attrptr
->values
[0].string
.text
));
2674 a
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2676 for (attrptr
= response
->attrs
,
2677 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2679 attrptr
= attrptr
->next
)
2681 if (attrptr
->group_tag
!= group
)
2683 int out_of_order
= 0; /* Are attribute groups out-of-order? */
2686 switch (attrptr
->group_tag
)
2691 case IPP_TAG_OPERATION
:
2695 case IPP_TAG_UNSUPPORTED_GROUP
:
2696 if (group
!= IPP_TAG_OPERATION
)
2701 case IPP_TAG_PRINTER
:
2702 if (group
!= IPP_TAG_OPERATION
&&
2703 group
!= IPP_TAG_UNSUPPORTED_GROUP
)
2707 case IPP_TAG_SUBSCRIPTION
:
2708 if (group
> attrptr
->group_tag
&&
2709 group
!= IPP_TAG_DOCUMENT
)
2714 if (group
> attrptr
->group_tag
)
2720 add_stringf(errors
, "Attribute groups out of order (%s < %s)",
2721 ippTagString(attrptr
->group_tag
),
2722 ippTagString(group
));
2724 if (attrptr
->group_tag
!= IPP_TAG_ZERO
)
2725 group
= attrptr
->group_tag
;
2728 validate_attr(errors
, attrptr
);
2732 if (cupsArrayFind(a
, attrptr
->name
))
2733 add_stringf(errors
, "Duplicate \"%s\" attribute in %s group",
2734 attrptr
->name
, ippTagString(group
));
2736 cupsArrayAdd(a
, attrptr
->name
);
2743 * Now check the test-defined expected status-code and attribute
2747 for (i
= 0; i
< num_statuses
; i
++)
2749 if (statuses
[i
].if_defined
&&
2750 !get_variable(vars
, statuses
[i
].if_defined
))
2753 if (statuses
[i
].if_not_defined
&&
2754 get_variable(vars
, statuses
[i
].if_not_defined
))
2757 if (response
->request
.status
.status_code
== statuses
[i
].status
)
2759 if (statuses
[i
].repeat_match
&&
2760 repeat_count
< statuses
[i
].repeat_limit
)
2763 if (statuses
[i
].define_match
)
2764 set_variable(vars
, statuses
[i
].define_match
, "1");
2770 if (statuses
[i
].repeat_no_match
&&
2771 repeat_count
< statuses
[i
].repeat_limit
)
2774 if (statuses
[i
].define_no_match
)
2776 set_variable(vars
, statuses
[i
].define_no_match
, "1");
2782 if (i
== num_statuses
&& num_statuses
> 0)
2784 for (i
= 0; i
< num_statuses
; i
++)
2786 if (statuses
[i
].if_defined
&&
2787 !get_variable(vars
, statuses
[i
].if_defined
))
2790 if (statuses
[i
].if_not_defined
&&
2791 get_variable(vars
, statuses
[i
].if_not_defined
))
2794 if (!statuses
[i
].repeat_match
)
2795 add_stringf(errors
, "EXPECTED: STATUS %s (got %s)",
2796 ippErrorString(statuses
[i
].status
),
2797 ippErrorString(cupsLastError()));
2800 if ((attrptr
= ippFindAttribute(response
, "status-message",
2801 IPP_TAG_TEXT
)) != NULL
)
2802 add_stringf(errors
, "status-message=\"%s\"",
2803 attrptr
->values
[0].string
.text
);
2806 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
2808 if (expect
->if_defined
&& !get_variable(vars
, expect
->if_defined
))
2811 if (expect
->if_not_defined
&&
2812 get_variable(vars
, expect
->if_not_defined
))
2815 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
2817 if ((found
&& expect
->not_expect
) ||
2818 (!found
&& !(expect
->not_expect
|| expect
->optional
)) ||
2819 (found
&& !expect_matches(expect
, found
->value_tag
)) ||
2820 (found
&& expect
->in_group
&&
2821 found
->group_tag
!= expect
->in_group
))
2823 if (expect
->define_no_match
)
2824 set_variable(vars
, expect
->define_no_match
, "1");
2825 else if (!expect
->define_match
&& !expect
->define_value
)
2827 if (found
&& expect
->not_expect
)
2828 add_stringf(errors
, "NOT EXPECTED: %s", expect
->name
);
2829 else if (!found
&& !(expect
->not_expect
|| expect
->optional
))
2830 add_stringf(errors
, "EXPECTED: %s", expect
->name
);
2833 if (!expect_matches(expect
, found
->value_tag
))
2834 add_stringf(errors
, "EXPECTED: %s OF-TYPE %s (got %s)",
2835 expect
->name
, expect
->of_type
,
2836 ippTagString(found
->value_tag
));
2838 if (expect
->in_group
&& found
->group_tag
!= expect
->in_group
)
2839 add_stringf(errors
, "EXPECTED: %s IN-GROUP %s (got %s).",
2840 expect
->name
, ippTagString(expect
->in_group
),
2841 ippTagString(found
->group_tag
));
2845 if (expect
->repeat_no_match
&&
2846 repeat_count
< expect
->repeat_limit
)
2853 ippAttributeString(found
, buffer
, sizeof(buffer
));
2856 !with_value(NULL
, expect
->with_value
, expect
->with_flags
, found
,
2857 buffer
, sizeof(buffer
)))
2859 if (expect
->define_no_match
)
2860 set_variable(vars
, expect
->define_no_match
, "1");
2861 else if (!expect
->define_match
&& !expect
->define_value
&&
2862 !expect
->repeat_match
&& !expect
->repeat_no_match
)
2864 if (expect
->with_flags
& _CUPS_WITH_REGEX
)
2865 add_stringf(errors
, "EXPECTED: %s %s /%s/",
2867 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
2868 "WITH-ALL-VALUES" : "WITH-VALUE",
2869 expect
->with_value
);
2871 add_stringf(errors
, "EXPECTED: %s %s \"%s\"",
2873 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
2874 "WITH-ALL-VALUES" : "WITH-VALUE",
2875 expect
->with_value
);
2877 with_value(errors
, expect
->with_value
, expect
->with_flags
, found
,
2878 buffer
, sizeof(buffer
));
2881 if (expect
->repeat_no_match
&&
2882 repeat_count
< expect
->repeat_limit
)
2888 if (found
&& expect
->count
> 0 &&
2889 found
->num_values
!= expect
->count
)
2891 if (expect
->define_no_match
)
2892 set_variable(vars
, expect
->define_no_match
, "1");
2893 else if (!expect
->define_match
&& !expect
->define_value
)
2895 add_stringf(errors
, "EXPECTED: %s COUNT %d (got %d)", expect
->name
,
2896 expect
->count
, found
->num_values
);
2899 if (expect
->repeat_no_match
&&
2900 repeat_count
< expect
->repeat_limit
)
2906 if (found
&& expect
->same_count_as
)
2908 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
2911 if (!attrptr
|| attrptr
->num_values
!= found
->num_values
)
2913 if (expect
->define_no_match
)
2914 set_variable(vars
, expect
->define_no_match
, "1");
2915 else if (!expect
->define_match
&& !expect
->define_value
)
2919 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2920 "(not returned)", expect
->name
,
2921 found
->num_values
, expect
->same_count_as
);
2922 else if (attrptr
->num_values
!= found
->num_values
)
2924 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2925 "(%d values)", expect
->name
, found
->num_values
,
2926 expect
->same_count_as
, attrptr
->num_values
);
2929 if (expect
->repeat_no_match
&&
2930 repeat_count
< expect
->repeat_limit
)
2937 if (found
&& expect
->define_match
)
2938 set_variable(vars
, expect
->define_match
, "1");
2940 if (found
&& expect
->define_value
)
2941 set_variable(vars
, expect
->define_value
, buffer
);
2943 if (found
&& expect
->repeat_match
&&
2944 repeat_count
< expect
->repeat_limit
)
2950 * If we are going to repeat this test, sleep 1 second so we don't flood
2951 * the printer with requests...
2956 if (Output
== _CUPS_OUTPUT_TEST
)
2958 printf("%04d]\n", repeat_count
);
2962 sleep(repeat_interval
);
2963 repeat_interval
= _cupsNextDelay(repeat_interval
, &repeat_prev
);
2965 if (Output
== _CUPS_OUTPUT_TEST
)
2967 printf(" %-68.68s [", name
);
2972 while (repeat_test
);
2978 if (cupsArrayCount(errors
) > 0)
2979 prev_pass
= pass
= 0;
2986 if (Output
== _CUPS_OUTPUT_PLIST
)
2988 puts("<key>Successful</key>");
2989 puts(prev_pass
? "<true />" : "<false />");
2990 puts("<key>StatusCode</key>");
2991 print_xml_string("string", ippErrorString(cupsLastError()));
2992 puts("<key>ResponseAttributes</key>");
2995 for (attrptr
= response
? response
->attrs
: NULL
,
2996 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2998 attrptr
= attrptr
->next
)
2999 print_attr(attrptr
, &group
);
3003 else if (Output
== _CUPS_OUTPUT_TEST
)
3005 puts(prev_pass
? "PASS]" : "FAIL]");
3007 if (!prev_pass
|| (Verbosity
&& response
))
3009 printf(" RECEIVED: %lu bytes in response\n",
3010 (unsigned long)ippLength(response
));
3011 printf(" status-code = %s (%s)\n", ippErrorString(cupsLastError()),
3012 cupsLastErrorString());
3016 for (attrptr
= response
->attrs
;
3018 attrptr
= attrptr
->next
)
3019 print_attr(attrptr
, NULL
);
3023 else if (!prev_pass
)
3024 fprintf(stderr
, "%s\n", cupsLastErrorString());
3026 if (prev_pass
&& Output
>= _CUPS_OUTPUT_LIST
&& !Verbosity
&&
3029 size_t width
; /* Length of value */
3031 for (i
= 0; i
< num_displayed
; i
++)
3033 widths
[i
] = strlen(displayed
[i
]);
3035 for (attrptr
= ippFindAttribute(response
, displayed
[i
], IPP_TAG_ZERO
);
3037 attrptr
= ippFindNextAttribute(response
, displayed
[i
],
3040 width
= ippAttributeString(attrptr
, NULL
, 0);
3041 if (width
> widths
[i
])
3046 if (Output
== _CUPS_OUTPUT_CSV
)
3047 print_csv(NULL
, num_displayed
, displayed
, widths
);
3049 print_line(NULL
, num_displayed
, displayed
, widths
);
3051 attrptr
= response
->attrs
;
3055 while (attrptr
&& attrptr
->group_tag
<= IPP_TAG_OPERATION
)
3056 attrptr
= attrptr
->next
;
3060 if (Output
== _CUPS_OUTPUT_CSV
)
3061 print_csv(attrptr
, num_displayed
, displayed
, widths
);
3063 print_line(attrptr
, num_displayed
, displayed
, widths
);
3065 while (attrptr
&& attrptr
->group_tag
> IPP_TAG_OPERATION
)
3066 attrptr
= attrptr
->next
;
3070 else if (!prev_pass
)
3072 if (Output
== _CUPS_OUTPUT_PLIST
)
3074 puts("<key>Errors</key>");
3077 for (error
= (char *)cupsArrayFirst(errors
);
3079 error
= (char *)cupsArrayNext(errors
))
3080 print_xml_string("string", error
);
3086 for (error
= (char *)cupsArrayFirst(errors
);
3088 error
= (char *)cupsArrayNext(errors
))
3089 printf(" %s\n", error
);
3093 if (num_displayed
> 0 && !Verbosity
&& response
&&
3094 Output
== _CUPS_OUTPUT_TEST
)
3096 for (attrptr
= response
->attrs
;
3098 attrptr
= attrptr
->next
)
3102 for (i
= 0; i
< num_displayed
; i
++)
3104 if (!strcmp(displayed
[i
], attrptr
->name
))
3106 print_attr(attrptr
, NULL
);
3116 if (Output
== _CUPS_OUTPUT_PLIST
)
3121 ippDelete(response
);
3124 for (i
= 0; i
< num_statuses
; i
++)
3126 if (statuses
[i
].if_defined
)
3127 free(statuses
[i
].if_defined
);
3128 if (statuses
[i
].if_not_defined
)
3129 free(statuses
[i
].if_not_defined
);
3130 if (statuses
[i
].define_match
)
3131 free(statuses
[i
].define_match
);
3132 if (statuses
[i
].define_no_match
)
3133 free(statuses
[i
].define_no_match
);
3137 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3140 if (expect
->of_type
)
3141 free(expect
->of_type
);
3142 if (expect
->same_count_as
)
3143 free(expect
->same_count_as
);
3144 if (expect
->if_defined
)
3145 free(expect
->if_defined
);
3146 if (expect
->if_not_defined
)
3147 free(expect
->if_not_defined
);
3148 if (expect
->with_value
)
3149 free(expect
->with_value
);
3150 if (expect
->define_match
)
3151 free(expect
->define_match
);
3152 if (expect
->define_no_match
)
3153 free(expect
->define_no_match
);
3154 if (expect
->define_value
)
3155 free(expect
->define_value
);
3159 for (i
= 0; i
< num_displayed
; i
++)
3163 if (!ignore_errors
&& !prev_pass
)
3169 cupsArrayDelete(errors
);
3176 ippDelete(response
);
3178 for (i
= 0; i
< num_statuses
; i
++)
3180 if (statuses
[i
].if_defined
)
3181 free(statuses
[i
].if_defined
);
3182 if (statuses
[i
].if_not_defined
)
3183 free(statuses
[i
].if_not_defined
);
3184 if (statuses
[i
].define_match
)
3185 free(statuses
[i
].define_match
);
3186 if (statuses
[i
].define_no_match
)
3187 free(statuses
[i
].define_no_match
);
3190 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3193 if (expect
->of_type
)
3194 free(expect
->of_type
);
3195 if (expect
->same_count_as
)
3196 free(expect
->same_count_as
);
3197 if (expect
->if_defined
)
3198 free(expect
->if_defined
);
3199 if (expect
->if_not_defined
)
3200 free(expect
->if_not_defined
);
3201 if (expect
->with_value
)
3202 free(expect
->with_value
);
3203 if (expect
->define_match
)
3204 free(expect
->define_match
);
3205 if (expect
->define_no_match
)
3206 free(expect
->define_no_match
);
3207 if (expect
->define_value
)
3208 free(expect
->define_value
);
3211 for (i
= 0; i
< num_displayed
; i
++)
3219 * 'expand_variables()' - Expand variables in a string.
3223 expand_variables(_cups_vars_t
*vars
, /* I - Variables */
3224 char *dst
, /* I - Destination string buffer */
3225 const char *src
, /* I - Source string */
3226 size_t dstsize
) /* I - Size of destination buffer */
3228 char *dstptr
, /* Pointer into destination */
3229 *dstend
, /* End of destination */
3230 temp
[256], /* Temporary string */
3231 *tempptr
; /* Pointer into temporary string */
3232 const char *value
; /* Value to substitute */
3236 dstend
= dst
+ dstsize
- 1;
3238 while (*src
&& dstptr
< dstend
)
3243 * Substitute a string/number...
3246 if (!strncmp(src
, "$$", 2))
3251 else if (!strncmp(src
, "$ENV[", 5))
3253 strlcpy(temp
, src
+ 5, sizeof(temp
));
3255 for (tempptr
= temp
; *tempptr
; tempptr
++)
3256 if (*tempptr
== ']')
3262 value
= getenv(temp
);
3263 src
+= tempptr
- temp
+ 5;
3267 strlcpy(temp
, src
+ 1, sizeof(temp
));
3269 for (tempptr
= temp
; *tempptr
; tempptr
++)
3270 if (!isalnum(*tempptr
& 255) && *tempptr
!= '-' && *tempptr
!= '_')
3276 if (!strcmp(temp
, "uri"))
3278 else if (!strcmp(temp
, "filename"))
3279 value
= vars
->filename
;
3280 else if (!strcmp(temp
, "scheme") || !strcmp(temp
, "method"))
3281 value
= vars
->scheme
;
3282 else if (!strcmp(temp
, "username"))
3283 value
= vars
->userpass
;
3284 else if (!strcmp(temp
, "hostname"))
3285 value
= vars
->hostname
;
3286 else if (!strcmp(temp
, "port"))
3288 snprintf(temp
, sizeof(temp
), "%d", vars
->port
);
3291 else if (!strcmp(temp
, "resource"))
3292 value
= vars
->resource
;
3293 else if (!strcmp(temp
, "user"))
3296 value
= get_variable(vars
, temp
);
3298 src
+= tempptr
- temp
+ 1;
3308 strlcpy(dstptr
, value
, dstend
- dstptr
+ 1);
3309 dstptr
+= strlen(dstptr
);
3321 * 'expect_matches()' - Return true if the tag matches the specification.
3324 static int /* O - 1 if matches, 0 otherwise */
3326 _cups_expect_t
*expect
, /* I - Expected attribute */
3327 ipp_tag_t value_tag
) /* I - Value tag for attribute */
3329 int match
; /* Match? */
3330 char *of_type
, /* Type name to match */
3331 *next
, /* Next name to match */
3332 sep
; /* Separator character */
3336 * If we don't expect a particular type, return immediately...
3339 if (!expect
->of_type
)
3343 * Parse the "of_type" value since the string can contain multiple attribute
3344 * types separated by "," or "|"...
3347 for (of_type
= expect
->of_type
, match
= 0; !match
&& *of_type
; of_type
= next
)
3350 * Find the next separator, and set it (temporarily) to nul if present.
3353 for (next
= of_type
; *next
&& *next
!= '|' && *next
!= ','; next
++);
3355 if ((sep
= *next
) != '\0')
3359 * Support some meta-types to make it easier to write the test file.
3362 if (!strcmp(of_type
, "text"))
3363 match
= value_tag
== IPP_TAG_TEXTLANG
|| value_tag
== IPP_TAG_TEXT
;
3364 else if (!strcmp(of_type
, "name"))
3365 match
= value_tag
== IPP_TAG_NAMELANG
|| value_tag
== IPP_TAG_NAME
;
3366 else if (!strcmp(of_type
, "collection"))
3367 match
= value_tag
== IPP_TAG_BEGIN_COLLECTION
;
3369 match
= value_tag
== ippTagValue(of_type
);
3372 * Restore the separator if we have one...
3384 * 'get_collection()' - Get a collection value from the current test file.
3387 static ipp_t
* /* O - Collection value */
3388 get_collection(_cups_vars_t
*vars
, /* I - Variables */
3389 FILE *fp
, /* I - File to read from */
3390 int *linenum
) /* IO - Line number */
3392 char token
[1024], /* Token from file */
3393 temp
[1024], /* Temporary string */
3394 attr
[128]; /* Attribute name */
3395 ipp_tag_t value
; /* Current value type */
3396 ipp_t
*col
= ippNew(); /* Collection value */
3397 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
3400 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
3402 if (!strcmp(token
, "}"))
3404 else if (!strcmp(token
, "{") && lastcol
)
3407 * Another collection value
3410 ipp_t
*subcol
= get_collection(vars
, fp
, linenum
);
3411 /* Collection value */
3415 ipp_attribute_t
*tempcol
; /* Pointer to new buffer */
3419 * Reallocate memory...
3422 if ((tempcol
= realloc(lastcol
, sizeof(ipp_attribute_t
) +
3423 (lastcol
->num_values
+ 1) *
3424 sizeof(_ipp_value_t
))) == NULL
)
3426 print_fatal_error("Unable to allocate memory on line %d.", *linenum
);
3430 if (tempcol
!= lastcol
)
3433 * Reset pointers in the list...
3437 col
->prev
->next
= tempcol
;
3439 col
->attrs
= tempcol
;
3441 lastcol
= col
->current
= col
->last
= tempcol
;
3444 lastcol
->values
[lastcol
->num_values
].collection
= subcol
;
3445 lastcol
->num_values
++;
3450 else if (!_cups_strcasecmp(token
, "MEMBER"))
3458 if (!get_token(fp
, token
, sizeof(token
), linenum
))
3460 print_fatal_error("Missing MEMBER value tag on line %d.", *linenum
);
3464 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
3466 print_fatal_error("Bad MEMBER value tag \"%s\" on line %d.", token
,
3471 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
3473 print_fatal_error("Missing MEMBER name on line %d.", *linenum
);
3477 if (!get_token(fp
, temp
, sizeof(temp
), linenum
))
3479 print_fatal_error("Missing MEMBER value on line %d.", *linenum
);
3483 expand_variables(vars
, token
, temp
, sizeof(token
));
3487 case IPP_TAG_BOOLEAN
:
3488 if (!_cups_strcasecmp(token
, "true"))
3489 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
3491 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, atoi(token
));
3494 case IPP_TAG_INTEGER
:
3496 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
3499 case IPP_TAG_RESOLUTION
:
3501 int xres
, /* X resolution */
3502 yres
; /* Y resolution */
3503 char units
[6]; /* Units */
3505 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
3506 (_cups_strcasecmp(units
, "dpi") &&
3507 _cups_strcasecmp(units
, "dpc") &&
3508 _cups_strcasecmp(units
, "dpcm") &&
3509 _cups_strcasecmp(units
, "other")))
3511 print_fatal_error("Bad resolution value \"%s\" on line %d.",
3516 if (!_cups_strcasecmp(units
, "dpi"))
3517 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
3519 else if (!_cups_strcasecmp(units
, "dpc") ||
3520 !_cups_strcasecmp(units
, "dpcm"))
3521 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
3524 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
3529 case IPP_TAG_RANGE
:
3531 int lowers
[4], /* Lower value */
3532 uppers
[4], /* Upper values */
3533 num_vals
; /* Number of values */
3536 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
3537 lowers
+ 0, uppers
+ 0,
3538 lowers
+ 1, uppers
+ 1,
3539 lowers
+ 2, uppers
+ 2,
3540 lowers
+ 3, uppers
+ 3);
3542 if ((num_vals
& 1) || num_vals
== 0)
3544 print_fatal_error("Bad rangeOfInteger value \"%s\" on line %d.",
3549 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
3554 case IPP_TAG_BEGIN_COLLECTION
:
3555 if (!strcmp(token
, "{"))
3557 ipp_t
*subcol
= get_collection(vars
, fp
, linenum
);
3558 /* Collection value */
3562 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
3570 print_fatal_error("Bad collection value on line %d.", *linenum
);
3574 case IPP_TAG_STRING
:
3575 ippAddOctetString(col
, IPP_TAG_ZERO
, attr
, token
, strlen(token
));
3579 if (!strchr(token
, ','))
3580 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
3584 * Multiple string values...
3587 int num_values
; /* Number of values */
3588 char *values
[100], /* Values */
3589 *ptr
; /* Pointer to next value */
3595 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
3598 values
[num_values
] = ptr
;
3602 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
3603 NULL
, (const char **)values
);
3613 * If we get here there was a parse error; free memory and return.
3625 * 'get_filename()' - Get a filename based on the current test file.
3628 static char * /* O - Filename */
3629 get_filename(const char *testfile
, /* I - Current test file */
3630 char *dst
, /* I - Destination filename */
3631 const char *src
, /* I - Source filename */
3632 size_t dstsize
) /* I - Size of destination buffer */
3634 char *dstptr
; /* Pointer into destination */
3635 _cups_globals_t
*cg
= _cupsGlobals();
3639 if (*src
== '<' && src
[strlen(src
) - 1] == '>')
3642 * Map <filename> to CUPS_DATADIR/ipptool/filename...
3645 snprintf(dst
, dstsize
, "%s/ipptool/%s", cg
->cups_datadir
, src
+ 1);
3646 dstptr
= dst
+ strlen(dst
) - 1;
3650 else if (*src
== '/' || !strchr(testfile
, '/')
3652 || (isalpha(*src
& 255) && src
[1] == ':')
3657 * Use the path as-is...
3660 strlcpy(dst
, src
, dstsize
);
3665 * Make path relative to testfile...
3668 strlcpy(dst
, testfile
, dstsize
);
3669 if ((dstptr
= strrchr(dst
, '/')) != NULL
)
3672 dstptr
= dst
; /* Should never happen */
3674 strlcpy(dstptr
, src
, dstsize
- (dstptr
- dst
));
3682 * 'get_token()' - Get a token from a file.
3685 static char * /* O - Token from file or NULL on EOF */
3686 get_token(FILE *fp
, /* I - File to read from */
3687 char *buf
, /* I - Buffer to read into */
3688 int buflen
, /* I - Length of buffer */
3689 int *linenum
) /* IO - Current line number */
3691 int ch
, /* Character from file */
3692 quote
; /* Quoting character */
3693 char *bufptr
, /* Pointer into buffer */
3694 *bufend
; /* End of buffer */
3700 * Skip whitespace...
3703 while (isspace(ch
= getc(fp
)))
3715 else if (ch
== '\'' || ch
== '\"')
3718 * Quoted text or regular expression...
3723 bufend
= buf
+ buflen
- 1;
3725 while ((ch
= getc(fp
)) != EOF
)
3730 * Escape next character...
3733 if (bufptr
< bufend
)
3736 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
3739 else if (ch
== quote
)
3741 else if (bufptr
< bufend
)
3755 while ((ch
= getc(fp
)) != EOF
)
3764 * Whitespace delimited text...
3770 bufend
= buf
+ buflen
- 1;
3772 while ((ch
= getc(fp
)) != EOF
)
3773 if (isspace(ch
) || ch
== '#')
3775 else if (bufptr
< bufend
)
3780 else if (ch
== '\n')
3792 * 'get_variable()' - Get the value of a variable.
3795 static char * /* O - Value or NULL */
3796 get_variable(_cups_vars_t
*vars
, /* I - Variables */
3797 const char *name
) /* I - Variable name */
3799 _cups_var_t key
, /* Search key */
3800 *match
; /* Matching variable, if any */
3803 key
.name
= (char *)name
;
3804 match
= cupsArrayFind(vars
->vars
, &key
);
3806 return (match
? match
->value
: NULL
);
3811 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
3815 static char * /* O - ISO 8601 date/time string */
3816 iso_date(ipp_uchar_t
*date
) /* I - IPP (RFC 1903) date/time value */
3818 time_t utctime
; /* UTC time since 1970 */
3819 struct tm
*utcdate
; /* UTC date/time */
3820 static char buffer
[255]; /* String buffer */
3823 utctime
= ippDateToTime(date
);
3824 utcdate
= gmtime(&utctime
);
3826 snprintf(buffer
, sizeof(buffer
), "%04d-%02d-%02dT%02d:%02d:%02dZ",
3827 utcdate
->tm_year
+ 1900, utcdate
->tm_mon
+ 1, utcdate
->tm_mday
,
3828 utcdate
->tm_hour
, utcdate
->tm_min
, utcdate
->tm_sec
);
3835 * 'password_cb()' - Password callback for authenticated tests.
3838 static const char * /* O - Password */
3839 password_cb(const char *prompt
) /* I - Prompt (unused) */
3848 * 'print_attr()' - Print an attribute on the screen.
3852 print_attr(ipp_attribute_t
*attr
, /* I - Attribute to print */
3853 ipp_tag_t
*group
) /* IO - Current group */
3855 int i
; /* Looping var */
3856 ipp_attribute_t
*colattr
; /* Collection attribute */
3859 if (Output
== _CUPS_OUTPUT_PLIST
)
3861 if (!attr
->name
|| (group
&& *group
!= attr
->group_tag
))
3863 if (attr
->group_tag
!= IPP_TAG_ZERO
)
3870 *group
= attr
->group_tag
;
3876 print_xml_string("key", attr
->name
);
3877 if (attr
->num_values
> 1)
3880 switch (attr
->value_tag
)
3882 case IPP_TAG_INTEGER
:
3884 for (i
= 0; i
< attr
->num_values
; i
++)
3885 if (Output
== _CUPS_OUTPUT_PLIST
)
3886 printf("<integer>%d</integer>\n", attr
->values
[i
].integer
);
3888 printf("%d ", attr
->values
[i
].integer
);
3891 case IPP_TAG_BOOLEAN
:
3892 for (i
= 0; i
< attr
->num_values
; i
++)
3893 if (Output
== _CUPS_OUTPUT_PLIST
)
3894 puts(attr
->values
[i
].boolean
? "<true />" : "<false />");
3895 else if (attr
->values
[i
].boolean
)
3896 fputs("true ", stdout
);
3898 fputs("false ", stdout
);
3901 case IPP_TAG_RANGE
:
3902 for (i
= 0; i
< attr
->num_values
; i
++)
3903 if (Output
== _CUPS_OUTPUT_PLIST
)
3904 printf("<dict><key>lower</key><integer>%d</integer>"
3905 "<key>upper</key><integer>%d</integer></dict>\n",
3906 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
3908 printf("%d-%d ", attr
->values
[i
].range
.lower
,
3909 attr
->values
[i
].range
.upper
);
3912 case IPP_TAG_RESOLUTION
:
3913 for (i
= 0; i
< attr
->num_values
; i
++)
3914 if (Output
== _CUPS_OUTPUT_PLIST
)
3915 printf("<dict><key>xres</key><integer>%d</integer>"
3916 "<key>yres</key><integer>%d</integer>"
3917 "<key>units</key><string>%s</string></dict>\n",
3918 attr
->values
[i
].resolution
.xres
,
3919 attr
->values
[i
].resolution
.yres
,
3920 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
3923 printf("%dx%d%s ", attr
->values
[i
].resolution
.xres
,
3924 attr
->values
[i
].resolution
.yres
,
3925 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
3930 for (i
= 0; i
< attr
->num_values
; i
++)
3931 if (Output
== _CUPS_OUTPUT_PLIST
)
3932 printf("<date>%s</date>\n", iso_date(attr
->values
[i
].date
));
3934 printf("%s ", iso_date(attr
->values
[i
].date
));
3937 case IPP_TAG_STRING
:
3938 for (i
= 0; i
< attr
->num_values
; i
++)
3940 if (Output
== _CUPS_OUTPUT_PLIST
)
3942 char buffer
[IPP_MAX_LENGTH
* 5 / 4 + 1];
3945 printf("<data>%s</data>\n",
3946 httpEncode64_2(buffer
, sizeof(buffer
),
3947 attr
->values
[i
].unknown
.data
,
3948 attr
->values
[i
].unknown
.length
));
3952 char *ptr
, /* Pointer into data */
3953 *end
; /* End of data */
3956 for (ptr
= attr
->values
[i
].unknown
.data
,
3957 end
= ptr
+ attr
->values
[i
].unknown
.length
;
3961 if (*ptr
== '\\' || *ptr
== '\"')
3962 printf("\\%c", *ptr
);
3963 else if (!isprint(*ptr
& 255))
3964 printf("\\%03o", *ptr
& 255);
3975 case IPP_TAG_KEYWORD
:
3976 case IPP_TAG_CHARSET
:
3978 case IPP_TAG_MIMETYPE
:
3979 case IPP_TAG_LANGUAGE
:
3980 for (i
= 0; i
< attr
->num_values
; i
++)
3981 if (Output
== _CUPS_OUTPUT_PLIST
)
3982 print_xml_string("string", attr
->values
[i
].string
.text
);
3984 printf("\"%s\" ", attr
->values
[i
].string
.text
);
3987 case IPP_TAG_TEXTLANG
:
3988 case IPP_TAG_NAMELANG
:
3989 for (i
= 0; i
< attr
->num_values
; i
++)
3990 if (Output
== _CUPS_OUTPUT_PLIST
)
3992 fputs("<dict><key>language</key><string>", stdout
);
3993 print_xml_string(NULL
, attr
->values
[i
].string
.language
);
3994 fputs("</string><key>string</key><string>", stdout
);
3995 print_xml_string(NULL
, attr
->values
[i
].string
.text
);
3996 puts("</string></dict>");
3999 printf("\"%s\"[%s] ", attr
->values
[i
].string
.text
,
4000 attr
->values
[i
].string
.language
);
4003 case IPP_TAG_BEGIN_COLLECTION
:
4004 for (i
= 0; i
< attr
->num_values
; i
++)
4006 if (Output
== _CUPS_OUTPUT_PLIST
)
4009 for (colattr
= attr
->values
[i
].collection
->attrs
;
4011 colattr
= colattr
->next
)
4012 print_attr(colattr
, NULL
);
4020 print_col(attr
->values
[i
].collection
);
4026 if (Output
== _CUPS_OUTPUT_PLIST
)
4027 printf("<string><<%s>></string>\n",
4028 ippTagString(attr
->value_tag
));
4030 fputs(ippTagString(attr
->value_tag
), stdout
);
4034 if (attr
->num_values
> 1)
4039 char buffer
[8192]; /* Value buffer */
4041 if (Output
== _CUPS_OUTPUT_TEST
)
4045 puts(" -- separator --");
4049 printf(" %s (%s%s) = ", attr
->name
,
4050 attr
->num_values
> 1 ? "1setOf " : "",
4051 ippTagString(attr
->value_tag
));
4054 ippAttributeString(attr
, buffer
, sizeof(buffer
));
4061 * 'print_col()' - Print a collection attribute on the screen.
4065 print_col(ipp_t
*col
) /* I - Collection attribute to print */
4067 int i
; /* Looping var */
4068 ipp_attribute_t
*attr
; /* Current attribute in collection */
4071 fputs("{ ", stdout
);
4072 for (attr
= col
->attrs
; attr
; attr
= attr
->next
)
4074 printf("%s (%s%s) = ", attr
->name
, attr
->num_values
> 1 ? "1setOf " : "",
4075 ippTagString(attr
->value_tag
));
4077 switch (attr
->value_tag
)
4079 case IPP_TAG_INTEGER
:
4081 for (i
= 0; i
< attr
->num_values
; i
++)
4082 printf("%d ", attr
->values
[i
].integer
);
4085 case IPP_TAG_BOOLEAN
:
4086 for (i
= 0; i
< attr
->num_values
; i
++)
4087 if (attr
->values
[i
].boolean
)
4093 case IPP_TAG_NOVALUE
:
4097 case IPP_TAG_RANGE
:
4098 for (i
= 0; i
< attr
->num_values
; i
++)
4099 printf("%d-%d ", attr
->values
[i
].range
.lower
,
4100 attr
->values
[i
].range
.upper
);
4103 case IPP_TAG_RESOLUTION
:
4104 for (i
= 0; i
< attr
->num_values
; i
++)
4105 printf("%dx%d%s ", attr
->values
[i
].resolution
.xres
,
4106 attr
->values
[i
].resolution
.yres
,
4107 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
4111 case IPP_TAG_STRING
:
4114 case IPP_TAG_KEYWORD
:
4115 case IPP_TAG_CHARSET
:
4117 case IPP_TAG_MIMETYPE
:
4118 case IPP_TAG_LANGUAGE
:
4119 for (i
= 0; i
< attr
->num_values
; i
++)
4120 printf("\"%s\" ", attr
->values
[i
].string
.text
);
4123 case IPP_TAG_TEXTLANG
:
4124 case IPP_TAG_NAMELANG
:
4125 for (i
= 0; i
< attr
->num_values
; i
++)
4126 printf("\"%s\"[%s] ", attr
->values
[i
].string
.text
,
4127 attr
->values
[i
].string
.language
);
4130 case IPP_TAG_BEGIN_COLLECTION
:
4131 for (i
= 0; i
< attr
->num_values
; i
++)
4133 print_col(attr
->values
[i
].collection
);
4139 break; /* anti-compiler-warning-code */
4148 * 'print_csv()' - Print a line of CSV text.
4153 ipp_attribute_t
*attr
, /* I - First attribute for line */
4154 int num_displayed
, /* I - Number of attributes to display */
4155 char **displayed
, /* I - Attributes to display */
4156 size_t *widths
) /* I - Column widths */
4158 int i
; /* Looping var */
4159 size_t maxlength
; /* Max length of all columns */
4160 char *buffer
, /* String buffer */
4161 *bufptr
; /* Pointer into buffer */
4162 ipp_attribute_t
*current
; /* Current attribute */
4166 * Get the maximum string length we have to show and allocate...
4169 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4170 if (widths
[i
] > maxlength
)
4171 maxlength
= widths
[i
];
4175 if ((buffer
= malloc(maxlength
)) == NULL
)
4179 * Loop through the attributes to display...
4184 for (i
= 0; i
< num_displayed
; i
++)
4191 for (current
= attr
; current
; current
= current
->next
)
4195 else if (!strcmp(current
->name
, displayed
[i
]))
4197 ippAttributeString(current
, buffer
, maxlength
);
4202 if (strchr(buffer
, ',') != NULL
|| strchr(buffer
, '\"') != NULL
||
4203 strchr(buffer
, '\\') != NULL
)
4206 for (bufptr
= buffer
; *bufptr
; bufptr
++)
4208 if (*bufptr
== '\\' || *bufptr
== '\"')
4215 fputs(buffer
, stdout
);
4221 for (i
= 0; i
< num_displayed
; i
++)
4226 fputs(displayed
[i
], stdout
);
4236 * 'print_fatal_error()' - Print a fatal error message.
4240 print_fatal_error(const char *s
, /* I - Printf-style format string */
4241 ...) /* I - Additional arguments as needed */
4243 char buffer
[10240]; /* Format buffer */
4244 va_list ap
; /* Pointer to arguments */
4248 * Format the error message...
4252 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
4259 if (Output
== _CUPS_OUTPUT_PLIST
)
4262 print_xml_trailer(0, buffer
);
4265 _cupsLangPrintf(stderr
, "ipptool: %s", buffer
);
4270 * 'print_line()' - Print a line of formatted or CSV text.
4275 ipp_attribute_t
*attr
, /* I - First attribute for line */
4276 int num_displayed
, /* I - Number of attributes to display */
4277 char **displayed
, /* I - Attributes to display */
4278 size_t *widths
) /* I - Column widths */
4280 int i
; /* Looping var */
4281 size_t maxlength
; /* Max length of all columns */
4282 char *buffer
; /* String buffer */
4283 ipp_attribute_t
*current
; /* Current attribute */
4287 * Get the maximum string length we have to show and allocate...
4290 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4291 if (widths
[i
] > maxlength
)
4292 maxlength
= widths
[i
];
4296 if ((buffer
= malloc(maxlength
)) == NULL
)
4300 * Loop through the attributes to display...
4305 for (i
= 0; i
< num_displayed
; i
++)
4312 for (current
= attr
; current
; current
= current
->next
)
4316 else if (!strcmp(current
->name
, displayed
[i
]))
4318 ippAttributeString(current
, buffer
, maxlength
);
4323 printf("%*s", (int)-widths
[i
], buffer
);
4329 for (i
= 0; i
< num_displayed
; i
++)
4334 printf("%*s", (int)-widths
[i
], displayed
[i
]);
4338 for (i
= 0; i
< num_displayed
; i
++)
4343 memset(buffer
, '-', widths
[i
]);
4344 buffer
[widths
[i
]] = '\0';
4345 fputs(buffer
, stdout
);
4355 * 'print_xml_header()' - Print a standard XML plist header.
4359 print_xml_header(void)
4363 puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
4364 puts("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
4365 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
4366 puts("<plist version=\"1.0\">");
4368 puts("<key>Transfer</key>");
4369 printf("<string>%s</string>\n",
4370 Transfer
== _CUPS_TRANSFER_AUTO
? "auto" :
4371 Transfer
== _CUPS_TRANSFER_CHUNKED
? "chunked" : "length");
4372 puts("<key>Tests</key>");
4381 * 'print_xml_string()' - Print an XML string with escaping.
4385 print_xml_string(const char *element
, /* I - Element name or NULL */
4386 const char *s
) /* I - String to print */
4389 printf("<%s>", element
);
4394 fputs("&", stdout
);
4396 fputs("<", stdout
);
4398 fputs(">", stdout
);
4399 else if ((*s
& 0xe0) == 0xc0)
4402 * Validate UTF-8 two-byte sequence...
4405 if ((s
[1] & 0xc0) != 0x80)
4416 else if ((*s
& 0xf0) == 0xe0)
4419 * Validate UTF-8 three-byte sequence...
4422 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80)
4434 else if ((*s
& 0xf8) == 0xf0)
4437 * Validate UTF-8 four-byte sequence...
4440 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80 ||
4441 (s
[3] & 0xc0) != 0x80)
4454 else if ((*s
& 0x80) || (*s
< ' ' && !isspace(*s
& 255)))
4457 * Invalid control character...
4469 printf("</%s>\n", element
);
4474 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
4478 print_xml_trailer(int success
, /* I - 1 on success, 0 on failure */
4479 const char *message
) /* I - Error message or NULL */
4484 puts("<key>Successful</key>");
4485 puts(success
? "<true />" : "<false />");
4488 puts("<key>ErrorMessage</key>");
4489 print_xml_string("string", message
);
4500 * 'set_variable()' - Set a variable value.
4504 set_variable(_cups_vars_t
*vars
, /* I - Variables */
4505 const char *name
, /* I - Variable name */
4506 const char *value
) /* I - Value string */
4508 _cups_var_t key
, /* Search key */
4509 *var
; /* New variable */
4512 if (!_cups_strcasecmp(name
, "filename"))
4515 free(vars
->filename
);
4517 vars
->filename
= strdup(value
);
4521 key
.name
= (char *)name
;
4522 if ((var
= cupsArrayFind(vars
->vars
, &key
)) != NULL
)
4525 var
->value
= strdup(value
);
4527 else if ((var
= malloc(sizeof(_cups_var_t
))) == NULL
)
4529 print_fatal_error("Unable to allocate memory for variable \"%s\".", name
);
4534 var
->name
= strdup(name
);
4535 var
->value
= strdup(value
);
4537 cupsArrayAdd(vars
->vars
, var
);
4544 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
4548 sigterm_handler(int sig
) /* I - Signal number (unused) */
4554 signal(SIGINT
, SIG_DFL
);
4555 signal(SIGTERM
, SIG_DFL
);
4561 * 'timeout_cb()' - Handle HTTP timeouts.
4564 static int /* O - 1 to continue, 0 to cancel */
4565 timeout_cb(http_t
*http
, /* I - Connection to server (unused) */
4566 void *user_data
) /* I - User data (unused) */
4571 /* Always cancel on timeout */
4577 * 'usage()' - Show program usage.
4583 _cupsLangPuts(stderr
, _("Usage: ipptool [options] URI filename [ ... "
4585 _cupsLangPuts(stderr
, _("Options:"));
4586 _cupsLangPuts(stderr
, _(" -4 Connect using IPv4."));
4587 _cupsLangPuts(stderr
, _(" -6 Connect using IPv6."));
4588 _cupsLangPuts(stderr
, _(" -C Send requests using "
4589 "chunking (default)."));
4590 _cupsLangPuts(stdout
, _(" -E Test with HTTP Upgrade to "
4592 _cupsLangPuts(stderr
, _(" -I Ignore errors."));
4593 _cupsLangPuts(stderr
, _(" -L Send requests using "
4594 "content-length."));
4595 _cupsLangPuts(stderr
, _(" -S Test with SSL "
4597 _cupsLangPuts(stderr
, _(" -T seconds Set the receive/send "
4598 "timeout in seconds."));
4599 _cupsLangPuts(stderr
, _(" -V version Set default IPP "
4601 _cupsLangPuts(stderr
, _(" -X Produce XML plist instead "
4603 _cupsLangPuts(stderr
, _(" -d name=value Set named variable to "
4605 _cupsLangPuts(stderr
, _(" -f filename Set default request "
4607 _cupsLangPuts(stderr
, _(" -i seconds Repeat the last file with "
4608 "the given time interval."));
4609 _cupsLangPuts(stderr
, _(" -n count Repeat the last file the "
4610 "given number of times."));
4611 _cupsLangPuts(stderr
, _(" -q Run silently."));
4612 _cupsLangPuts(stderr
, _(" -t Produce a test report."));
4613 _cupsLangPuts(stderr
, _(" -v Be verbose."));
4620 * 'validate_attr()' - Determine whether an attribute is valid.
4623 static int /* O - 1 if valid, 0 otherwise */
4624 validate_attr(cups_array_t
*errors
, /* I - Errors array */
4625 ipp_attribute_t
*attr
) /* I - Attribute to validate */
4627 int i
; /* Looping var */
4628 char scheme
[64], /* Scheme from URI */
4629 userpass
[256], /* Username/password from URI */
4630 hostname
[256], /* Hostname from URI */
4631 resource
[1024]; /* Resource from URI */
4632 int port
, /* Port number from URI */
4633 uri_status
, /* URI separation status */
4634 valid
= 1; /* Is the attribute valid? */
4635 const char *ptr
; /* Pointer into string */
4636 ipp_attribute_t
*colattr
; /* Collection attribute */
4637 regex_t re
; /* Regular expression */
4638 ipp_uchar_t
*date
; /* Current date value */
4649 * Validate the attribute name.
4652 for (ptr
= attr
->name
; *ptr
; ptr
++)
4653 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' && *ptr
!= '_')
4656 if (*ptr
|| ptr
== attr
->name
)
4661 "\"%s\": Bad attribute name - invalid character "
4662 "(RFC 2911 section 4.1.3).", attr
->name
);
4665 if ((ptr
- attr
->name
) > 255)
4670 "\"%s\": Bad attribute name - bad length "
4671 "(RFC 2911 section 4.1.3).", attr
->name
);
4674 switch (attr
->value_tag
)
4676 case IPP_TAG_INTEGER
:
4679 case IPP_TAG_BOOLEAN
:
4680 for (i
= 0; i
< attr
->num_values
; i
++)
4682 if (attr
->values
[i
].boolean
!= 0 &&
4683 attr
->values
[i
].boolean
!= 1)
4688 "\"%s\": Bad boolen value %d "
4689 "(RFC 2911 section 4.1.11).", attr
->name
,
4690 attr
->values
[i
].boolean
);
4696 for (i
= 0; i
< attr
->num_values
; i
++)
4698 if (attr
->values
[i
].integer
< 1)
4703 "\"%s\": Bad enum value %d - out of range "
4704 "(RFC 2911 section 4.1.4).", attr
->name
,
4705 attr
->values
[i
].integer
);
4710 case IPP_TAG_STRING
:
4711 for (i
= 0; i
< attr
->num_values
; i
++)
4713 if (attr
->values
[i
].unknown
.length
> IPP_MAX_OCTETSTRING
)
4718 "\"%s\": Bad octetString value - bad length %d "
4719 "(RFC 2911 section 4.1.10).", attr
->name
,
4720 attr
->values
[i
].unknown
.length
);
4726 for (i
= 0; i
< attr
->num_values
; i
++)
4728 date
= attr
->values
[i
].date
;
4730 if (date
[2] < 1 || date
[2] > 12)
4735 "\"%s\": Bad dateTime month %u "
4736 "(RFC 2911 section 4.1.14).", attr
->name
, date
[2]);
4739 if (date
[3] < 1 || date
[3] > 31)
4744 "\"%s\": Bad dateTime day %u "
4745 "(RFC 2911 section 4.1.14).", attr
->name
, date
[3]);
4753 "\"%s\": Bad dateTime hours %u "
4754 "(RFC 2911 section 4.1.14).", attr
->name
, date
[4]);
4762 "\"%s\": Bad dateTime minutes %u "
4763 "(RFC 2911 section 4.1.14).", attr
->name
, date
[5]);
4771 "\"%s\": Bad dateTime seconds %u "
4772 "(RFC 2911 section 4.1.14).", attr
->name
, date
[6]);
4780 "\"%s\": Bad dateTime deciseconds %u "
4781 "(RFC 2911 section 4.1.14).", attr
->name
, date
[7]);
4784 if (date
[8] != '-' && date
[8] != '+')
4789 "\"%s\": Bad dateTime UTC sign '%c' "
4790 "(RFC 2911 section 4.1.14).", attr
->name
, date
[8]);
4798 "\"%s\": Bad dateTime UTC hours %u "
4799 "(RFC 2911 section 4.1.14).", attr
->name
, date
[9]);
4807 "\"%s\": Bad dateTime UTC minutes %u "
4808 "(RFC 2911 section 4.1.14).", attr
->name
, date
[10]);
4813 case IPP_TAG_RESOLUTION
:
4814 for (i
= 0; i
< attr
->num_values
; i
++)
4816 if (attr
->values
[i
].resolution
.xres
<= 0)
4821 "\"%s\": Bad resolution value %dx%d%s - cross "
4822 "feed resolution must be positive "
4823 "(RFC 2911 section 4.1.15).", attr
->name
,
4824 attr
->values
[i
].resolution
.xres
,
4825 attr
->values
[i
].resolution
.yres
,
4826 attr
->values
[i
].resolution
.units
==
4827 IPP_RES_PER_INCH
? "dpi" :
4828 attr
->values
[i
].resolution
.units
==
4829 IPP_RES_PER_CM
? "dpcm" : "unknown");
4832 if (attr
->values
[i
].resolution
.yres
<= 0)
4837 "\"%s\": Bad resolution value %dx%d%s - feed "
4838 "resolution must be positive "
4839 "(RFC 2911 section 4.1.15).", attr
->name
,
4840 attr
->values
[i
].resolution
.xres
,
4841 attr
->values
[i
].resolution
.yres
,
4842 attr
->values
[i
].resolution
.units
==
4843 IPP_RES_PER_INCH
? "dpi" :
4844 attr
->values
[i
].resolution
.units
==
4845 IPP_RES_PER_CM
? "dpcm" : "unknown");
4848 if (attr
->values
[i
].resolution
.units
!= IPP_RES_PER_INCH
&&
4849 attr
->values
[i
].resolution
.units
!= IPP_RES_PER_CM
)
4854 "\"%s\": Bad resolution value %dx%d%s - bad "
4855 "units value (RFC 2911 section 4.1.15).",
4856 attr
->name
, attr
->values
[i
].resolution
.xres
,
4857 attr
->values
[i
].resolution
.yres
,
4858 attr
->values
[i
].resolution
.units
==
4859 IPP_RES_PER_INCH
? "dpi" :
4860 attr
->values
[i
].resolution
.units
==
4861 IPP_RES_PER_CM
? "dpcm" : "unknown");
4866 case IPP_TAG_RANGE
:
4867 for (i
= 0; i
< attr
->num_values
; i
++)
4869 if (attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
4874 "\"%s\": Bad rangeOfInteger value %d-%d - lower "
4875 "greater than upper (RFC 2911 section 4.1.13).",
4876 attr
->name
, attr
->values
[i
].range
.lower
,
4877 attr
->values
[i
].range
.upper
);
4882 case IPP_TAG_BEGIN_COLLECTION
:
4883 for (i
= 0; i
< attr
->num_values
; i
++)
4885 for (colattr
= attr
->values
[i
].collection
->attrs
;
4887 colattr
= colattr
->next
)
4889 if (!validate_attr(NULL
, colattr
))
4896 if (colattr
&& errors
)
4898 add_stringf(errors
, "\"%s\": Bad collection value.", attr
->name
);
4902 validate_attr(errors
, colattr
);
4903 colattr
= colattr
->next
;
4910 case IPP_TAG_TEXTLANG
:
4911 for (i
= 0; i
< attr
->num_values
; i
++)
4913 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4915 if ((*ptr
& 0xe0) == 0xc0)
4918 if ((*ptr
& 0xc0) != 0x80)
4921 else if ((*ptr
& 0xf0) == 0xe0)
4924 if ((*ptr
& 0xc0) != 0x80)
4927 if ((*ptr
& 0xc0) != 0x80)
4930 else if ((*ptr
& 0xf8) == 0xf0)
4933 if ((*ptr
& 0xc0) != 0x80)
4936 if ((*ptr
& 0xc0) != 0x80)
4939 if ((*ptr
& 0xc0) != 0x80)
4942 else if (*ptr
& 0x80)
4951 "\"%s\": Bad text value \"%s\" - bad UTF-8 "
4952 "sequence (RFC 2911 section 4.1.1).", attr
->name
,
4953 attr
->values
[i
].string
.text
);
4956 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_TEXT
- 1))
4961 "\"%s\": Bad text value \"%s\" - bad length %d "
4962 "(RFC 2911 section 4.1.1).", attr
->name
,
4963 attr
->values
[i
].string
.text
,
4964 (int)strlen(attr
->values
[i
].string
.text
));
4970 case IPP_TAG_NAMELANG
:
4971 for (i
= 0; i
< attr
->num_values
; i
++)
4973 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4975 if ((*ptr
& 0xe0) == 0xc0)
4978 if ((*ptr
& 0xc0) != 0x80)
4981 else if ((*ptr
& 0xf0) == 0xe0)
4984 if ((*ptr
& 0xc0) != 0x80)
4987 if ((*ptr
& 0xc0) != 0x80)
4990 else if ((*ptr
& 0xf8) == 0xf0)
4993 if ((*ptr
& 0xc0) != 0x80)
4996 if ((*ptr
& 0xc0) != 0x80)
4999 if ((*ptr
& 0xc0) != 0x80)
5002 else if (*ptr
& 0x80)
5011 "\"%s\": Bad name value \"%s\" - bad UTF-8 "
5012 "sequence (RFC 2911 section 4.1.2).", attr
->name
,
5013 attr
->values
[i
].string
.text
);
5016 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_NAME
- 1))
5021 "\"%s\": Bad name value \"%s\" - bad length %d "
5022 "(RFC 2911 section 4.1.2).", attr
->name
,
5023 attr
->values
[i
].string
.text
,
5024 (int)strlen(attr
->values
[i
].string
.text
));
5029 case IPP_TAG_KEYWORD
:
5030 for (i
= 0; i
< attr
->num_values
; i
++)
5032 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5033 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' &&
5037 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5042 "\"%s\": Bad keyword value \"%s\" - invalid "
5043 "character (RFC 2911 section 4.1.3).",
5044 attr
->name
, attr
->values
[i
].string
.text
);
5047 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_KEYWORD
- 1))
5052 "\"%s\": Bad keyword value \"%s\" - bad "
5053 "length %d (RFC 2911 section 4.1.3).",
5054 attr
->name
, attr
->values
[i
].string
.text
,
5055 (int)strlen(attr
->values
[i
].string
.text
));
5061 for (i
= 0; i
< attr
->num_values
; i
++)
5063 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
,
5064 attr
->values
[i
].string
.text
,
5065 scheme
, sizeof(scheme
),
5066 userpass
, sizeof(userpass
),
5067 hostname
, sizeof(hostname
),
5068 &port
, resource
, sizeof(resource
));
5070 if (uri_status
< HTTP_URI_OK
)
5075 "\"%s\": Bad URI value \"%s\" - %s "
5076 "(RFC 2911 section 4.1.5).", attr
->name
,
5077 attr
->values
[i
].string
.text
,
5078 URIStatusStrings
[uri_status
-
5079 HTTP_URI_OVERFLOW
]);
5082 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_URI
- 1))
5087 "\"%s\": Bad URI value \"%s\" - bad length %d "
5088 "(RFC 2911 section 4.1.5).", attr
->name
,
5089 attr
->values
[i
].string
.text
,
5090 (int)strlen(attr
->values
[i
].string
.text
));
5095 case IPP_TAG_URISCHEME
:
5096 for (i
= 0; i
< attr
->num_values
; i
++)
5098 ptr
= attr
->values
[i
].string
.text
;
5099 if (islower(*ptr
& 255))
5101 for (ptr
++; *ptr
; ptr
++)
5102 if (!islower(*ptr
& 255) && !isdigit(*ptr
& 255) &&
5103 *ptr
!= '+' && *ptr
!= '-' && *ptr
!= '.')
5107 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5112 "\"%s\": Bad uriScheme value \"%s\" - bad "
5113 "characters (RFC 2911 section 4.1.6).",
5114 attr
->name
, attr
->values
[i
].string
.text
);
5117 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_URISCHEME
- 1))
5122 "\"%s\": Bad uriScheme value \"%s\" - bad "
5123 "length %d (RFC 2911 section 4.1.6).",
5124 attr
->name
, attr
->values
[i
].string
.text
,
5125 (int)strlen(attr
->values
[i
].string
.text
));
5130 case IPP_TAG_CHARSET
:
5131 for (i
= 0; i
< attr
->num_values
; i
++)
5133 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5134 if (!isprint(*ptr
& 255) || isupper(*ptr
& 255) ||
5135 isspace(*ptr
& 255))
5138 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5143 "\"%s\": Bad charset value \"%s\" - bad "
5144 "characters (RFC 2911 section 4.1.7).",
5145 attr
->name
, attr
->values
[i
].string
.text
);
5148 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_CHARSET
- 1))
5153 "\"%s\": Bad charset value \"%s\" - bad "
5154 "length %d (RFC 2911 section 4.1.7).",
5155 attr
->name
, attr
->values
[i
].string
.text
,
5156 (int)strlen(attr
->values
[i
].string
.text
));
5161 case IPP_TAG_LANGUAGE
:
5163 * The following regular expression is derived from the ABNF for
5164 * language tags in RFC 4646. All I can say is that this is the
5165 * easiest way to check the values...
5168 if ((i
= regcomp(&re
,
5170 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
5172 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
5173 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
5174 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
5175 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
5176 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
5178 "x(-[a-z0-9]{1,8})+" /* privateuse */
5180 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
5182 REG_NOSUB
| REG_EXTENDED
)) != 0)
5184 char temp
[256]; /* Temporary error string */
5186 regerror(i
, &re
, temp
, sizeof(temp
));
5187 print_fatal_error("Unable to compile naturalLanguage regular "
5188 "expression: %s.", temp
);
5192 for (i
= 0; i
< attr
->num_values
; i
++)
5194 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5199 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5200 "characters (RFC 2911 section 4.1.8).",
5201 attr
->name
, attr
->values
[i
].string
.text
);
5204 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_LANGUAGE
- 1))
5209 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5210 "length %d (RFC 2911 section 4.1.8).",
5211 attr
->name
, attr
->values
[i
].string
.text
,
5212 (int)strlen(attr
->values
[i
].string
.text
));
5219 case IPP_TAG_MIMETYPE
:
5221 * The following regular expression is derived from the ABNF for
5222 * language tags in RFC 2045 and 4288. All I can say is that this is
5223 * the easiest way to check the values...
5226 if ((i
= regcomp(&re
,
5228 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
5230 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
5231 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
5232 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
5235 REG_NOSUB
| REG_EXTENDED
)) != 0)
5237 char temp
[256]; /* Temporary error string */
5239 regerror(i
, &re
, temp
, sizeof(temp
));
5240 print_fatal_error("Unable to compile mimeMediaType regular "
5241 "expression: %s.", temp
);
5245 for (i
= 0; i
< attr
->num_values
; i
++)
5247 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5252 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5253 "characters (RFC 2911 section 4.1.9).",
5254 attr
->name
, attr
->values
[i
].string
.text
);
5257 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_MIMETYPE
- 1))
5262 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5263 "length %d (RFC 2911 section 4.1.9).",
5264 attr
->name
, attr
->values
[i
].string
.text
,
5265 (int)strlen(attr
->values
[i
].string
.text
));
5281 * 'with_value()' - Test a WITH-VALUE predicate.
5284 static int /* O - 1 on match, 0 on non-match */
5285 with_value(cups_array_t
*errors
, /* I - Errors array */
5286 char *value
, /* I - Value string */
5287 int flags
, /* I - Flags for match */
5288 ipp_attribute_t
*attr
, /* I - Attribute to compare */
5289 char *matchbuf
, /* I - Buffer to hold matching value */
5290 size_t matchlen
) /* I - Length of match buffer */
5292 int i
, /* Looping var */
5294 char temp
[256], /* Temporary value string */
5295 *valptr
; /* Pointer into value */
5299 match
= (flags
& _CUPS_WITH_ALL
) ? 1 : 0;
5302 * NULL matches everything.
5305 if (!value
|| !*value
)
5309 * Compare the value string to the attribute value.
5312 switch (attr
->value_tag
)
5314 case IPP_TAG_INTEGER
:
5316 for (i
= 0; i
< attr
->num_values
; i
++)
5318 char op
, /* Comparison operator */
5319 *nextptr
; /* Next pointer */
5320 int intvalue
, /* Integer value */
5321 valmatch
= 0; /* Does the current value match? */
5325 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5326 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5327 *valptr
== '=' || *valptr
== '>')
5330 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5332 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5340 intvalue
= strtol(valptr
, &nextptr
, 0);
5341 if (nextptr
== valptr
)
5345 if ((op
== '=' && attr
->values
[i
].integer
== intvalue
) ||
5346 (op
== '<' && attr
->values
[i
].integer
< intvalue
) ||
5347 (op
== '>' && attr
->values
[i
].integer
> intvalue
))
5350 snprintf(matchbuf
, matchlen
, "%d",
5351 attr
->values
[i
].integer
);
5358 if (flags
& _CUPS_WITH_ALL
)
5373 if (!match
&& errors
)
5375 for (i
= 0; i
< attr
->num_values
; i
++)
5376 add_stringf(errors
, "GOT: %s=%d", attr
->name
,
5377 attr
->values
[i
].integer
);
5381 case IPP_TAG_RANGE
:
5382 for (i
= 0; i
< attr
->num_values
; i
++)
5384 char op
, /* Comparison operator */
5385 *nextptr
; /* Next pointer */
5386 int intvalue
, /* Integer value */
5387 valmatch
= 0; /* Does the current value match? */
5391 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5392 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5393 *valptr
== '=' || *valptr
== '>')
5396 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5398 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5406 intvalue
= strtol(valptr
, &nextptr
, 0);
5407 if (nextptr
== valptr
)
5411 if ((op
== '=' && (attr
->values
[i
].range
.lower
== intvalue
||
5412 attr
->values
[i
].range
.upper
== intvalue
)) ||
5413 (op
== '<' && attr
->values
[i
].range
.upper
< intvalue
) ||
5414 (op
== '>' && attr
->values
[i
].range
.upper
> intvalue
))
5417 snprintf(matchbuf
, matchlen
, "%d-%d",
5418 attr
->values
[0].range
.lower
,
5419 attr
->values
[0].range
.upper
);
5426 if (flags
& _CUPS_WITH_ALL
)
5441 if (!match
&& errors
)
5443 for (i
= 0; i
< attr
->num_values
; i
++)
5444 add_stringf(errors
, "GOT: %s=%d-%d", attr
->name
,
5445 attr
->values
[i
].range
.lower
,
5446 attr
->values
[i
].range
.upper
);
5450 case IPP_TAG_BOOLEAN
:
5451 for (i
= 0; i
< attr
->num_values
; i
++)
5453 if (!strcmp(value
, "true") == attr
->values
[i
].boolean
)
5456 strlcpy(matchbuf
, value
, matchlen
);
5458 if (!(flags
& _CUPS_WITH_ALL
))
5464 else if (flags
& _CUPS_WITH_ALL
)
5471 if (!match
&& errors
)
5473 for (i
= 0; i
< attr
->num_values
; i
++)
5474 add_stringf(errors
, "GOT: %s=%s", attr
->name
,
5475 attr
->values
[i
].boolean
? "true" : "false");
5479 case IPP_TAG_RESOLUTION
:
5480 for (i
= 0; i
< attr
->num_values
; i
++)
5482 if (attr
->values
[i
].resolution
.xres
==
5483 attr
->values
[i
].resolution
.yres
)
5484 snprintf(temp
, sizeof(temp
), "%d%s",
5485 attr
->values
[i
].resolution
.xres
,
5486 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5489 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5490 attr
->values
[i
].resolution
.xres
,
5491 attr
->values
[i
].resolution
.yres
,
5492 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5495 if (!strcmp(value
, temp
))
5498 strlcpy(matchbuf
, value
, matchlen
);
5500 if (!(flags
& _CUPS_WITH_ALL
))
5506 else if (flags
& _CUPS_WITH_ALL
)
5513 if (!match
&& errors
)
5515 for (i
= 0; i
< attr
->num_values
; i
++)
5517 if (attr
->values
[i
].resolution
.xres
==
5518 attr
->values
[i
].resolution
.yres
)
5519 snprintf(temp
, sizeof(temp
), "%d%s",
5520 attr
->values
[i
].resolution
.xres
,
5521 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5524 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5525 attr
->values
[i
].resolution
.xres
,
5526 attr
->values
[i
].resolution
.yres
,
5527 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5530 if (strcmp(value
, temp
))
5531 add_stringf(errors
, "GOT: %s=%s", attr
->name
, temp
);
5536 case IPP_TAG_NOVALUE
:
5537 case IPP_TAG_UNKNOWN
:
5540 case IPP_TAG_CHARSET
:
5541 case IPP_TAG_KEYWORD
:
5542 case IPP_TAG_LANGUAGE
:
5543 case IPP_TAG_MIMETYPE
:
5545 case IPP_TAG_NAMELANG
:
5547 case IPP_TAG_TEXTLANG
:
5549 case IPP_TAG_URISCHEME
:
5550 if (flags
& _CUPS_WITH_REGEX
)
5553 * Value is an extended, case-sensitive POSIX regular expression...
5556 regex_t re
; /* Regular expression */
5558 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
5560 regerror(i
, &re
, temp
, sizeof(temp
));
5562 print_fatal_error("Unable to compile WITH-VALUE regular expression "
5563 "\"%s\" - %s", value
, temp
);
5568 * See if ALL of the values match the given regular expression.
5571 for (i
= 0; i
< attr
->num_values
; i
++)
5573 if (!regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5576 strlcpy(matchbuf
, attr
->values
[i
].string
.text
, matchlen
);
5578 if (!(flags
& _CUPS_WITH_ALL
))
5584 else if (flags
& _CUPS_WITH_ALL
)
5596 * Value is a literal string, see if the value(s) match...
5599 for (i
= 0; i
< attr
->num_values
; i
++)
5601 if (!strcmp(value
, attr
->values
[i
].string
.text
))
5604 strlcpy(matchbuf
, attr
->values
[i
].string
.text
, matchlen
);
5606 if (!(flags
& _CUPS_WITH_ALL
))
5612 else if (flags
& _CUPS_WITH_ALL
)
5620 if (!match
&& errors
)
5622 for (i
= 0; i
< attr
->num_values
; i
++)
5623 add_stringf(errors
, "GOT: %s=\"%s\"", attr
->name
,
5624 attr
->values
[i
].string
.text
);