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>
61 #endif /* !O_BINARY */
68 typedef enum _cups_transfer_e
/**** How to send request data ****/
70 _CUPS_TRANSFER_AUTO
, /* Chunk for files, length for static */
71 _CUPS_TRANSFER_CHUNKED
, /* Chunk always */
72 _CUPS_TRANSFER_LENGTH
/* Length always */
75 typedef enum _cups_output_e
/**** Output mode ****/
77 _CUPS_OUTPUT_QUIET
, /* No output */
78 _CUPS_OUTPUT_TEST
, /* Traditional CUPS test output */
79 _CUPS_OUTPUT_PLIST
, /* XML plist test output */
80 _CUPS_OUTPUT_LIST
, /* Tabular list output */
81 _CUPS_OUTPUT_CSV
/* Comma-separated values output */
84 typedef enum _cups_with_e
/**** WITH flags ****/
86 _CUPS_WITH_LITERAL
= 0, /* Match string is a literal value */
87 _CUPS_WITH_ALL
= 1, /* Must match all values */
88 _CUPS_WITH_REGEX
= 2 /* Match string is a regular expression */
91 typedef struct _cups_expect_s
/**** Expected attribute info ****/
93 int optional
, /* Optional attribute? */
94 not_expect
; /* Don't expect attribute? */
95 char *name
, /* Attribute name */
96 *of_type
, /* Type name */
97 *same_count_as
, /* Parallel attribute name */
98 *if_defined
, /* Only required if variable defined */
99 *if_not_defined
, /* Only required if variable is not defined */
100 *with_value
, /* Attribute must include this value */
101 *define_match
, /* Variable to define on match */
102 *define_no_match
, /* Variable to define on no-match */
103 *define_value
; /* Variable to define with value */
104 int repeat_limit
, /* Maximum number of times to repeat */
105 repeat_match
, /* Repeat test on match */
106 repeat_no_match
, /* Repeat test on no match */
107 with_flags
, /* WITH flags */
108 count
; /* Expected count if > 0 */
109 ipp_tag_t in_group
; /* IN-GROUP value */
112 typedef struct _cups_status_s
/**** Status info ****/
114 ipp_status_t status
; /* Expected status code */
115 char *if_defined
, /* Only if variable is defined */
116 *if_not_defined
, /* Only if variable is not defined */
117 *define_match
, /* Variable to define on match */
118 *define_no_match
, /* Variable to define on no-match */
119 *define_value
; /* Variable to define with value */
120 int repeat_limit
, /* Maximum number of times to repeat */
121 repeat_match
, /* Repeat the test when it does not match */
122 repeat_no_match
; /* Repeat the test when it matches */
125 typedef struct _cups_var_s
/**** Variable ****/
127 char *name
, /* Name of variable */
128 *value
; /* Value of variable */
131 typedef struct _cups_vars_s
/**** Set of variables ****/
133 char *uri
, /* URI for printer */
134 *filename
, /* Filename */
135 scheme
[64], /* Scheme from URI */
136 userpass
[256], /* Username/password from URI */
137 hostname
[256], /* Hostname from URI */
138 resource
[1024]; /* Resource path from URI */
139 int port
; /* Port number from URI */
140 http_encryption_t encryption
; /* Encryption for connection? */
141 double timeout
; /* Timeout for connection */
142 int family
; /* Address family */
143 cups_array_t
*vars
; /* Array of variables */
151 _cups_transfer_t Transfer
= _CUPS_TRANSFER_AUTO
;
152 /* How to transfer requests */
153 _cups_output_t Output
= _CUPS_OUTPUT_LIST
;
155 int Cancel
= 0, /* Cancel test? */
156 IgnoreErrors
= 0, /* Ignore errors? */
157 Verbosity
= 0, /* Show all attributes? */
158 Version
= 11, /* Default IPP version */
159 XMLHeader
= 0, /* 1 if header is written */
160 TestCount
= 0, /* Number of tests run */
161 PassCount
= 0, /* Number of passing tests */
162 FailCount
= 0, /* Number of failing tests */
163 SkipCount
= 0; /* Number of skipped tests */
164 char *Password
= NULL
; /* Password from URI */
165 const char * const URIStatusStrings
[] = /* URI status strings */
168 "Bad arguments to function",
169 "Bad resource in URI",
170 "Bad port number in URI",
171 "Bad hostname/address in URI",
172 "Bad username in URI",
176 "Missing scheme in URI",
177 "Unknown scheme in URI",
178 "Missing resource in URI"
186 static void add_stringf(cups_array_t
*a
, const char *s
, ...)
187 __attribute__ ((__format__ (__printf__
, 2, 3)));
188 static int compare_vars(_cups_var_t
*a
, _cups_var_t
*b
);
189 static int do_tests(_cups_vars_t
*vars
, const char *testfile
);
190 static void expand_variables(_cups_vars_t
*vars
, char *dst
, const char *src
,
191 size_t dstsize
) __attribute__((nonnull(1,2,3)));
192 static int expect_matches(_cups_expect_t
*expect
, ipp_tag_t value_tag
);
193 static ipp_t
*get_collection(_cups_vars_t
*vars
, FILE *fp
, int *linenum
);
194 static char *get_filename(const char *testfile
, char *dst
, const char *src
,
196 static char *get_token(FILE *fp
, char *buf
, int buflen
,
198 static char *get_variable(_cups_vars_t
*vars
, const char *name
);
199 static char *iso_date(ipp_uchar_t
*date
);
200 static const char *password_cb(const char *prompt
);
201 static void print_attr(ipp_attribute_t
*attr
, ipp_tag_t
*group
);
202 static void print_col(ipp_t
*col
);
203 static void print_csv(ipp_attribute_t
*attr
, int num_displayed
,
204 char **displayed
, size_t *widths
);
205 static void print_fatal_error(const char *s
, ...)
206 __attribute__ ((__format__ (__printf__
, 1, 2)));
207 static void print_line(ipp_attribute_t
*attr
, int num_displayed
,
208 char **displayed
, size_t *widths
);
209 static void print_xml_header(void);
210 static void print_xml_string(const char *element
, const char *s
);
211 static void print_xml_trailer(int success
, const char *message
);
212 static void set_variable(_cups_vars_t
*vars
, const char *name
,
215 static void sigterm_handler(int sig
);
217 static int timeout_cb(http_t
*http
, void *user_data
);
218 static void usage(void) __attribute__((noreturn
));
219 static int validate_attr(cups_array_t
*errors
, ipp_attribute_t
*attr
);
220 static int with_value(cups_array_t
*errors
, char *value
, int flags
,
221 ipp_attribute_t
*attr
, char *matchbuf
,
226 * 'main()' - Parse options and do tests.
229 int /* O - Exit status */
230 main(int argc
, /* I - Number of command-line args */
231 char *argv
[]) /* I - Command-line arguments */
233 int i
; /* Looping var */
234 int status
; /* Status of tests... */
235 char *opt
, /* Current option */
236 name
[1024], /* Name/value buffer */
237 *value
, /* Pointer to value */
238 filename
[1024], /* Real filename */
239 testname
[1024], /* Real test filename */
240 uri
[1024]; /* Copy of printer URI */
241 const char *ext
, /* Extension on filename */
242 *testfile
; /* Test file to use */
243 int interval
, /* Test interval in microseconds */
244 repeat
; /* Repeat count */
245 _cups_vars_t vars
; /* Variables */
246 http_uri_status_t uri_status
; /* URI separation status */
247 _cups_globals_t
*cg
= _cupsGlobals();
253 * Catch SIGINT and SIGTERM...
256 signal(SIGINT
, sigterm_handler
);
257 signal(SIGTERM
, sigterm_handler
);
261 * Initialize the locale and variables...
264 _cupsSetLocale(argv
);
266 memset(&vars
, 0, sizeof(vars
));
267 vars
.family
= AF_UNSPEC
;
268 vars
.vars
= cupsArrayNew((cups_array_func_t
)compare_vars
, NULL
);
273 * ipptool URI testfile
281 for (i
= 1; i
< argc
; i
++)
283 if (argv
[i
][0] == '-')
285 for (opt
= argv
[i
] + 1; *opt
; opt
++)
289 case '4' : /* Connect using IPv4 only */
290 vars
.family
= AF_INET
;
294 case '6' : /* Connect using IPv6 only */
295 vars
.family
= AF_INET6
;
297 #endif /* AF_INET6 */
299 case 'C' : /* Enable HTTP chunking */
300 Transfer
= _CUPS_TRANSFER_CHUNKED
;
303 case 'E' : /* Encrypt with TLS */
305 vars
.encryption
= HTTP_ENCRYPT_REQUIRED
;
307 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
309 #endif /* HAVE_SSL */
312 case 'I' : /* Ignore errors */
316 case 'L' : /* Disable HTTP chunking */
317 Transfer
= _CUPS_TRANSFER_LENGTH
;
320 case 'S' : /* Encrypt with SSL */
322 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
324 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
326 #endif /* HAVE_SSL */
329 case 'T' : /* Set timeout */
334 _cupsLangPuts(stderr
,
335 _("ipptool: Missing timeout for \"-T\"."));
339 vars
.timeout
= _cupsStrScand(argv
[i
], NULL
, localeconv());
342 case 'V' : /* Set IPP version */
347 _cupsLangPuts(stderr
,
348 _("ipptool: Missing version for \"-V\"."));
352 if (!strcmp(argv
[i
], "1.0"))
354 else if (!strcmp(argv
[i
], "1.1"))
356 else if (!strcmp(argv
[i
], "2.0"))
358 else if (!strcmp(argv
[i
], "2.1"))
360 else if (!strcmp(argv
[i
], "2.2"))
364 _cupsLangPrintf(stderr
,
365 _("ipptool: Bad version %s for \"-V\"."),
371 case 'X' : /* Produce XML output */
372 Output
= _CUPS_OUTPUT_PLIST
;
374 if (interval
|| repeat
)
376 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are "
377 "incompatible with -X\"."));
382 case 'c' : /* CSV output */
383 Output
= _CUPS_OUTPUT_CSV
;
386 case 'd' : /* Define a variable */
391 _cupsLangPuts(stderr
,
392 _("ipptool: Missing name=value for \"-d\"."));
396 strlcpy(name
, argv
[i
], sizeof(name
));
397 if ((value
= strchr(name
, '=')) != NULL
)
400 value
= name
+ strlen(name
);
402 set_variable(&vars
, name
, value
);
405 case 'f' : /* Set the default test filename */
410 _cupsLangPuts(stderr
,
411 _("ipptool: Missing filename for \"-f\"."));
418 if (access(argv
[i
], 0))
424 snprintf(filename
, sizeof(filename
), "%s.gz", argv
[i
]);
425 if (access(filename
, 0) && filename
[0] != '/'
427 && (!isalpha(filename
[0] & 255) || filename
[1] != ':')
431 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s",
432 cg
->cups_datadir
, argv
[i
]);
433 if (access(filename
, 0))
435 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s.gz",
436 cg
->cups_datadir
, argv
[i
]);
437 if (access(filename
, 0))
438 vars
.filename
= strdup(argv
[i
]);
441 vars
.filename
= strdup(filename
);
444 vars
.filename
= strdup(filename
);
447 vars
.filename
= strdup(argv
[i
]);
449 if ((ext
= strrchr(vars
.filename
, '.')) != NULL
)
452 * Guess the MIME media type based on the extension...
455 if (!_cups_strcasecmp(ext
, ".gif"))
456 set_variable(&vars
, "filetype", "image/gif");
457 else if (!_cups_strcasecmp(ext
, ".htm") ||
458 !_cups_strcasecmp(ext
, ".htm.gz") ||
459 !_cups_strcasecmp(ext
, ".html") ||
460 !_cups_strcasecmp(ext
, ".html.gz"))
461 set_variable(&vars
, "filetype", "text/html");
462 else if (!_cups_strcasecmp(ext
, ".jpg"))
463 set_variable(&vars
, "filetype", "image/jpeg");
464 else if (!_cups_strcasecmp(ext
, ".pcl") ||
465 !_cups_strcasecmp(ext
, ".pcl.gz"))
466 set_variable(&vars
, "filetype", "application/vnd.hp-PCL");
467 else if (!_cups_strcasecmp(ext
, ".pdf"))
468 set_variable(&vars
, "filetype", "application/pdf");
469 else if (!_cups_strcasecmp(ext
, ".png"))
470 set_variable(&vars
, "filetype", "image/png");
471 else if (!_cups_strcasecmp(ext
, ".ps") ||
472 !_cups_strcasecmp(ext
, ".ps.gz"))
473 set_variable(&vars
, "filetype", "application/postscript");
474 else if (!_cups_strcasecmp(ext
, ".ras") ||
475 !_cups_strcasecmp(ext
, ".ras.gz"))
476 set_variable(&vars
, "filetype", "image/pwg-raster");
477 else if (!_cups_strcasecmp(ext
, ".txt") ||
478 !_cups_strcasecmp(ext
, ".txt.gz"))
479 set_variable(&vars
, "filetype", "text/plain");
480 else if (!_cups_strcasecmp(ext
, ".xps"))
481 set_variable(&vars
, "filetype", "application/openxps");
483 set_variable(&vars
, "filetype", "application/octet-stream");
488 * Use the "auto-type" MIME media type...
491 set_variable(&vars
, "filetype", "application/octet-stream");
495 case 'i' : /* Test every N seconds */
500 _cupsLangPuts(stderr
,
501 _("ipptool: Missing seconds for \"-i\"."));
506 interval
= (int)(_cupsStrScand(argv
[i
], NULL
, localeconv()) *
510 _cupsLangPuts(stderr
,
511 _("ipptool: Invalid seconds for \"-i\"."));
516 if (Output
== _CUPS_OUTPUT_PLIST
&& interval
)
518 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are "
519 "incompatible with -X\"."));
524 case 'l' : /* List as a table */
525 Output
= _CUPS_OUTPUT_LIST
;
528 case 'n' : /* Repeat count */
533 _cupsLangPuts(stderr
,
534 _("ipptool: Missing count for \"-n\"."));
538 repeat
= atoi(argv
[i
]);
540 if (Output
== _CUPS_OUTPUT_PLIST
&& repeat
)
542 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are "
543 "incompatible with -X\"."));
548 case 'q' : /* Be quiet */
549 Output
= _CUPS_OUTPUT_QUIET
;
552 case 't' : /* CUPS test output */
553 Output
= _CUPS_OUTPUT_TEST
;
556 case 'v' : /* Be verbose */
561 _cupsLangPrintf(stderr
, _("ipptool: Unknown option \"-%c\"."),
568 else if (!strncmp(argv
[i
], "ipp://", 6) || !strncmp(argv
[i
], "http://", 7)
570 || !strncmp(argv
[i
], "ipps://", 7)
571 || !strncmp(argv
[i
], "https://", 8)
572 #endif /* HAVE_SSL */
581 _cupsLangPuts(stderr
, _("ipptool: May only specify a single URI."));
586 if (!strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8))
587 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
588 #endif /* HAVE_SSL */
591 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, vars
.uri
,
592 vars
.scheme
, sizeof(vars
.scheme
),
593 vars
.userpass
, sizeof(vars
.userpass
),
594 vars
.hostname
, sizeof(vars
.hostname
),
596 vars
.resource
, sizeof(vars
.resource
));
598 if (uri_status
!= HTTP_URI_OK
)
600 _cupsLangPrintf(stderr
, _("ipptool: Bad URI - %s."),
601 URIStatusStrings
[uri_status
- HTTP_URI_OVERFLOW
]);
605 if (vars
.userpass
[0])
607 if ((Password
= strchr(vars
.userpass
, ':')) != NULL
)
610 cupsSetUser(vars
.userpass
);
611 cupsSetPasswordCB(password_cb
);
612 set_variable(&vars
, "uriuser", vars
.userpass
);
615 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), vars
.scheme
, NULL
,
616 vars
.hostname
, vars
.port
, vars
.resource
);
627 _cupsLangPuts(stderr
, _("ipptool: URI required before test file."));
631 if (access(argv
[i
], 0) && argv
[i
][0] != '/'
633 && (!isalpha(argv
[i
][0] & 255) || argv
[i
][1] != ':')
637 snprintf(testname
, sizeof(testname
), "%s/ipptool/%s", cg
->cups_datadir
,
639 if (access(testname
, 0))
647 if (!do_tests(&vars
, testfile
))
652 if (!vars
.uri
|| !testfile
)
656 * Loop if the interval is set...
659 if (Output
== _CUPS_OUTPUT_PLIST
)
660 print_xml_trailer(!status
, NULL
);
661 else if (interval
> 0 && repeat
> 0)
666 do_tests(&vars
, testfile
);
670 else if (interval
> 0)
675 do_tests(&vars
, testfile
);
678 else if (Output
== _CUPS_OUTPUT_TEST
&& TestCount
> 1)
681 * Show a summary report if there were multiple tests...
684 printf("\nSummary: %d tests, %d passed, %d failed, %d skipped\n"
685 "Score: %d%%\n", TestCount
, PassCount
, FailCount
, SkipCount
,
686 100 * (PassCount
+ SkipCount
) / TestCount
);
698 * 'add_stringf()' - Add a formatted string to an array.
702 add_stringf(cups_array_t
*a
, /* I - Array */
703 const char *s
, /* I - Printf-style format string */
704 ...) /* I - Additional args as needed */
706 char buffer
[10240]; /* Format buffer */
707 va_list ap
; /* Argument pointer */
711 * Don't bother is the array is NULL...
718 * Format the message...
722 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
726 * Add it to the array...
729 cupsArrayAdd(a
, buffer
);
734 * 'compare_vars()' - Compare two variables.
737 static int /* O - Result of comparison */
738 compare_vars(_cups_var_t
*a
, /* I - First variable */
739 _cups_var_t
*b
) /* I - Second variable */
741 return (_cups_strcasecmp(a
->name
, b
->name
));
746 * 'do_tests()' - Do tests as specified in the test file.
749 static int /* 1 = success, 0 = failure */
750 do_tests(_cups_vars_t
*vars
, /* I - Variables */
751 const char *testfile
) /* I - Test file to use */
753 int i
, /* Looping var */
754 linenum
, /* Current line number */
755 pass
, /* Did we pass the test? */
756 prev_pass
= 1, /* Did we pass the previous test? */
757 request_id
, /* Current request ID */
758 show_header
= 1, /* Show the test header? */
759 ignore_errors
, /* Ignore test failures? */
760 skip_previous
= 0, /* Skip on previous test failure? */
761 repeat_count
, /* Repeat count */
762 repeat_interval
, /* Repeat interval */
763 repeat_prev
, /* Previous repeat interval */
764 repeat_test
; /* Repeat a test? */
765 http_t
*http
= NULL
; /* HTTP connection to server */
766 FILE *fp
= NULL
; /* Test file */
767 char resource
[512], /* Resource for request */
768 token
[1024], /* Token from file */
769 *tokenptr
, /* Pointer into token */
770 temp
[1024], /* Temporary string */
771 buffer
[8192], /* Copy buffer */
772 compression
[16]; /* COMPRESSION value */
773 ipp_t
*request
= NULL
, /* IPP request */
774 *response
= NULL
; /* IPP response */
775 size_t length
; /* Length of IPP request */
776 http_status_t status
; /* HTTP status */
777 cups_file_t
*reqfile
; /* File to send */
778 ssize_t bytes
; /* Bytes read/written */
779 char attr
[128]; /* Attribute name */
780 ipp_op_t op
; /* Operation */
781 ipp_tag_t group
; /* Current group */
782 ipp_tag_t value
; /* Current value type */
783 ipp_attribute_t
*attrptr
, /* Attribute pointer */
784 *found
, /* Found attribute */
785 *lastcol
= NULL
; /* Last collection attribute */
786 char name
[1024]; /* Name of test */
787 char filename
[1024]; /* Filename */
788 _cups_transfer_t transfer
; /* To chunk or not to chunk */
789 int version
, /* IPP version number to use */
790 skip_test
; /* Skip this test? */
791 int num_statuses
= 0; /* Number of valid status codes */
792 _cups_status_t statuses
[100], /* Valid status codes */
793 *last_status
; /* Last STATUS (for predicates) */
794 int num_expects
= 0; /* Number of expected attributes */
795 _cups_expect_t expects
[200], /* Expected attributes */
796 *expect
, /* Current expected attribute */
797 *last_expect
; /* Last EXPECT (for predicates) */
798 int num_displayed
= 0; /* Number of displayed attributes */
799 char *displayed
[200]; /* Displayed attributes */
800 size_t widths
[200]; /* Width of columns */
801 cups_array_t
*a
, /* Duplicate attribute array */
802 *errors
= NULL
; /* Errors array */
803 const char *error
; /* Current error */
807 * Open the test file...
810 if ((fp
= fopen(testfile
, "r")) == NULL
)
812 print_fatal_error("Unable to open test file %s - %s", testfile
,
819 * Connect to the server...
822 if ((http
= httpConnect2(vars
->hostname
, vars
->port
, NULL
, vars
->family
,
823 vars
->encryption
, 1, 30000, NULL
)) == NULL
)
825 print_fatal_error("Unable to connect to %s on port %d - %s", vars
->hostname
,
826 vars
->port
, cupsLastErrorString());
832 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
,
833 "deflate, gzip, identity");
835 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
, "identity");
836 #endif /* HAVE_LIBZ */
838 if (vars
->timeout
> 0.0)
839 httpSetTimeout(http
, vars
->timeout
, timeout_cb
, NULL
);
845 CUPS_SRAND(time(NULL
));
847 errors
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
,
848 (cups_afree_func_t
)free
);
851 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
853 while (!Cancel
&& get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
856 * Expect an open brace...
859 if (!strcmp(token
, "DEFINE"))
865 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
866 get_token(fp
, temp
, sizeof(temp
), &linenum
))
868 expand_variables(vars
, token
, temp
, sizeof(token
));
869 set_variable(vars
, attr
, token
);
873 print_fatal_error("Missing DEFINE name and/or value on line %d.",
881 else if (!strcmp(token
, "DEFINE-DEFAULT"))
884 * DEFINE-DEFAULT name value
887 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
888 get_token(fp
, temp
, sizeof(temp
), &linenum
))
890 expand_variables(vars
, token
, temp
, sizeof(token
));
891 if (!get_variable(vars
, attr
))
892 set_variable(vars
, attr
, token
);
896 print_fatal_error("Missing DEFINE-DEFAULT name and/or value on line "
904 else if (!strcmp(token
, "IGNORE-ERRORS"))
911 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
912 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
914 IgnoreErrors
= !_cups_strcasecmp(temp
, "yes");
918 print_fatal_error("Missing IGNORE-ERRORS value on line %d.", linenum
);
925 else if (!strcmp(token
, "INCLUDE"))
932 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
935 * Map the filename to and then run the tests...
938 if (!do_tests(vars
, get_filename(testfile
, filename
, temp
,
949 print_fatal_error("Missing INCLUDE filename on line %d.", linenum
);
957 else if (!strcmp(token
, "INCLUDE-IF-DEFINED"))
960 * INCLUDE-IF-DEFINED name "filename"
961 * INCLUDE-IF-DEFINED name <filename>
964 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
965 get_token(fp
, temp
, sizeof(temp
), &linenum
))
968 * Map the filename to and then run the tests...
971 if (get_variable(vars
, attr
) &&
972 !do_tests(vars
, get_filename(testfile
, filename
, temp
,
983 print_fatal_error("Missing INCLUDE-IF-DEFINED name or filename on line "
992 else if (!strcmp(token
, "INCLUDE-IF-NOT-DEFINED"))
995 * INCLUDE-IF-NOT-DEFINED name "filename"
996 * INCLUDE-IF-NOT-DEFINED name <filename>
999 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1000 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1003 * Map the filename to and then run the tests...
1006 if (!get_variable(vars
, attr
) &&
1007 !do_tests(vars
, get_filename(testfile
, filename
, temp
,
1018 print_fatal_error("Missing INCLUDE-IF-NOT-DEFINED name or filename on "
1019 "line %d.", linenum
);
1027 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1030 * SKIP-IF-DEFINED variable
1033 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1035 if (get_variable(vars
, temp
))
1040 print_fatal_error("Missing SKIP-IF-DEFINED variable on line %d.",
1046 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1049 * SKIP-IF-NOT-DEFINED variable
1052 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1054 if (!get_variable(vars
, temp
))
1059 print_fatal_error("Missing SKIP-IF-NOT-DEFINED variable on line %d.",
1065 else if (!strcmp(token
, "TRANSFER"))
1073 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1075 if (!strcmp(temp
, "auto"))
1076 Transfer
= _CUPS_TRANSFER_AUTO
;
1077 else if (!strcmp(temp
, "chunked"))
1078 Transfer
= _CUPS_TRANSFER_CHUNKED
;
1079 else if (!strcmp(temp
, "length"))
1080 Transfer
= _CUPS_TRANSFER_LENGTH
;
1083 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp
,
1091 print_fatal_error("Missing TRANSFER value on line %d.", linenum
);
1098 else if (!strcmp(token
, "VERSION"))
1100 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1102 if (!strcmp(temp
, "1.0"))
1104 else if (!strcmp(temp
, "1.1"))
1106 else if (!strcmp(temp
, "2.0"))
1108 else if (!strcmp(temp
, "2.1"))
1110 else if (!strcmp(temp
, "2.2"))
1114 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1121 print_fatal_error("Missing VERSION number on line %d.", linenum
);
1128 else if (strcmp(token
, "{"))
1130 print_fatal_error("Unexpected token %s seen on line %d.", token
, linenum
);
1136 * Initialize things...
1141 if (Output
== _CUPS_OUTPUT_PLIST
)
1143 else if (Output
== _CUPS_OUTPUT_TEST
)
1144 printf("\"%s\":\n", testfile
);
1149 strlcpy(resource
, vars
->resource
, sizeof(resource
));
1154 group
= IPP_TAG_ZERO
;
1155 ignore_errors
= IgnoreErrors
;
1162 transfer
= Transfer
;
1163 compression
[0] = '\0';
1165 strlcpy(name
, testfile
, sizeof(name
));
1166 if (strrchr(name
, '.') != NULL
)
1167 *strrchr(name
, '.') = '\0';
1170 * Parse until we see a close brace...
1173 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
1175 if (_cups_strcasecmp(token
, "COUNT") &&
1176 _cups_strcasecmp(token
, "DEFINE-MATCH") &&
1177 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1178 _cups_strcasecmp(token
, "DEFINE-VALUE") &&
1179 _cups_strcasecmp(token
, "IF-DEFINED") &&
1180 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1181 _cups_strcasecmp(token
, "IN-GROUP") &&
1182 _cups_strcasecmp(token
, "OF-TYPE") &&
1183 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1184 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1185 _cups_strcasecmp(token
, "REPEAT-NO-MATCH") &&
1186 _cups_strcasecmp(token
, "SAME-COUNT-AS") &&
1187 _cups_strcasecmp(token
, "WITH-ALL-VALUES") &&
1188 _cups_strcasecmp(token
, "WITH-VALUE"))
1191 if (_cups_strcasecmp(token
, "DEFINE-MATCH") &&
1192 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1193 _cups_strcasecmp(token
, "IF-DEFINED") &&
1194 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1195 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1196 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1197 _cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
1200 if (!strcmp(token
, "}"))
1202 else if (!strcmp(token
, "{") && lastcol
)
1205 * Another collection value
1208 ipp_t
*col
= get_collection(vars
, fp
, &linenum
);
1209 /* Collection value */
1213 ipp_attribute_t
*tempcol
; /* Pointer to new buffer */
1217 * Reallocate memory...
1220 if ((tempcol
= realloc(lastcol
, sizeof(ipp_attribute_t
) +
1221 (lastcol
->num_values
+ 1) *
1222 sizeof(_ipp_value_t
))) == NULL
)
1224 print_fatal_error("Unable to allocate memory on line %d.", linenum
);
1229 if (tempcol
!= lastcol
)
1232 * Reset pointers in the list...
1236 request
->prev
->next
= tempcol
;
1238 request
->attrs
= tempcol
;
1240 lastcol
= request
->current
= request
->last
= tempcol
;
1243 lastcol
->values
[lastcol
->num_values
].collection
= col
;
1244 lastcol
->num_values
++;
1252 else if (!strcmp(token
, "COMPRESSION"))
1256 * COMPRESSION deflate
1260 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1262 expand_variables(vars
, compression
, temp
, sizeof(compression
));
1264 if (strcmp(compression
, "none") && strcmp(compression
, "deflate") &&
1265 strcmp(compression
, "gzip"))
1267 if (strcmp(compression
, "none"))
1268 #endif /* HAVE_LIBZ */
1270 print_fatal_error("Unsupported COMPRESSION value '%s' on line %d.",
1271 compression
, linenum
);
1276 if (!strcmp(compression
, "none"))
1277 compression
[0] = '\0';
1281 print_fatal_error("Missing COMPRESSION value on line %d.", linenum
);
1286 else if (!strcmp(token
, "DEFINE"))
1292 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1293 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1295 expand_variables(vars
, token
, temp
, sizeof(token
));
1296 set_variable(vars
, attr
, token
);
1300 print_fatal_error("Missing DEFINE name and/or value on line %d.",
1306 else if (!strcmp(token
, "IGNORE-ERRORS"))
1313 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1314 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1316 ignore_errors
= !_cups_strcasecmp(temp
, "yes");
1320 print_fatal_error("Missing IGNORE-ERRORS value on line %d.", linenum
);
1327 else if (!_cups_strcasecmp(token
, "NAME"))
1333 get_token(fp
, name
, sizeof(name
), &linenum
);
1335 else if (!strcmp(token
, "REQUEST-ID"))
1342 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1344 if (isdigit(temp
[0] & 255))
1345 request_id
= atoi(temp
);
1346 else if (!_cups_strcasecmp(temp
, "random"))
1347 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
1350 print_fatal_error("Bad REQUEST-ID value \"%s\" on line %d.", temp
,
1358 print_fatal_error("Missing REQUEST-ID value on line %d.", linenum
);
1363 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1366 * SKIP-IF-DEFINED variable
1369 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1371 if (get_variable(vars
, temp
))
1376 print_fatal_error("Missing SKIP-IF-DEFINED value on line %d.",
1382 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1385 * SKIP-IF-NOT-DEFINED variable
1388 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1390 if (!get_variable(vars
, temp
))
1395 print_fatal_error("Missing SKIP-IF-NOT-DEFINED value on line %d.",
1401 else if (!strcmp(token
, "SKIP-PREVIOUS-ERROR"))
1404 * SKIP-PREVIOUS-ERROR yes
1405 * SKIP-PREVIOUS-ERROR no
1408 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1409 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1411 skip_previous
= !_cups_strcasecmp(temp
, "yes");
1415 print_fatal_error("Missing SKIP-PREVIOUS-ERROR value on line %d.", linenum
);
1422 else if (!strcmp(token
, "TRANSFER"))
1430 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1432 if (!strcmp(temp
, "auto"))
1433 transfer
= _CUPS_TRANSFER_AUTO
;
1434 else if (!strcmp(temp
, "chunked"))
1435 transfer
= _CUPS_TRANSFER_CHUNKED
;
1436 else if (!strcmp(temp
, "length"))
1437 transfer
= _CUPS_TRANSFER_LENGTH
;
1440 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp
,
1448 print_fatal_error("Missing TRANSFER value on line %d.", linenum
);
1453 else if (!_cups_strcasecmp(token
, "VERSION"))
1455 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1457 if (!strcmp(temp
, "0.0"))
1459 else if (!strcmp(temp
, "1.0"))
1461 else if (!strcmp(temp
, "1.1"))
1463 else if (!strcmp(temp
, "2.0"))
1465 else if (!strcmp(temp
, "2.1"))
1467 else if (!strcmp(temp
, "2.2"))
1471 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1478 print_fatal_error("Missing VERSION number on line %d.", linenum
);
1483 else if (!_cups_strcasecmp(token
, "RESOURCE"))
1489 if (!get_token(fp
, resource
, sizeof(resource
), &linenum
))
1491 print_fatal_error("Missing RESOURCE path on line %d.", linenum
);
1496 else if (!_cups_strcasecmp(token
, "OPERATION"))
1502 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1504 print_fatal_error("Missing OPERATION code on line %d.", linenum
);
1509 expand_variables(vars
, token
, temp
, sizeof(token
));
1511 if ((op
= ippOpValue(token
)) == (ipp_op_t
)-1 &&
1512 (op
= strtol(token
, NULL
, 0)) == 0)
1514 print_fatal_error("Bad OPERATION code \"%s\" on line %d.", token
,
1520 else if (!_cups_strcasecmp(token
, "GROUP"))
1523 * Attribute group...
1526 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1528 print_fatal_error("Missing GROUP tag on line %d.", linenum
);
1533 if ((value
= ippTagValue(token
)) < 0)
1535 print_fatal_error("Bad GROUP tag \"%s\" on line %d.", token
, linenum
);
1541 ippAddSeparator(request
);
1545 else if (!_cups_strcasecmp(token
, "DELAY"))
1548 * Delay before operation...
1553 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1555 print_fatal_error("Missing DELAY value on line %d.", linenum
);
1560 expand_variables(vars
, token
, temp
, sizeof(token
));
1562 if ((delay
= _cupsStrScand(token
, NULL
, localeconv())) <= 0.0)
1564 print_fatal_error("Bad DELAY value \"%s\" on line %d.", token
,
1571 if (Output
== _CUPS_OUTPUT_TEST
)
1572 printf(" [%g second delay]\n", delay
);
1574 usleep((int)(1000000.0 * delay
));
1577 else if (!_cups_strcasecmp(token
, "ATTR"))
1583 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1585 print_fatal_error("Missing ATTR value tag on line %d.", linenum
);
1590 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
1592 print_fatal_error("Bad ATTR value tag \"%s\" on line %d.", token
,
1598 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
1600 print_fatal_error("Missing ATTR name on line %d.", linenum
);
1605 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1607 print_fatal_error("Missing ATTR value on line %d.", linenum
);
1612 expand_variables(vars
, token
, temp
, sizeof(token
));
1617 case IPP_TAG_BOOLEAN
:
1618 if (!_cups_strcasecmp(token
, "true"))
1619 attrptr
= ippAddBoolean(request
, group
, attr
, 1);
1621 attrptr
= ippAddBoolean(request
, group
, attr
, atoi(token
));
1624 case IPP_TAG_INTEGER
:
1626 if (!strchr(token
, ','))
1627 attrptr
= ippAddInteger(request
, group
, value
, attr
,
1628 strtol(token
, &tokenptr
, 0));
1631 int values
[100], /* Values */
1632 num_values
= 1; /* Number of values */
1634 values
[0] = strtol(token
, &tokenptr
, 10);
1635 while (tokenptr
&& *tokenptr
&&
1636 num_values
< (int)(sizeof(values
) / sizeof(values
[0])))
1638 if (*tokenptr
== ',')
1640 else if (!isdigit(*tokenptr
& 255) && *tokenptr
!= '-')
1643 values
[num_values
] = strtol(tokenptr
, &tokenptr
, 0);
1647 attrptr
= ippAddIntegers(request
, group
, value
, attr
, num_values
, values
);
1650 if (!tokenptr
|| *tokenptr
)
1652 print_fatal_error("Bad %s value \"%s\" on line %d.",
1653 ippTagString(value
), token
, linenum
);
1659 case IPP_TAG_RESOLUTION
:
1661 int xres
, /* X resolution */
1662 yres
; /* Y resolution */
1663 char *ptr
; /* Pointer into value */
1665 xres
= yres
= strtol(token
, (char **)&ptr
, 10);
1666 if (ptr
> token
&& xres
> 0)
1669 yres
= strtol(ptr
+ 1, (char **)&ptr
, 10);
1672 if (ptr
<= token
|| xres
<= 0 || yres
<= 0 || !ptr
||
1673 (_cups_strcasecmp(ptr
, "dpi") &&
1674 _cups_strcasecmp(ptr
, "dpc") &&
1675 _cups_strcasecmp(ptr
, "dpcm") &&
1676 _cups_strcasecmp(ptr
, "other")))
1678 print_fatal_error("Bad resolution value \"%s\" on line %d.",
1684 if (!_cups_strcasecmp(ptr
, "dpi"))
1685 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_INCH
,
1687 else if (!_cups_strcasecmp(ptr
, "dpc") ||
1688 !_cups_strcasecmp(ptr
, "dpcm"))
1689 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_CM
,
1692 attrptr
= ippAddResolution(request
, group
, attr
, (ipp_res_t
)0,
1697 case IPP_TAG_RANGE
:
1699 int lowers
[4], /* Lower value */
1700 uppers
[4], /* Upper values */
1701 num_vals
; /* Number of values */
1704 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
1705 lowers
+ 0, uppers
+ 0,
1706 lowers
+ 1, uppers
+ 1,
1707 lowers
+ 2, uppers
+ 2,
1708 lowers
+ 3, uppers
+ 3);
1710 if ((num_vals
& 1) || num_vals
== 0)
1712 print_fatal_error("Bad rangeOfInteger value \"%s\" on line "
1713 "%d.", token
, linenum
);
1718 attrptr
= ippAddRanges(request
, group
, attr
, num_vals
/ 2, lowers
,
1723 case IPP_TAG_BEGIN_COLLECTION
:
1724 if (!strcmp(token
, "{"))
1726 ipp_t
*col
= get_collection(vars
, fp
, &linenum
);
1727 /* Collection value */
1731 attrptr
= lastcol
= ippAddCollection(request
, group
, attr
, col
);
1742 print_fatal_error("Bad ATTR collection value on line %d.",
1749 case IPP_TAG_STRING
:
1750 attrptr
= ippAddOctetString(request
, group
, attr
, token
,
1755 print_fatal_error("Unsupported ATTR value tag %s on line %d.",
1756 ippTagString(value
), linenum
);
1760 case IPP_TAG_TEXTLANG
:
1761 case IPP_TAG_NAMELANG
:
1764 case IPP_TAG_KEYWORD
:
1766 case IPP_TAG_URISCHEME
:
1767 case IPP_TAG_CHARSET
:
1768 case IPP_TAG_LANGUAGE
:
1769 case IPP_TAG_MIMETYPE
:
1770 if (!strchr(token
, ','))
1771 attrptr
= ippAddString(request
, group
, value
, attr
, NULL
, token
);
1775 * Multiple string values...
1778 int num_values
; /* Number of values */
1779 char *values
[100], /* Values */
1780 *ptr
; /* Pointer to next value */
1786 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
1788 if (ptr
> token
&& ptr
[-1] == '\\')
1789 _cups_strcpy(ptr
- 1, ptr
);
1793 values
[num_values
] = ptr
;
1798 attrptr
= ippAddStrings(request
, group
, value
, attr
, num_values
,
1799 NULL
, (const char **)values
);
1806 print_fatal_error("Unable to add attribute on line %d: %s", linenum
,
1807 cupsLastErrorString());
1812 else if (!_cups_strcasecmp(token
, "FILE"))
1818 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1820 print_fatal_error("Missing FILE filename on line %d.", linenum
);
1825 expand_variables(vars
, token
, temp
, sizeof(token
));
1826 get_filename(testfile
, filename
, token
, sizeof(filename
));
1828 else if (!_cups_strcasecmp(token
, "STATUS"))
1834 if (num_statuses
>= (int)(sizeof(statuses
) / sizeof(statuses
[0])))
1836 print_fatal_error("Too many STATUS's on line %d.", linenum
);
1841 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1843 print_fatal_error("Missing STATUS code on line %d.", linenum
);
1848 if ((statuses
[num_statuses
].status
= ippErrorValue(token
))
1849 == (ipp_status_t
)-1 &&
1850 (statuses
[num_statuses
].status
= strtol(token
, NULL
, 0)) == 0)
1852 print_fatal_error("Bad STATUS code \"%s\" on line %d.", token
,
1858 last_status
= statuses
+ num_statuses
;
1861 last_status
->define_match
= NULL
;
1862 last_status
->define_no_match
= NULL
;
1863 last_status
->if_defined
= NULL
;
1864 last_status
->if_not_defined
= NULL
;
1865 last_status
->repeat_limit
= 1000;
1866 last_status
->repeat_match
= 0;
1867 last_status
->repeat_no_match
= 0;
1869 else if (!_cups_strcasecmp(token
, "EXPECT"))
1872 * Expected attributes...
1875 if (num_expects
>= (int)(sizeof(expects
) / sizeof(expects
[0])))
1877 print_fatal_error("Too many EXPECT's on line %d.", linenum
);
1882 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1884 print_fatal_error("Missing EXPECT name on line %d.", linenum
);
1889 last_expect
= expects
+ num_expects
;
1892 memset(last_expect
, 0, sizeof(_cups_expect_t
));
1893 last_expect
->repeat_limit
= 1000;
1895 if (token
[0] == '!')
1897 last_expect
->not_expect
= 1;
1898 last_expect
->name
= strdup(token
+ 1);
1900 else if (token
[0] == '?')
1902 last_expect
->optional
= 1;
1903 last_expect
->name
= strdup(token
+ 1);
1906 last_expect
->name
= strdup(token
);
1908 else if (!_cups_strcasecmp(token
, "COUNT"))
1910 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1912 print_fatal_error("Missing COUNT number on line %d.", linenum
);
1917 if ((i
= atoi(token
)) <= 0)
1919 print_fatal_error("Bad COUNT \"%s\" on line %d.", token
, linenum
);
1925 last_expect
->count
= i
;
1928 print_fatal_error("COUNT without a preceding EXPECT on line %d.",
1934 else if (!_cups_strcasecmp(token
, "DEFINE-MATCH"))
1936 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1938 print_fatal_error("Missing DEFINE-MATCH variable on line %d.",
1945 last_expect
->define_match
= strdup(token
);
1946 else if (last_status
)
1947 last_status
->define_match
= strdup(token
);
1950 print_fatal_error("DEFINE-MATCH without a preceding EXPECT or STATUS "
1951 "on line %d.", linenum
);
1956 else if (!_cups_strcasecmp(token
, "DEFINE-NO-MATCH"))
1958 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1960 print_fatal_error("Missing DEFINE-NO-MATCH variable on line %d.",
1967 last_expect
->define_no_match
= strdup(token
);
1968 else if (last_status
)
1969 last_status
->define_no_match
= strdup(token
);
1972 print_fatal_error("DEFINE-NO-MATCH without a preceding EXPECT or "
1973 "STATUS on line %d.", linenum
);
1978 else if (!_cups_strcasecmp(token
, "DEFINE-VALUE"))
1980 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1982 print_fatal_error("Missing DEFINE-VALUE variable on line %d.",
1989 last_expect
->define_value
= strdup(token
);
1992 print_fatal_error("DEFINE-VALUE without a preceding EXPECT on "
1993 "line %d.", linenum
);
1998 else if (!_cups_strcasecmp(token
, "OF-TYPE"))
2000 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2002 print_fatal_error("Missing OF-TYPE value tag(s) on line %d.",
2009 last_expect
->of_type
= strdup(token
);
2012 print_fatal_error("OF-TYPE without a preceding EXPECT on line %d.",
2018 else if (!_cups_strcasecmp(token
, "IN-GROUP"))
2020 ipp_tag_t in_group
; /* IN-GROUP value */
2023 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2025 print_fatal_error("Missing IN-GROUP group tag on line %d.", linenum
);
2030 if ((in_group
= ippTagValue(token
)) == (ipp_tag_t
)-1)
2033 else if (last_expect
)
2034 last_expect
->in_group
= in_group
;
2037 print_fatal_error("IN-GROUP without a preceding EXPECT on line %d.",
2043 else if (!_cups_strcasecmp(token
, "REPEAT-LIMIT"))
2045 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2047 print_fatal_error("Missing REPEAT-LIMIT value on line %d.", linenum
);
2051 else if (atoi(token
) <= 0)
2053 print_fatal_error("Bad REPEAT-LIMIT value on line %d.", linenum
);
2059 last_status
->repeat_limit
= atoi(token
);
2060 else if (last_expect
)
2061 last_expect
->repeat_limit
= atoi(token
);
2064 print_fatal_error("REPEAT-LIMIT without a preceding EXPECT or STATUS "
2065 "on line %d.", linenum
);
2070 else if (!_cups_strcasecmp(token
, "REPEAT-MATCH"))
2073 last_status
->repeat_match
= 1;
2074 else if (last_expect
)
2075 last_expect
->repeat_match
= 1;
2078 print_fatal_error("REPEAT-MATCH without a preceding EXPECT or STATUS "
2079 "on line %d.", linenum
);
2084 else if (!_cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
2087 last_status
->repeat_no_match
= 1;
2088 else if (last_expect
)
2089 last_expect
->repeat_no_match
= 1;
2092 print_fatal_error("REPEAT-NO-MATCH without a preceding EXPECT or "
2093 "STATUS on ine %d.", linenum
);
2098 else if (!_cups_strcasecmp(token
, "SAME-COUNT-AS"))
2100 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2102 print_fatal_error("Missing SAME-COUNT-AS name on line %d.", linenum
);
2108 last_expect
->same_count_as
= strdup(token
);
2111 print_fatal_error("SAME-COUNT-AS without a preceding EXPECT on line "
2117 else if (!_cups_strcasecmp(token
, "IF-DEFINED"))
2119 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2121 print_fatal_error("Missing IF-DEFINED name on line %d.", linenum
);
2127 last_expect
->if_defined
= strdup(token
);
2128 else if (last_status
)
2129 last_status
->if_defined
= strdup(token
);
2132 print_fatal_error("IF-DEFINED without a preceding EXPECT or STATUS "
2133 "on line %d.", linenum
);
2138 else if (!_cups_strcasecmp(token
, "IF-NOT-DEFINED"))
2140 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2142 print_fatal_error("Missing IF-NOT-DEFINED name on line %d.", linenum
);
2148 last_expect
->if_not_defined
= strdup(token
);
2149 else if (last_status
)
2150 last_status
->if_not_defined
= strdup(token
);
2153 print_fatal_error("IF-NOT-DEFINED without a preceding EXPECT or STATUS "
2154 "on line %d.", linenum
);
2159 else if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") ||
2160 !_cups_strcasecmp(token
, "WITH-VALUE"))
2162 if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") && last_expect
)
2163 last_expect
->with_flags
= _CUPS_WITH_ALL
;
2165 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
2167 print_fatal_error("Missing %s value on line %d.", token
, linenum
);
2175 * Expand any variables in the value and then save it.
2178 expand_variables(vars
, token
, temp
, sizeof(token
));
2180 tokenptr
= token
+ strlen(token
) - 1;
2182 if (token
[0] == '/' && tokenptr
> token
&& *tokenptr
== '/')
2185 * WITH-VALUE is a POSIX extended regular expression.
2188 last_expect
->with_value
= calloc(1, tokenptr
- token
);
2189 last_expect
->with_flags
|= _CUPS_WITH_REGEX
;
2191 if (last_expect
->with_value
)
2192 memcpy(last_expect
->with_value
, token
+ 1, tokenptr
- token
- 1);
2197 * WITH-VALUE is a literal value...
2200 char *ptr
; /* Pointer into value */
2202 for (ptr
= token
; *ptr
; ptr
++)
2204 if (*ptr
== '\\' && ptr
[1])
2207 * Remove \ from \foo...
2210 _cups_strcpy(ptr
, ptr
+ 1);
2214 last_expect
->with_value
= strdup(token
);
2215 last_expect
->with_flags
|= _CUPS_WITH_LITERAL
;
2220 print_fatal_error("%s without a preceding EXPECT on line %d.", token
,
2226 else if (!_cups_strcasecmp(token
, "DISPLAY"))
2229 * Display attributes...
2232 if (num_displayed
>= (int)(sizeof(displayed
) / sizeof(displayed
[0])))
2234 print_fatal_error("Too many DISPLAY's on line %d", linenum
);
2239 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2241 print_fatal_error("Missing DISPLAY name on line %d.", linenum
);
2246 displayed
[num_displayed
] = strdup(token
);
2251 print_fatal_error("Unexpected token %s seen on line %d.", token
,
2259 * Submit the IPP request...
2264 request
->request
.op
.version
[0] = version
/ 10;
2265 request
->request
.op
.version
[1] = version
% 10;
2266 request
->request
.op
.operation_id
= op
;
2267 request
->request
.op
.request_id
= request_id
;
2269 if (Output
== _CUPS_OUTPUT_PLIST
)
2272 puts("<key>Name</key>");
2273 print_xml_string("string", name
);
2274 puts("<key>Operation</key>");
2275 print_xml_string("string", ippOpString(op
));
2276 puts("<key>RequestAttributes</key>");
2281 for (attrptr
= request
->attrs
, group
= attrptr
->group_tag
;
2283 attrptr
= attrptr
->next
)
2284 print_attr(attrptr
, &group
);
2289 else if (Output
== _CUPS_OUTPUT_TEST
)
2293 printf(" %s:\n", ippOpString(op
));
2295 for (attrptr
= request
->attrs
; attrptr
; attrptr
= attrptr
->next
)
2296 print_attr(attrptr
, NULL
);
2299 printf(" %-68.68s [", name
);
2303 if ((skip_previous
&& !prev_pass
) || skip_test
)
2310 if (Output
== _CUPS_OUTPUT_PLIST
)
2312 puts("<key>Successful</key>");
2314 puts("<key>StatusCode</key>");
2315 print_xml_string("string", "skip");
2316 puts("<key>ResponseAttributes</key>");
2319 else if (Output
== _CUPS_OUTPUT_TEST
)
2326 repeat_interval
= 1;
2335 if (transfer
== _CUPS_TRANSFER_CHUNKED
||
2336 (transfer
== _CUPS_TRANSFER_AUTO
&& filename
[0]))
2339 * Send request using chunking - a 0 length means "chunk".
2347 * Send request using content length...
2350 length
= ippLength(request
);
2352 if (filename
[0] && (reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2355 * Read the file to get the uncompressed file size...
2358 while ((bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
2361 cupsFileClose(reqfile
);
2366 * Send the request...
2373 if (status
!= HTTP_ERROR
)
2375 while (!response
&& !Cancel
&& prev_pass
)
2377 status
= cupsSendRequest(http
, request
, resource
, length
);
2381 httpSetField(http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
2382 #endif /* HAVE_LIBZ */
2384 if (!Cancel
&& status
== HTTP_CONTINUE
&&
2385 request
->state
== IPP_DATA
&& filename
[0])
2387 if ((reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2390 (bytes
= cupsFileRead(reqfile
, buffer
,
2391 sizeof(buffer
))) > 0)
2392 if ((status
= cupsWriteRequestData(http
, buffer
,
2393 bytes
)) != HTTP_CONTINUE
)
2396 cupsFileClose(reqfile
);
2400 snprintf(buffer
, sizeof(buffer
), "%s: %s", filename
,
2402 _cupsSetError(IPP_INTERNAL_ERROR
, buffer
, 0);
2404 status
= HTTP_ERROR
;
2409 * Get the server's response...
2412 if (!Cancel
&& status
!= HTTP_ERROR
)
2414 response
= cupsGetResponse(http
, resource
);
2415 status
= httpGetStatus(http
);
2418 if (!Cancel
&& status
== HTTP_ERROR
&& http
->error
!= EINVAL
&&
2420 http
->error
!= WSAETIMEDOUT
)
2422 http
->error
!= ETIMEDOUT
)
2425 if (httpReconnect(http
))
2428 else if (status
== HTTP_ERROR
)
2433 else if (status
!= HTTP_OK
)
2441 if (!Cancel
&& status
== HTTP_ERROR
&& http
->error
!= EINVAL
&&
2443 http
->error
!= WSAETIMEDOUT
)
2445 http
->error
!= ETIMEDOUT
)
2448 if (httpReconnect(http
))
2451 else if (status
== HTTP_ERROR
)
2454 httpReconnect(http
);
2458 else if (status
!= HTTP_OK
)
2465 * Check results of request...
2468 cupsArrayClear(errors
);
2470 if (http
->version
!= HTTP_1_1
)
2471 add_stringf(errors
, "Bad HTTP version (%d.%d)", http
->version
/ 100,
2472 http
->version
% 100);
2477 * No response, log error...
2480 add_stringf(errors
, "IPP request failed with status %s (%s)",
2481 ippErrorString(cupsLastError()),
2482 cupsLastErrorString());
2487 * Collect common attribute values...
2490 if ((attrptr
= ippFindAttribute(response
, "job-id",
2491 IPP_TAG_INTEGER
)) != NULL
)
2493 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2494 set_variable(vars
, "job-id", temp
);
2497 if ((attrptr
= ippFindAttribute(response
, "job-uri",
2498 IPP_TAG_URI
)) != NULL
)
2499 set_variable(vars
, "job-uri", attrptr
->values
[0].string
.text
);
2501 if ((attrptr
= ippFindAttribute(response
, "notify-subscription-id",
2502 IPP_TAG_INTEGER
)) != NULL
)
2504 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2505 set_variable(vars
, "notify-subscription-id", temp
);
2509 * Check response, validating groups and attributes and logging errors
2513 if (response
->state
!= IPP_DATA
)
2515 "Missing end-of-attributes-tag in response "
2516 "(RFC 2910 section 3.5.1)");
2519 (response
->request
.status
.version
[0] != (version
/ 10) ||
2520 response
->request
.status
.version
[1] != (version
% 10)))
2522 "Bad version %d.%d in response - expected %d.%d "
2523 "(RFC 2911 section 3.1.8).",
2524 response
->request
.status
.version
[0],
2525 response
->request
.status
.version
[1],
2526 version
/ 10, version
% 10);
2528 if (response
->request
.status
.request_id
!= request_id
)
2530 "Bad request ID %d in response - expected %d "
2531 "(RFC 2911 section 3.1.1)",
2532 response
->request
.status
.request_id
, request_id
);
2534 attrptr
= response
->attrs
;
2537 "Missing first attribute \"attributes-charset "
2538 "(charset)\" in group operation-attributes-tag "
2539 "(RFC 2911 section 3.1.4).");
2542 if (!attrptr
->name
||
2543 attrptr
->value_tag
!= IPP_TAG_CHARSET
||
2544 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2545 attrptr
->num_values
!= 1 ||
2546 strcmp(attrptr
->name
, "attributes-charset"))
2548 "Bad first attribute \"%s (%s%s)\" in group %s, "
2549 "expected \"attributes-charset (charset)\" in "
2550 "group operation-attributes-tag (RFC 2911 section "
2552 attrptr
->name
? attrptr
->name
: "(null)",
2553 attrptr
->num_values
> 1 ? "1setOf " : "",
2554 ippTagString(attrptr
->value_tag
),
2555 ippTagString(attrptr
->group_tag
));
2557 attrptr
= attrptr
->next
;
2560 "Missing second attribute \"attributes-natural-"
2561 "language (naturalLanguage)\" in group "
2562 "operation-attributes-tag (RFC 2911 section "
2564 else if (!attrptr
->name
||
2565 attrptr
->value_tag
!= IPP_TAG_LANGUAGE
||
2566 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2567 attrptr
->num_values
!= 1 ||
2568 strcmp(attrptr
->name
, "attributes-natural-language"))
2570 "Bad first attribute \"%s (%s%s)\" in group %s, "
2571 "expected \"attributes-natural-language "
2572 "(naturalLanguage)\" in group "
2573 "operation-attributes-tag (RFC 2911 section "
2575 attrptr
->name
? attrptr
->name
: "(null)",
2576 attrptr
->num_values
> 1 ? "1setOf " : "",
2577 ippTagString(attrptr
->value_tag
),
2578 ippTagString(attrptr
->group_tag
));
2581 if ((attrptr
= ippFindAttribute(response
, "status-message",
2582 IPP_TAG_ZERO
)) != NULL
)
2584 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2586 "status-message (text(255)) has wrong value tag "
2587 "%s (RFC 2911 section 3.1.6.2).",
2588 ippTagString(attrptr
->value_tag
));
2589 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2591 "status-message (text(255)) has wrong group tag "
2592 "%s (RFC 2911 section 3.1.6.2).",
2593 ippTagString(attrptr
->group_tag
));
2594 if (attrptr
->num_values
!= 1)
2596 "status-message (text(255)) has %d values "
2597 "(RFC 2911 section 3.1.6.2).",
2598 attrptr
->num_values
);
2599 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2600 strlen(attrptr
->values
[0].string
.text
) > 255)
2602 "status-message (text(255)) has bad length %d"
2603 " (RFC 2911 section 3.1.6.2).",
2604 (int)strlen(attrptr
->values
[0].string
.text
));
2607 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
2608 IPP_TAG_ZERO
)) != NULL
)
2610 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2612 "detailed-status-message (text(MAX)) has wrong "
2613 "value tag %s (RFC 2911 section 3.1.6.3).",
2614 ippTagString(attrptr
->value_tag
));
2615 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2617 "detailed-status-message (text(MAX)) has wrong "
2618 "group tag %s (RFC 2911 section 3.1.6.3).",
2619 ippTagString(attrptr
->group_tag
));
2620 if (attrptr
->num_values
!= 1)
2622 "detailed-status-message (text(MAX)) has %d values"
2623 " (RFC 2911 section 3.1.6.3).",
2624 attrptr
->num_values
);
2625 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2626 strlen(attrptr
->values
[0].string
.text
) > 1023)
2628 "detailed-status-message (text(MAX)) has bad "
2629 "length %d (RFC 2911 section 3.1.6.3).",
2630 (int)strlen(attrptr
->values
[0].string
.text
));
2633 a
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2635 for (attrptr
= response
->attrs
, group
= attrptr
->group_tag
;
2637 attrptr
= attrptr
->next
)
2639 if (attrptr
->group_tag
!= group
)
2641 int out_of_order
= 0; /* Are attribute groups out-of-order? */
2644 switch (attrptr
->group_tag
)
2649 case IPP_TAG_OPERATION
:
2653 case IPP_TAG_UNSUPPORTED_GROUP
:
2654 if (group
!= IPP_TAG_OPERATION
)
2659 case IPP_TAG_PRINTER
:
2660 if (group
!= IPP_TAG_OPERATION
&&
2661 group
!= IPP_TAG_UNSUPPORTED_GROUP
)
2665 case IPP_TAG_SUBSCRIPTION
:
2666 if (group
> attrptr
->group_tag
&&
2667 group
!= IPP_TAG_DOCUMENT
)
2672 if (group
> attrptr
->group_tag
)
2678 add_stringf(errors
, "Attribute groups out of order (%s < %s)",
2679 ippTagString(attrptr
->group_tag
),
2680 ippTagString(group
));
2682 if (attrptr
->group_tag
!= IPP_TAG_ZERO
)
2683 group
= attrptr
->group_tag
;
2686 validate_attr(errors
, attrptr
);
2690 if (cupsArrayFind(a
, attrptr
->name
))
2691 add_stringf(errors
, "Duplicate \"%s\" attribute in %s group",
2692 attrptr
->name
, ippTagString(group
));
2694 cupsArrayAdd(a
, attrptr
->name
);
2701 * Now check the test-defined expected status-code and attribute
2705 for (i
= 0; i
< num_statuses
; i
++)
2707 if (statuses
[i
].if_defined
&&
2708 !get_variable(vars
, statuses
[i
].if_defined
))
2711 if (statuses
[i
].if_not_defined
&&
2712 get_variable(vars
, statuses
[i
].if_not_defined
))
2715 if (response
->request
.status
.status_code
== statuses
[i
].status
)
2717 if (statuses
[i
].repeat_match
&&
2718 repeat_count
< statuses
[i
].repeat_limit
)
2721 if (statuses
[i
].define_match
)
2722 set_variable(vars
, statuses
[i
].define_match
, "1");
2728 if (statuses
[i
].repeat_no_match
&&
2729 repeat_count
< statuses
[i
].repeat_limit
)
2732 if (statuses
[i
].define_no_match
)
2734 set_variable(vars
, statuses
[i
].define_no_match
, "1");
2740 if (i
== num_statuses
&& num_statuses
> 0)
2742 for (i
= 0; i
< num_statuses
; i
++)
2744 if (statuses
[i
].if_defined
&&
2745 !get_variable(vars
, statuses
[i
].if_defined
))
2748 if (statuses
[i
].if_not_defined
&&
2749 get_variable(vars
, statuses
[i
].if_not_defined
))
2752 if (!statuses
[i
].repeat_match
)
2753 add_stringf(errors
, "EXPECTED: STATUS %s (got %s)",
2754 ippErrorString(statuses
[i
].status
),
2755 ippErrorString(cupsLastError()));
2758 if ((attrptr
= ippFindAttribute(response
, "status-message",
2759 IPP_TAG_TEXT
)) != NULL
)
2760 add_stringf(errors
, "status-message=\"%s\"",
2761 attrptr
->values
[0].string
.text
);
2764 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
2766 if (expect
->if_defined
&& !get_variable(vars
, expect
->if_defined
))
2769 if (expect
->if_not_defined
&&
2770 get_variable(vars
, expect
->if_not_defined
))
2773 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
2775 if ((found
&& expect
->not_expect
) ||
2776 (!found
&& !(expect
->not_expect
|| expect
->optional
)) ||
2777 (found
&& !expect_matches(expect
, found
->value_tag
)) ||
2778 (found
&& expect
->in_group
&&
2779 found
->group_tag
!= expect
->in_group
))
2781 if (expect
->define_no_match
)
2782 set_variable(vars
, expect
->define_no_match
, "1");
2783 else if (!expect
->define_match
&& !expect
->define_value
)
2785 if (found
&& expect
->not_expect
)
2786 add_stringf(errors
, "NOT EXPECTED: %s", expect
->name
);
2787 else if (!found
&& !(expect
->not_expect
|| expect
->optional
))
2788 add_stringf(errors
, "EXPECTED: %s", expect
->name
);
2791 if (!expect_matches(expect
, found
->value_tag
))
2792 add_stringf(errors
, "EXPECTED: %s OF-TYPE %s (got %s)",
2793 expect
->name
, expect
->of_type
,
2794 ippTagString(found
->value_tag
));
2796 if (expect
->in_group
&& found
->group_tag
!= expect
->in_group
)
2797 add_stringf(errors
, "EXPECTED: %s IN-GROUP %s (got %s).",
2798 expect
->name
, ippTagString(expect
->in_group
),
2799 ippTagString(found
->group_tag
));
2803 if (expect
->repeat_no_match
&&
2804 repeat_count
< expect
->repeat_limit
)
2811 ippAttributeString(found
, buffer
, sizeof(buffer
));
2814 !with_value(NULL
, expect
->with_value
, expect
->with_flags
, found
,
2815 buffer
, sizeof(buffer
)))
2817 if (expect
->define_no_match
)
2818 set_variable(vars
, expect
->define_no_match
, "1");
2819 else if (!expect
->define_match
&& !expect
->define_value
&&
2820 !expect
->repeat_match
&& !expect
->repeat_no_match
)
2822 if (expect
->with_flags
& _CUPS_WITH_REGEX
)
2823 add_stringf(errors
, "EXPECTED: %s %s /%s/",
2825 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
2826 "WITH-ALL-VALUES" : "WITH-VALUE",
2827 expect
->with_value
);
2829 add_stringf(errors
, "EXPECTED: %s %s \"%s\"",
2831 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
2832 "WITH-ALL-VALUES" : "WITH-VALUE",
2833 expect
->with_value
);
2835 with_value(errors
, expect
->with_value
, expect
->with_flags
, found
,
2836 buffer
, sizeof(buffer
));
2839 if (expect
->repeat_no_match
&&
2840 repeat_count
< expect
->repeat_limit
)
2846 if (found
&& expect
->count
> 0 &&
2847 found
->num_values
!= expect
->count
)
2849 if (expect
->define_no_match
)
2850 set_variable(vars
, expect
->define_no_match
, "1");
2851 else if (!expect
->define_match
&& !expect
->define_value
)
2853 add_stringf(errors
, "EXPECTED: %s COUNT %d (got %d)", expect
->name
,
2854 expect
->count
, found
->num_values
);
2857 if (expect
->repeat_no_match
&&
2858 repeat_count
< expect
->repeat_limit
)
2864 if (found
&& expect
->same_count_as
)
2866 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
2869 if (!attrptr
|| attrptr
->num_values
!= found
->num_values
)
2871 if (expect
->define_no_match
)
2872 set_variable(vars
, expect
->define_no_match
, "1");
2873 else if (!expect
->define_match
&& !expect
->define_value
)
2877 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2878 "(not returned)", expect
->name
,
2879 found
->num_values
, expect
->same_count_as
);
2880 else if (attrptr
->num_values
!= found
->num_values
)
2882 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2883 "(%d values)", expect
->name
, found
->num_values
,
2884 expect
->same_count_as
, attrptr
->num_values
);
2887 if (expect
->repeat_no_match
&&
2888 repeat_count
< expect
->repeat_limit
)
2895 if (found
&& expect
->define_match
)
2896 set_variable(vars
, expect
->define_match
, "1");
2898 if (found
&& expect
->define_value
)
2899 set_variable(vars
, expect
->define_value
, buffer
);
2901 if (found
&& expect
->repeat_match
&&
2902 repeat_count
< expect
->repeat_limit
)
2908 * If we are going to repeat this test, sleep 1 second so we don't flood
2909 * the printer with requests...
2914 if (Output
== _CUPS_OUTPUT_TEST
)
2916 printf("%04d]\n", repeat_count
);
2920 sleep(repeat_interval
);
2921 repeat_interval
= _cupsNextDelay(repeat_interval
, &repeat_prev
);
2923 if (Output
== _CUPS_OUTPUT_TEST
)
2925 printf(" %-68.68s [", name
);
2930 while (repeat_test
);
2936 if (cupsArrayCount(errors
) > 0)
2937 prev_pass
= pass
= 0;
2944 if (Output
== _CUPS_OUTPUT_PLIST
)
2946 puts("<key>Successful</key>");
2947 puts(prev_pass
? "<true />" : "<false />");
2948 puts("<key>StatusCode</key>");
2949 print_xml_string("string", ippErrorString(cupsLastError()));
2950 puts("<key>ResponseAttributes</key>");
2953 for (attrptr
= response
? response
->attrs
: NULL
,
2954 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2956 attrptr
= attrptr
->next
)
2957 print_attr(attrptr
, &group
);
2961 else if (Output
== _CUPS_OUTPUT_TEST
)
2963 puts(prev_pass
? "PASS]" : "FAIL]");
2965 if (!prev_pass
|| (Verbosity
&& response
))
2967 printf(" RECEIVED: %lu bytes in response\n",
2968 (unsigned long)ippLength(response
));
2969 printf(" status-code = %s (%s)\n", ippErrorString(cupsLastError()),
2970 cupsLastErrorString());
2974 for (attrptr
= response
->attrs
;
2976 attrptr
= attrptr
->next
)
2977 print_attr(attrptr
, NULL
);
2981 else if (!prev_pass
)
2982 fprintf(stderr
, "%s\n", cupsLastErrorString());
2984 if (prev_pass
&& Output
>= _CUPS_OUTPUT_LIST
&& !Verbosity
&&
2987 size_t width
; /* Length of value */
2989 for (i
= 0; i
< num_displayed
; i
++)
2991 widths
[i
] = strlen(displayed
[i
]);
2993 for (attrptr
= ippFindAttribute(response
, displayed
[i
], IPP_TAG_ZERO
);
2995 attrptr
= ippFindNextAttribute(response
, displayed
[i
],
2998 width
= ippAttributeString(attrptr
, NULL
, 0);
2999 if (width
> widths
[i
])
3004 if (Output
== _CUPS_OUTPUT_CSV
)
3005 print_csv(NULL
, num_displayed
, displayed
, widths
);
3007 print_line(NULL
, num_displayed
, displayed
, widths
);
3009 attrptr
= response
->attrs
;
3013 while (attrptr
&& attrptr
->group_tag
<= IPP_TAG_OPERATION
)
3014 attrptr
= attrptr
->next
;
3018 if (Output
== _CUPS_OUTPUT_CSV
)
3019 print_csv(attrptr
, num_displayed
, displayed
, widths
);
3021 print_line(attrptr
, num_displayed
, displayed
, widths
);
3023 while (attrptr
&& attrptr
->group_tag
> IPP_TAG_OPERATION
)
3024 attrptr
= attrptr
->next
;
3028 else if (!prev_pass
)
3030 if (Output
== _CUPS_OUTPUT_PLIST
)
3032 puts("<key>Errors</key>");
3035 for (error
= (char *)cupsArrayFirst(errors
);
3037 error
= (char *)cupsArrayNext(errors
))
3038 print_xml_string("string", error
);
3044 for (error
= (char *)cupsArrayFirst(errors
);
3046 error
= (char *)cupsArrayNext(errors
))
3047 printf(" %s\n", error
);
3051 if (num_displayed
> 0 && !Verbosity
&& response
&&
3052 Output
== _CUPS_OUTPUT_TEST
)
3054 for (attrptr
= response
->attrs
;
3056 attrptr
= attrptr
->next
)
3060 for (i
= 0; i
< num_displayed
; i
++)
3062 if (!strcmp(displayed
[i
], attrptr
->name
))
3064 print_attr(attrptr
, NULL
);
3074 if (Output
== _CUPS_OUTPUT_PLIST
)
3079 ippDelete(response
);
3082 for (i
= 0; i
< num_statuses
; i
++)
3084 if (statuses
[i
].if_defined
)
3085 free(statuses
[i
].if_defined
);
3086 if (statuses
[i
].if_not_defined
)
3087 free(statuses
[i
].if_not_defined
);
3088 if (statuses
[i
].define_match
)
3089 free(statuses
[i
].define_match
);
3090 if (statuses
[i
].define_no_match
)
3091 free(statuses
[i
].define_no_match
);
3095 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3098 if (expect
->of_type
)
3099 free(expect
->of_type
);
3100 if (expect
->same_count_as
)
3101 free(expect
->same_count_as
);
3102 if (expect
->if_defined
)
3103 free(expect
->if_defined
);
3104 if (expect
->if_not_defined
)
3105 free(expect
->if_not_defined
);
3106 if (expect
->with_value
)
3107 free(expect
->with_value
);
3108 if (expect
->define_match
)
3109 free(expect
->define_match
);
3110 if (expect
->define_no_match
)
3111 free(expect
->define_no_match
);
3112 if (expect
->define_value
)
3113 free(expect
->define_value
);
3117 for (i
= 0; i
< num_displayed
; i
++)
3121 if (!ignore_errors
&& !prev_pass
)
3127 cupsArrayDelete(errors
);
3134 ippDelete(response
);
3136 for (i
= 0; i
< num_statuses
; i
++)
3138 if (statuses
[i
].if_defined
)
3139 free(statuses
[i
].if_defined
);
3140 if (statuses
[i
].if_not_defined
)
3141 free(statuses
[i
].if_not_defined
);
3142 if (statuses
[i
].define_match
)
3143 free(statuses
[i
].define_match
);
3144 if (statuses
[i
].define_no_match
)
3145 free(statuses
[i
].define_no_match
);
3148 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3151 if (expect
->of_type
)
3152 free(expect
->of_type
);
3153 if (expect
->same_count_as
)
3154 free(expect
->same_count_as
);
3155 if (expect
->if_defined
)
3156 free(expect
->if_defined
);
3157 if (expect
->if_not_defined
)
3158 free(expect
->if_not_defined
);
3159 if (expect
->with_value
)
3160 free(expect
->with_value
);
3161 if (expect
->define_match
)
3162 free(expect
->define_match
);
3163 if (expect
->define_no_match
)
3164 free(expect
->define_no_match
);
3165 if (expect
->define_value
)
3166 free(expect
->define_value
);
3169 for (i
= 0; i
< num_displayed
; i
++)
3177 * 'expand_variables()' - Expand variables in a string.
3181 expand_variables(_cups_vars_t
*vars
, /* I - Variables */
3182 char *dst
, /* I - Destination string buffer */
3183 const char *src
, /* I - Source string */
3184 size_t dstsize
) /* I - Size of destination buffer */
3186 char *dstptr
, /* Pointer into destination */
3187 *dstend
, /* End of destination */
3188 temp
[256], /* Temporary string */
3189 *tempptr
; /* Pointer into temporary string */
3190 const char *value
; /* Value to substitute */
3194 dstend
= dst
+ dstsize
- 1;
3196 while (*src
&& dstptr
< dstend
)
3201 * Substitute a string/number...
3204 if (!strncmp(src
, "$$", 2))
3209 else if (!strncmp(src
, "$ENV[", 5))
3211 strlcpy(temp
, src
+ 5, sizeof(temp
));
3213 for (tempptr
= temp
; *tempptr
; tempptr
++)
3214 if (*tempptr
== ']')
3220 value
= getenv(temp
);
3221 src
+= tempptr
- temp
+ 5;
3225 strlcpy(temp
, src
+ 1, sizeof(temp
));
3227 for (tempptr
= temp
; *tempptr
; tempptr
++)
3228 if (!isalnum(*tempptr
& 255) && *tempptr
!= '-' && *tempptr
!= '_')
3234 if (!strcmp(temp
, "uri"))
3236 else if (!strcmp(temp
, "filename"))
3237 value
= vars
->filename
;
3238 else if (!strcmp(temp
, "scheme") || !strcmp(temp
, "method"))
3239 value
= vars
->scheme
;
3240 else if (!strcmp(temp
, "username"))
3241 value
= vars
->userpass
;
3242 else if (!strcmp(temp
, "hostname"))
3243 value
= vars
->hostname
;
3244 else if (!strcmp(temp
, "port"))
3246 snprintf(temp
, sizeof(temp
), "%d", vars
->port
);
3249 else if (!strcmp(temp
, "resource"))
3250 value
= vars
->resource
;
3251 else if (!strcmp(temp
, "user"))
3254 value
= get_variable(vars
, temp
);
3256 src
+= tempptr
- temp
+ 1;
3266 strlcpy(dstptr
, value
, dstend
- dstptr
+ 1);
3267 dstptr
+= strlen(dstptr
);
3279 * 'expect_matches()' - Return true if the tag matches the specification.
3282 static int /* O - 1 if matches, 0 otherwise */
3284 _cups_expect_t
*expect
, /* I - Expected attribute */
3285 ipp_tag_t value_tag
) /* I - Value tag for attribute */
3287 int match
; /* Match? */
3288 char *of_type
, /* Type name to match */
3289 *next
, /* Next name to match */
3290 sep
; /* Separator character */
3294 * If we don't expect a particular type, return immediately...
3297 if (!expect
->of_type
)
3301 * Parse the "of_type" value since the string can contain multiple attribute
3302 * types separated by "," or "|"...
3305 for (of_type
= expect
->of_type
, match
= 0; !match
&& *of_type
; of_type
= next
)
3308 * Find the next separator, and set it (temporarily) to nul if present.
3311 for (next
= of_type
; *next
&& *next
!= '|' && *next
!= ','; next
++);
3313 if ((sep
= *next
) != '\0')
3317 * Support some meta-types to make it easier to write the test file.
3320 if (!strcmp(of_type
, "text"))
3321 match
= value_tag
== IPP_TAG_TEXTLANG
|| value_tag
== IPP_TAG_TEXT
;
3322 else if (!strcmp(of_type
, "name"))
3323 match
= value_tag
== IPP_TAG_NAMELANG
|| value_tag
== IPP_TAG_NAME
;
3324 else if (!strcmp(of_type
, "collection"))
3325 match
= value_tag
== IPP_TAG_BEGIN_COLLECTION
;
3327 match
= value_tag
== ippTagValue(of_type
);
3330 * Restore the separator if we have one...
3342 * 'get_collection()' - Get a collection value from the current test file.
3345 static ipp_t
* /* O - Collection value */
3346 get_collection(_cups_vars_t
*vars
, /* I - Variables */
3347 FILE *fp
, /* I - File to read from */
3348 int *linenum
) /* IO - Line number */
3350 char token
[1024], /* Token from file */
3351 temp
[1024], /* Temporary string */
3352 attr
[128]; /* Attribute name */
3353 ipp_tag_t value
; /* Current value type */
3354 ipp_t
*col
= ippNew(); /* Collection value */
3355 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
3358 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
3360 if (!strcmp(token
, "}"))
3362 else if (!strcmp(token
, "{") && lastcol
)
3365 * Another collection value
3368 ipp_t
*subcol
= get_collection(vars
, fp
, linenum
);
3369 /* Collection value */
3373 ipp_attribute_t
*tempcol
; /* Pointer to new buffer */
3377 * Reallocate memory...
3380 if ((tempcol
= realloc(lastcol
, sizeof(ipp_attribute_t
) +
3381 (lastcol
->num_values
+ 1) *
3382 sizeof(_ipp_value_t
))) == NULL
)
3384 print_fatal_error("Unable to allocate memory on line %d.", *linenum
);
3388 if (tempcol
!= lastcol
)
3391 * Reset pointers in the list...
3395 col
->prev
->next
= tempcol
;
3397 col
->attrs
= tempcol
;
3399 lastcol
= col
->current
= col
->last
= tempcol
;
3402 lastcol
->values
[lastcol
->num_values
].collection
= subcol
;
3403 lastcol
->num_values
++;
3408 else if (!_cups_strcasecmp(token
, "MEMBER"))
3416 if (!get_token(fp
, token
, sizeof(token
), linenum
))
3418 print_fatal_error("Missing MEMBER value tag on line %d.", *linenum
);
3422 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
3424 print_fatal_error("Bad MEMBER value tag \"%s\" on line %d.", token
,
3429 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
3431 print_fatal_error("Missing MEMBER name on line %d.", *linenum
);
3435 if (!get_token(fp
, temp
, sizeof(temp
), linenum
))
3437 print_fatal_error("Missing MEMBER value on line %d.", *linenum
);
3441 expand_variables(vars
, token
, temp
, sizeof(token
));
3445 case IPP_TAG_BOOLEAN
:
3446 if (!_cups_strcasecmp(token
, "true"))
3447 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
3449 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, atoi(token
));
3452 case IPP_TAG_INTEGER
:
3454 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
3457 case IPP_TAG_RESOLUTION
:
3459 int xres
, /* X resolution */
3460 yres
; /* Y resolution */
3461 char units
[6]; /* Units */
3463 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
3464 (_cups_strcasecmp(units
, "dpi") &&
3465 _cups_strcasecmp(units
, "dpc") &&
3466 _cups_strcasecmp(units
, "dpcm") &&
3467 _cups_strcasecmp(units
, "other")))
3469 print_fatal_error("Bad resolution value \"%s\" on line %d.",
3474 if (!_cups_strcasecmp(units
, "dpi"))
3475 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
3477 else if (!_cups_strcasecmp(units
, "dpc") ||
3478 !_cups_strcasecmp(units
, "dpcm"))
3479 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
3482 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
3487 case IPP_TAG_RANGE
:
3489 int lowers
[4], /* Lower value */
3490 uppers
[4], /* Upper values */
3491 num_vals
; /* Number of values */
3494 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
3495 lowers
+ 0, uppers
+ 0,
3496 lowers
+ 1, uppers
+ 1,
3497 lowers
+ 2, uppers
+ 2,
3498 lowers
+ 3, uppers
+ 3);
3500 if ((num_vals
& 1) || num_vals
== 0)
3502 print_fatal_error("Bad rangeOfInteger value \"%s\" on line %d.",
3507 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
3512 case IPP_TAG_BEGIN_COLLECTION
:
3513 if (!strcmp(token
, "{"))
3515 ipp_t
*subcol
= get_collection(vars
, fp
, linenum
);
3516 /* Collection value */
3520 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
3528 print_fatal_error("Bad collection value on line %d.", *linenum
);
3532 case IPP_TAG_STRING
:
3533 ippAddOctetString(col
, IPP_TAG_ZERO
, attr
, token
, strlen(token
));
3537 if (!strchr(token
, ','))
3538 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
3542 * Multiple string values...
3545 int num_values
; /* Number of values */
3546 char *values
[100], /* Values */
3547 *ptr
; /* Pointer to next value */
3553 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
3556 values
[num_values
] = ptr
;
3560 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
3561 NULL
, (const char **)values
);
3571 * If we get here there was a parse error; free memory and return.
3583 * 'get_filename()' - Get a filename based on the current test file.
3586 static char * /* O - Filename */
3587 get_filename(const char *testfile
, /* I - Current test file */
3588 char *dst
, /* I - Destination filename */
3589 const char *src
, /* I - Source filename */
3590 size_t dstsize
) /* I - Size of destination buffer */
3592 char *dstptr
; /* Pointer into destination */
3593 _cups_globals_t
*cg
= _cupsGlobals();
3597 if (*src
== '<' && src
[strlen(src
) - 1] == '>')
3600 * Map <filename> to CUPS_DATADIR/ipptool/filename...
3603 snprintf(dst
, dstsize
, "%s/ipptool/%s", cg
->cups_datadir
, src
+ 1);
3604 dstptr
= dst
+ strlen(dst
) - 1;
3608 else if (*src
== '/' || !strchr(testfile
, '/')
3610 || (isalpha(*src
& 255) && src
[1] == ':')
3615 * Use the path as-is...
3618 strlcpy(dst
, src
, dstsize
);
3623 * Make path relative to testfile...
3626 strlcpy(dst
, testfile
, dstsize
);
3627 if ((dstptr
= strrchr(dst
, '/')) != NULL
)
3630 dstptr
= dst
; /* Should never happen */
3632 strlcpy(dstptr
, src
, dstsize
- (dstptr
- dst
));
3640 * 'get_token()' - Get a token from a file.
3643 static char * /* O - Token from file or NULL on EOF */
3644 get_token(FILE *fp
, /* I - File to read from */
3645 char *buf
, /* I - Buffer to read into */
3646 int buflen
, /* I - Length of buffer */
3647 int *linenum
) /* IO - Current line number */
3649 int ch
, /* Character from file */
3650 quote
; /* Quoting character */
3651 char *bufptr
, /* Pointer into buffer */
3652 *bufend
; /* End of buffer */
3658 * Skip whitespace...
3661 while (isspace(ch
= getc(fp
)))
3673 else if (ch
== '\'' || ch
== '\"')
3676 * Quoted text or regular expression...
3681 bufend
= buf
+ buflen
- 1;
3683 while ((ch
= getc(fp
)) != EOF
)
3688 * Escape next character...
3691 if (bufptr
< bufend
)
3694 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
3697 else if (ch
== quote
)
3699 else if (bufptr
< bufend
)
3713 while ((ch
= getc(fp
)) != EOF
)
3722 * Whitespace delimited text...
3728 bufend
= buf
+ buflen
- 1;
3730 while ((ch
= getc(fp
)) != EOF
)
3731 if (isspace(ch
) || ch
== '#')
3733 else if (bufptr
< bufend
)
3738 else if (ch
== '\n')
3750 * 'get_variable()' - Get the value of a variable.
3753 static char * /* O - Value or NULL */
3754 get_variable(_cups_vars_t
*vars
, /* I - Variables */
3755 const char *name
) /* I - Variable name */
3757 _cups_var_t key
, /* Search key */
3758 *match
; /* Matching variable, if any */
3761 key
.name
= (char *)name
;
3762 match
= cupsArrayFind(vars
->vars
, &key
);
3764 return (match
? match
->value
: NULL
);
3769 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
3773 static char * /* O - ISO 8601 date/time string */
3774 iso_date(ipp_uchar_t
*date
) /* I - IPP (RFC 1903) date/time value */
3776 time_t utctime
; /* UTC time since 1970 */
3777 struct tm
*utcdate
; /* UTC date/time */
3778 static char buffer
[255]; /* String buffer */
3781 utctime
= ippDateToTime(date
);
3782 utcdate
= gmtime(&utctime
);
3784 snprintf(buffer
, sizeof(buffer
), "%04d-%02d-%02dT%02d:%02d:%02dZ",
3785 utcdate
->tm_year
+ 1900, utcdate
->tm_mon
+ 1, utcdate
->tm_mday
,
3786 utcdate
->tm_hour
, utcdate
->tm_min
, utcdate
->tm_sec
);
3793 * 'password_cb()' - Password callback for authenticated tests.
3796 static const char * /* O - Password */
3797 password_cb(const char *prompt
) /* I - Prompt (unused) */
3806 * 'print_attr()' - Print an attribute on the screen.
3810 print_attr(ipp_attribute_t
*attr
, /* I - Attribute to print */
3811 ipp_tag_t
*group
) /* IO - Current group */
3813 int i
; /* Looping var */
3814 ipp_attribute_t
*colattr
; /* Collection attribute */
3817 if (Output
== _CUPS_OUTPUT_PLIST
)
3819 if (!attr
->name
|| (group
&& *group
!= attr
->group_tag
))
3821 if (attr
->group_tag
!= IPP_TAG_ZERO
)
3828 *group
= attr
->group_tag
;
3834 print_xml_string("key", attr
->name
);
3835 if (attr
->num_values
> 1)
3838 switch (attr
->value_tag
)
3840 case IPP_TAG_INTEGER
:
3842 for (i
= 0; i
< attr
->num_values
; i
++)
3843 if (Output
== _CUPS_OUTPUT_PLIST
)
3844 printf("<integer>%d</integer>\n", attr
->values
[i
].integer
);
3846 printf("%d ", attr
->values
[i
].integer
);
3849 case IPP_TAG_BOOLEAN
:
3850 for (i
= 0; i
< attr
->num_values
; i
++)
3851 if (Output
== _CUPS_OUTPUT_PLIST
)
3852 puts(attr
->values
[i
].boolean
? "<true />" : "<false />");
3853 else if (attr
->values
[i
].boolean
)
3854 fputs("true ", stdout
);
3856 fputs("false ", stdout
);
3859 case IPP_TAG_RANGE
:
3860 for (i
= 0; i
< attr
->num_values
; i
++)
3861 if (Output
== _CUPS_OUTPUT_PLIST
)
3862 printf("<dict><key>lower</key><integer>%d</integer>"
3863 "<key>upper</key><integer>%d</integer></dict>\n",
3864 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
3866 printf("%d-%d ", attr
->values
[i
].range
.lower
,
3867 attr
->values
[i
].range
.upper
);
3870 case IPP_TAG_RESOLUTION
:
3871 for (i
= 0; i
< attr
->num_values
; i
++)
3872 if (Output
== _CUPS_OUTPUT_PLIST
)
3873 printf("<dict><key>xres</key><integer>%d</integer>"
3874 "<key>yres</key><integer>%d</integer>"
3875 "<key>units</key><string>%s</string></dict>\n",
3876 attr
->values
[i
].resolution
.xres
,
3877 attr
->values
[i
].resolution
.yres
,
3878 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
3881 printf("%dx%d%s ", attr
->values
[i
].resolution
.xres
,
3882 attr
->values
[i
].resolution
.yres
,
3883 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
3888 for (i
= 0; i
< attr
->num_values
; i
++)
3889 if (Output
== _CUPS_OUTPUT_PLIST
)
3890 printf("<date>%s</date>\n", iso_date(attr
->values
[i
].date
));
3892 printf("%s ", iso_date(attr
->values
[i
].date
));
3895 case IPP_TAG_STRING
:
3896 for (i
= 0; i
< attr
->num_values
; i
++)
3898 if (Output
== _CUPS_OUTPUT_PLIST
)
3900 char buffer
[IPP_MAX_LENGTH
* 5 / 4 + 1];
3903 printf("<data>%s</data>\n",
3904 httpEncode64_2(buffer
, sizeof(buffer
),
3905 attr
->values
[i
].unknown
.data
,
3906 attr
->values
[i
].unknown
.length
));
3910 char *ptr
, /* Pointer into data */
3911 *end
; /* End of data */
3914 for (ptr
= attr
->values
[i
].unknown
.data
,
3915 end
= ptr
+ attr
->values
[i
].unknown
.length
;
3919 if (*ptr
== '\\' || *ptr
== '\"')
3920 printf("\\%c", *ptr
);
3921 else if (!isprint(*ptr
& 255))
3922 printf("\\%03o", *ptr
& 255);
3933 case IPP_TAG_KEYWORD
:
3934 case IPP_TAG_CHARSET
:
3936 case IPP_TAG_MIMETYPE
:
3937 case IPP_TAG_LANGUAGE
:
3938 for (i
= 0; i
< attr
->num_values
; i
++)
3939 if (Output
== _CUPS_OUTPUT_PLIST
)
3940 print_xml_string("string", attr
->values
[i
].string
.text
);
3942 printf("\"%s\" ", attr
->values
[i
].string
.text
);
3945 case IPP_TAG_TEXTLANG
:
3946 case IPP_TAG_NAMELANG
:
3947 for (i
= 0; i
< attr
->num_values
; i
++)
3948 if (Output
== _CUPS_OUTPUT_PLIST
)
3950 fputs("<dict><key>language</key><string>", stdout
);
3951 print_xml_string(NULL
, attr
->values
[i
].string
.language
);
3952 fputs("</string><key>string</key><string>", stdout
);
3953 print_xml_string(NULL
, attr
->values
[i
].string
.text
);
3954 puts("</string></dict>");
3957 printf("\"%s\"[%s] ", attr
->values
[i
].string
.text
,
3958 attr
->values
[i
].string
.language
);
3961 case IPP_TAG_BEGIN_COLLECTION
:
3962 for (i
= 0; i
< attr
->num_values
; i
++)
3964 if (Output
== _CUPS_OUTPUT_PLIST
)
3967 for (colattr
= attr
->values
[i
].collection
->attrs
;
3969 colattr
= colattr
->next
)
3970 print_attr(colattr
, NULL
);
3978 print_col(attr
->values
[i
].collection
);
3984 if (Output
== _CUPS_OUTPUT_PLIST
)
3985 printf("<string><<%s>></string>\n",
3986 ippTagString(attr
->value_tag
));
3988 fputs(ippTagString(attr
->value_tag
), stdout
);
3992 if (attr
->num_values
> 1)
3997 char buffer
[8192]; /* Value buffer */
3999 if (Output
== _CUPS_OUTPUT_TEST
)
4003 puts(" -- separator --");
4007 printf(" %s (%s%s) = ", attr
->name
,
4008 attr
->num_values
> 1 ? "1setOf " : "",
4009 ippTagString(attr
->value_tag
));
4012 ippAttributeString(attr
, buffer
, sizeof(buffer
));
4019 * 'print_col()' - Print a collection attribute on the screen.
4023 print_col(ipp_t
*col
) /* I - Collection attribute to print */
4025 int i
; /* Looping var */
4026 ipp_attribute_t
*attr
; /* Current attribute in collection */
4029 fputs("{ ", stdout
);
4030 for (attr
= col
->attrs
; attr
; attr
= attr
->next
)
4032 printf("%s (%s%s) = ", attr
->name
, attr
->num_values
> 1 ? "1setOf " : "",
4033 ippTagString(attr
->value_tag
));
4035 switch (attr
->value_tag
)
4037 case IPP_TAG_INTEGER
:
4039 for (i
= 0; i
< attr
->num_values
; i
++)
4040 printf("%d ", attr
->values
[i
].integer
);
4043 case IPP_TAG_BOOLEAN
:
4044 for (i
= 0; i
< attr
->num_values
; i
++)
4045 if (attr
->values
[i
].boolean
)
4051 case IPP_TAG_NOVALUE
:
4055 case IPP_TAG_RANGE
:
4056 for (i
= 0; i
< attr
->num_values
; i
++)
4057 printf("%d-%d ", attr
->values
[i
].range
.lower
,
4058 attr
->values
[i
].range
.upper
);
4061 case IPP_TAG_RESOLUTION
:
4062 for (i
= 0; i
< attr
->num_values
; i
++)
4063 printf("%dx%d%s ", attr
->values
[i
].resolution
.xres
,
4064 attr
->values
[i
].resolution
.yres
,
4065 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
4069 case IPP_TAG_STRING
:
4072 case IPP_TAG_KEYWORD
:
4073 case IPP_TAG_CHARSET
:
4075 case IPP_TAG_MIMETYPE
:
4076 case IPP_TAG_LANGUAGE
:
4077 for (i
= 0; i
< attr
->num_values
; i
++)
4078 printf("\"%s\" ", attr
->values
[i
].string
.text
);
4081 case IPP_TAG_TEXTLANG
:
4082 case IPP_TAG_NAMELANG
:
4083 for (i
= 0; i
< attr
->num_values
; i
++)
4084 printf("\"%s\"[%s] ", attr
->values
[i
].string
.text
,
4085 attr
->values
[i
].string
.language
);
4088 case IPP_TAG_BEGIN_COLLECTION
:
4089 for (i
= 0; i
< attr
->num_values
; i
++)
4091 print_col(attr
->values
[i
].collection
);
4097 break; /* anti-compiler-warning-code */
4106 * 'print_csv()' - Print a line of CSV text.
4111 ipp_attribute_t
*attr
, /* I - First attribute for line */
4112 int num_displayed
, /* I - Number of attributes to display */
4113 char **displayed
, /* I - Attributes to display */
4114 size_t *widths
) /* I - Column widths */
4116 int i
; /* Looping var */
4117 size_t maxlength
; /* Max length of all columns */
4118 char *buffer
, /* String buffer */
4119 *bufptr
; /* Pointer into buffer */
4120 ipp_attribute_t
*current
; /* Current attribute */
4124 * Get the maximum string length we have to show and allocate...
4127 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4128 if (widths
[i
] > maxlength
)
4129 maxlength
= widths
[i
];
4133 if ((buffer
= malloc(maxlength
)) == NULL
)
4137 * Loop through the attributes to display...
4142 for (i
= 0; i
< num_displayed
; i
++)
4149 for (current
= attr
; current
; current
= current
->next
)
4153 else if (!strcmp(current
->name
, displayed
[i
]))
4155 ippAttributeString(current
, buffer
, maxlength
);
4160 if (strchr(buffer
, ',') != NULL
|| strchr(buffer
, '\"') != NULL
||
4161 strchr(buffer
, '\\') != NULL
)
4164 for (bufptr
= buffer
; *bufptr
; bufptr
++)
4166 if (*bufptr
== '\\' || *bufptr
== '\"')
4173 fputs(buffer
, stdout
);
4179 for (i
= 0; i
< num_displayed
; i
++)
4184 fputs(displayed
[i
], stdout
);
4194 * 'print_fatal_error()' - Print a fatal error message.
4198 print_fatal_error(const char *s
, /* I - Printf-style format string */
4199 ...) /* I - Additional arguments as needed */
4201 char buffer
[10240]; /* Format buffer */
4202 va_list ap
; /* Pointer to arguments */
4206 * Format the error message...
4210 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
4217 if (Output
== _CUPS_OUTPUT_PLIST
)
4220 print_xml_trailer(0, buffer
);
4223 _cupsLangPrintf(stderr
, "ipptool: %s", buffer
);
4228 * 'print_line()' - Print a line of formatted or CSV text.
4233 ipp_attribute_t
*attr
, /* I - First attribute for line */
4234 int num_displayed
, /* I - Number of attributes to display */
4235 char **displayed
, /* I - Attributes to display */
4236 size_t *widths
) /* I - Column widths */
4238 int i
; /* Looping var */
4239 size_t maxlength
; /* Max length of all columns */
4240 char *buffer
; /* String buffer */
4241 ipp_attribute_t
*current
; /* Current attribute */
4245 * Get the maximum string length we have to show and allocate...
4248 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4249 if (widths
[i
] > maxlength
)
4250 maxlength
= widths
[i
];
4254 if ((buffer
= malloc(maxlength
)) == NULL
)
4258 * Loop through the attributes to display...
4263 for (i
= 0; i
< num_displayed
; i
++)
4270 for (current
= attr
; current
; current
= current
->next
)
4274 else if (!strcmp(current
->name
, displayed
[i
]))
4276 ippAttributeString(current
, buffer
, maxlength
);
4281 printf("%*s", (int)-widths
[i
], buffer
);
4287 for (i
= 0; i
< num_displayed
; i
++)
4292 printf("%*s", (int)-widths
[i
], displayed
[i
]);
4296 for (i
= 0; i
< num_displayed
; i
++)
4301 memset(buffer
, '-', widths
[i
]);
4302 buffer
[widths
[i
]] = '\0';
4303 fputs(buffer
, stdout
);
4313 * 'print_xml_header()' - Print a standard XML plist header.
4317 print_xml_header(void)
4321 puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
4322 puts("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
4323 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
4324 puts("<plist version=\"1.0\">");
4326 puts("<key>Transfer</key>");
4327 printf("<string>%s</string>\n",
4328 Transfer
== _CUPS_TRANSFER_AUTO
? "auto" :
4329 Transfer
== _CUPS_TRANSFER_CHUNKED
? "chunked" : "length");
4330 puts("<key>Tests</key>");
4339 * 'print_xml_string()' - Print an XML string with escaping.
4343 print_xml_string(const char *element
, /* I - Element name or NULL */
4344 const char *s
) /* I - String to print */
4347 printf("<%s>", element
);
4352 fputs("&", stdout
);
4354 fputs("<", stdout
);
4356 fputs(">", stdout
);
4357 else if ((*s
& 0xe0) == 0xc0)
4360 * Validate UTF-8 two-byte sequence...
4363 if ((s
[1] & 0xc0) != 0x80)
4374 else if ((*s
& 0xf0) == 0xe0)
4377 * Validate UTF-8 three-byte sequence...
4380 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80)
4392 else if ((*s
& 0xf8) == 0xf0)
4395 * Validate UTF-8 four-byte sequence...
4398 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80 ||
4399 (s
[3] & 0xc0) != 0x80)
4412 else if ((*s
& 0x80) || (*s
< ' ' && !isspace(*s
& 255)))
4415 * Invalid control character...
4427 printf("</%s>\n", element
);
4432 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
4436 print_xml_trailer(int success
, /* I - 1 on success, 0 on failure */
4437 const char *message
) /* I - Error message or NULL */
4442 puts("<key>Successful</key>");
4443 puts(success
? "<true />" : "<false />");
4446 puts("<key>ErrorMessage</key>");
4447 print_xml_string("string", message
);
4458 * 'set_variable()' - Set a variable value.
4462 set_variable(_cups_vars_t
*vars
, /* I - Variables */
4463 const char *name
, /* I - Variable name */
4464 const char *value
) /* I - Value string */
4466 _cups_var_t key
, /* Search key */
4467 *var
; /* New variable */
4470 if (!_cups_strcasecmp(name
, "filename"))
4473 free(vars
->filename
);
4475 vars
->filename
= strdup(value
);
4479 key
.name
= (char *)name
;
4480 if ((var
= cupsArrayFind(vars
->vars
, &key
)) != NULL
)
4483 var
->value
= strdup(value
);
4485 else if ((var
= malloc(sizeof(_cups_var_t
))) == NULL
)
4487 print_fatal_error("Unable to allocate memory for variable \"%s\".", name
);
4492 var
->name
= strdup(name
);
4493 var
->value
= strdup(value
);
4495 cupsArrayAdd(vars
->vars
, var
);
4502 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
4506 sigterm_handler(int sig
) /* I - Signal number (unused) */
4512 signal(SIGINT
, SIG_DFL
);
4513 signal(SIGTERM
, SIG_DFL
);
4519 * 'timeout_cb()' - Handle HTTP timeouts.
4522 static int /* O - 1 to continue, 0 to cancel */
4523 timeout_cb(http_t
*http
, /* I - Connection to server (unused) */
4524 void *user_data
) /* I - User data (unused) */
4529 /* Always cancel on timeout */
4535 * 'usage()' - Show program usage.
4541 _cupsLangPuts(stderr
, _("Usage: ipptool [options] URI filename [ ... "
4543 _cupsLangPuts(stderr
, _("Options:"));
4544 _cupsLangPuts(stderr
, _(" -4 Connect using IPv4."));
4545 _cupsLangPuts(stderr
, _(" -6 Connect using IPv6."));
4546 _cupsLangPuts(stderr
, _(" -C Send requests using "
4547 "chunking (default)."));
4548 _cupsLangPuts(stdout
, _(" -E Test with HTTP Upgrade to "
4550 _cupsLangPuts(stderr
, _(" -I Ignore errors."));
4551 _cupsLangPuts(stderr
, _(" -L Send requests using "
4552 "content-length."));
4553 _cupsLangPuts(stderr
, _(" -S Test with SSL "
4555 _cupsLangPuts(stderr
, _(" -T seconds Set the receive/send "
4556 "timeout in seconds."));
4557 _cupsLangPuts(stderr
, _(" -V version Set default IPP "
4559 _cupsLangPuts(stderr
, _(" -X Produce XML plist instead "
4561 _cupsLangPuts(stderr
, _(" -d name=value Set named variable to "
4563 _cupsLangPuts(stderr
, _(" -f filename Set default request "
4565 _cupsLangPuts(stderr
, _(" -i seconds Repeat the last file with "
4566 "the given time interval."));
4567 _cupsLangPuts(stderr
, _(" -n count Repeat the last file the "
4568 "given number of times."));
4569 _cupsLangPuts(stderr
, _(" -q Run silently."));
4570 _cupsLangPuts(stderr
, _(" -t Produce a test report."));
4571 _cupsLangPuts(stderr
, _(" -v Be verbose."));
4578 * 'validate_attr()' - Determine whether an attribute is valid.
4581 static int /* O - 1 if valid, 0 otherwise */
4582 validate_attr(cups_array_t
*errors
, /* I - Errors array */
4583 ipp_attribute_t
*attr
) /* I - Attribute to validate */
4585 int i
; /* Looping var */
4586 char scheme
[64], /* Scheme from URI */
4587 userpass
[256], /* Username/password from URI */
4588 hostname
[256], /* Hostname from URI */
4589 resource
[1024]; /* Resource from URI */
4590 int port
, /* Port number from URI */
4591 uri_status
, /* URI separation status */
4592 valid
= 1; /* Is the attribute valid? */
4593 const char *ptr
; /* Pointer into string */
4594 ipp_attribute_t
*colattr
; /* Collection attribute */
4595 regex_t re
; /* Regular expression */
4596 ipp_uchar_t
*date
; /* Current date value */
4607 * Validate the attribute name.
4610 for (ptr
= attr
->name
; *ptr
; ptr
++)
4611 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' && *ptr
!= '_')
4614 if (*ptr
|| ptr
== attr
->name
)
4619 "\"%s\": Bad attribute name - invalid character "
4620 "(RFC 2911 section 4.1.3).", attr
->name
);
4623 if ((ptr
- attr
->name
) > 255)
4628 "\"%s\": Bad attribute name - bad length "
4629 "(RFC 2911 section 4.1.3).", attr
->name
);
4632 switch (attr
->value_tag
)
4634 case IPP_TAG_INTEGER
:
4637 case IPP_TAG_BOOLEAN
:
4638 for (i
= 0; i
< attr
->num_values
; i
++)
4640 if (attr
->values
[i
].boolean
!= 0 &&
4641 attr
->values
[i
].boolean
!= 1)
4646 "\"%s\": Bad boolen value %d "
4647 "(RFC 2911 section 4.1.11).", attr
->name
,
4648 attr
->values
[i
].boolean
);
4654 for (i
= 0; i
< attr
->num_values
; i
++)
4656 if (attr
->values
[i
].integer
< 1)
4661 "\"%s\": Bad enum value %d - out of range "
4662 "(RFC 2911 section 4.1.4).", attr
->name
,
4663 attr
->values
[i
].integer
);
4668 case IPP_TAG_STRING
:
4669 for (i
= 0; i
< attr
->num_values
; i
++)
4671 if (attr
->values
[i
].unknown
.length
> IPP_MAX_OCTETSTRING
)
4676 "\"%s\": Bad octetString value - bad length %d "
4677 "(RFC 2911 section 4.1.10).", attr
->name
,
4678 attr
->values
[i
].unknown
.length
);
4684 for (i
= 0; i
< attr
->num_values
; i
++)
4686 date
= attr
->values
[i
].date
;
4688 if (date
[2] < 1 || date
[2] > 12)
4693 "\"%s\": Bad dateTime month %u "
4694 "(RFC 2911 section 4.1.14).", attr
->name
, date
[2]);
4697 if (date
[3] < 1 || date
[3] > 31)
4702 "\"%s\": Bad dateTime day %u "
4703 "(RFC 2911 section 4.1.14).", attr
->name
, date
[3]);
4711 "\"%s\": Bad dateTime hours %u "
4712 "(RFC 2911 section 4.1.14).", attr
->name
, date
[4]);
4720 "\"%s\": Bad dateTime minutes %u "
4721 "(RFC 2911 section 4.1.14).", attr
->name
, date
[5]);
4729 "\"%s\": Bad dateTime seconds %u "
4730 "(RFC 2911 section 4.1.14).", attr
->name
, date
[6]);
4738 "\"%s\": Bad dateTime deciseconds %u "
4739 "(RFC 2911 section 4.1.14).", attr
->name
, date
[7]);
4742 if (date
[8] != '-' && date
[8] != '+')
4747 "\"%s\": Bad dateTime UTC sign '%c' "
4748 "(RFC 2911 section 4.1.14).", attr
->name
, date
[8]);
4756 "\"%s\": Bad dateTime UTC hours %u "
4757 "(RFC 2911 section 4.1.14).", attr
->name
, date
[9]);
4765 "\"%s\": Bad dateTime UTC minutes %u "
4766 "(RFC 2911 section 4.1.14).", attr
->name
, date
[10]);
4771 case IPP_TAG_RESOLUTION
:
4772 for (i
= 0; i
< attr
->num_values
; i
++)
4774 if (attr
->values
[i
].resolution
.xres
<= 0)
4779 "\"%s\": Bad resolution value %dx%d%s - cross "
4780 "feed resolution must be positive "
4781 "(RFC 2911 section 4.1.15).", attr
->name
,
4782 attr
->values
[i
].resolution
.xres
,
4783 attr
->values
[i
].resolution
.yres
,
4784 attr
->values
[i
].resolution
.units
==
4785 IPP_RES_PER_INCH
? "dpi" :
4786 attr
->values
[i
].resolution
.units
==
4787 IPP_RES_PER_CM
? "dpcm" : "unknown");
4790 if (attr
->values
[i
].resolution
.yres
<= 0)
4795 "\"%s\": Bad resolution value %dx%d%s - feed "
4796 "resolution must be positive "
4797 "(RFC 2911 section 4.1.15).", attr
->name
,
4798 attr
->values
[i
].resolution
.xres
,
4799 attr
->values
[i
].resolution
.yres
,
4800 attr
->values
[i
].resolution
.units
==
4801 IPP_RES_PER_INCH
? "dpi" :
4802 attr
->values
[i
].resolution
.units
==
4803 IPP_RES_PER_CM
? "dpcm" : "unknown");
4806 if (attr
->values
[i
].resolution
.units
!= IPP_RES_PER_INCH
&&
4807 attr
->values
[i
].resolution
.units
!= IPP_RES_PER_CM
)
4812 "\"%s\": Bad resolution value %dx%d%s - bad "
4813 "units value (RFC 2911 section 4.1.15).",
4814 attr
->name
, attr
->values
[i
].resolution
.xres
,
4815 attr
->values
[i
].resolution
.yres
,
4816 attr
->values
[i
].resolution
.units
==
4817 IPP_RES_PER_INCH
? "dpi" :
4818 attr
->values
[i
].resolution
.units
==
4819 IPP_RES_PER_CM
? "dpcm" : "unknown");
4824 case IPP_TAG_RANGE
:
4825 for (i
= 0; i
< attr
->num_values
; i
++)
4827 if (attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
4832 "\"%s\": Bad rangeOfInteger value %d-%d - lower "
4833 "greater than upper (RFC 2911 section 4.1.13).",
4834 attr
->name
, attr
->values
[i
].range
.lower
,
4835 attr
->values
[i
].range
.upper
);
4840 case IPP_TAG_BEGIN_COLLECTION
:
4841 for (i
= 0; i
< attr
->num_values
; i
++)
4843 for (colattr
= attr
->values
[i
].collection
->attrs
;
4845 colattr
= colattr
->next
)
4847 if (!validate_attr(NULL
, colattr
))
4854 if (colattr
&& errors
)
4856 add_stringf(errors
, "\"%s\": Bad collection value.", attr
->name
);
4860 validate_attr(errors
, colattr
);
4861 colattr
= colattr
->next
;
4868 case IPP_TAG_TEXTLANG
:
4869 for (i
= 0; i
< attr
->num_values
; i
++)
4871 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4873 if ((*ptr
& 0xe0) == 0xc0)
4876 if ((*ptr
& 0xc0) != 0x80)
4879 else if ((*ptr
& 0xf0) == 0xe0)
4882 if ((*ptr
& 0xc0) != 0x80)
4885 if ((*ptr
& 0xc0) != 0x80)
4888 else if ((*ptr
& 0xf8) == 0xf0)
4891 if ((*ptr
& 0xc0) != 0x80)
4894 if ((*ptr
& 0xc0) != 0x80)
4897 if ((*ptr
& 0xc0) != 0x80)
4900 else if (*ptr
& 0x80)
4909 "\"%s\": Bad text value \"%s\" - bad UTF-8 "
4910 "sequence (RFC 2911 section 4.1.1).", attr
->name
,
4911 attr
->values
[i
].string
.text
);
4914 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_TEXT
- 1))
4919 "\"%s\": Bad text value \"%s\" - bad length %d "
4920 "(RFC 2911 section 4.1.1).", attr
->name
,
4921 attr
->values
[i
].string
.text
,
4922 (int)strlen(attr
->values
[i
].string
.text
));
4928 case IPP_TAG_NAMELANG
:
4929 for (i
= 0; i
< attr
->num_values
; i
++)
4931 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4933 if ((*ptr
& 0xe0) == 0xc0)
4936 if ((*ptr
& 0xc0) != 0x80)
4939 else if ((*ptr
& 0xf0) == 0xe0)
4942 if ((*ptr
& 0xc0) != 0x80)
4945 if ((*ptr
& 0xc0) != 0x80)
4948 else if ((*ptr
& 0xf8) == 0xf0)
4951 if ((*ptr
& 0xc0) != 0x80)
4954 if ((*ptr
& 0xc0) != 0x80)
4957 if ((*ptr
& 0xc0) != 0x80)
4960 else if (*ptr
& 0x80)
4969 "\"%s\": Bad name value \"%s\" - bad UTF-8 "
4970 "sequence (RFC 2911 section 4.1.2).", attr
->name
,
4971 attr
->values
[i
].string
.text
);
4974 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_NAME
- 1))
4979 "\"%s\": Bad name value \"%s\" - bad length %d "
4980 "(RFC 2911 section 4.1.2).", attr
->name
,
4981 attr
->values
[i
].string
.text
,
4982 (int)strlen(attr
->values
[i
].string
.text
));
4987 case IPP_TAG_KEYWORD
:
4988 for (i
= 0; i
< attr
->num_values
; i
++)
4990 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4991 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' &&
4995 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5000 "\"%s\": Bad keyword value \"%s\" - invalid "
5001 "character (RFC 2911 section 4.1.3).",
5002 attr
->name
, attr
->values
[i
].string
.text
);
5005 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_KEYWORD
- 1))
5010 "\"%s\": Bad keyword value \"%s\" - bad "
5011 "length %d (RFC 2911 section 4.1.3).",
5012 attr
->name
, attr
->values
[i
].string
.text
,
5013 (int)strlen(attr
->values
[i
].string
.text
));
5019 for (i
= 0; i
< attr
->num_values
; i
++)
5021 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
,
5022 attr
->values
[i
].string
.text
,
5023 scheme
, sizeof(scheme
),
5024 userpass
, sizeof(userpass
),
5025 hostname
, sizeof(hostname
),
5026 &port
, resource
, sizeof(resource
));
5028 if (uri_status
< HTTP_URI_OK
)
5033 "\"%s\": Bad URI value \"%s\" - %s "
5034 "(RFC 2911 section 4.1.5).", attr
->name
,
5035 attr
->values
[i
].string
.text
,
5036 URIStatusStrings
[uri_status
-
5037 HTTP_URI_OVERFLOW
]);
5040 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_URI
- 1))
5045 "\"%s\": Bad URI value \"%s\" - bad length %d "
5046 "(RFC 2911 section 4.1.5).", attr
->name
,
5047 attr
->values
[i
].string
.text
,
5048 (int)strlen(attr
->values
[i
].string
.text
));
5053 case IPP_TAG_URISCHEME
:
5054 for (i
= 0; i
< attr
->num_values
; i
++)
5056 ptr
= attr
->values
[i
].string
.text
;
5057 if (islower(*ptr
& 255))
5059 for (ptr
++; *ptr
; ptr
++)
5060 if (!islower(*ptr
& 255) && !isdigit(*ptr
& 255) &&
5061 *ptr
!= '+' && *ptr
!= '-' && *ptr
!= '.')
5065 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5070 "\"%s\": Bad uriScheme value \"%s\" - bad "
5071 "characters (RFC 2911 section 4.1.6).",
5072 attr
->name
, attr
->values
[i
].string
.text
);
5075 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_URISCHEME
- 1))
5080 "\"%s\": Bad uriScheme value \"%s\" - bad "
5081 "length %d (RFC 2911 section 4.1.6).",
5082 attr
->name
, attr
->values
[i
].string
.text
,
5083 (int)strlen(attr
->values
[i
].string
.text
));
5088 case IPP_TAG_CHARSET
:
5089 for (i
= 0; i
< attr
->num_values
; i
++)
5091 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5092 if (!isprint(*ptr
& 255) || isupper(*ptr
& 255) ||
5093 isspace(*ptr
& 255))
5096 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5101 "\"%s\": Bad charset value \"%s\" - bad "
5102 "characters (RFC 2911 section 4.1.7).",
5103 attr
->name
, attr
->values
[i
].string
.text
);
5106 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_CHARSET
- 1))
5111 "\"%s\": Bad charset value \"%s\" - bad "
5112 "length %d (RFC 2911 section 4.1.7).",
5113 attr
->name
, attr
->values
[i
].string
.text
,
5114 (int)strlen(attr
->values
[i
].string
.text
));
5119 case IPP_TAG_LANGUAGE
:
5121 * The following regular expression is derived from the ABNF for
5122 * language tags in RFC 4646. All I can say is that this is the
5123 * easiest way to check the values...
5126 if ((i
= regcomp(&re
,
5128 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
5130 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
5131 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
5132 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
5133 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
5134 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
5136 "x(-[a-z0-9]{1,8})+" /* privateuse */
5138 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
5140 REG_NOSUB
| REG_EXTENDED
)) != 0)
5142 char temp
[256]; /* Temporary error string */
5144 regerror(i
, &re
, temp
, sizeof(temp
));
5145 print_fatal_error("Unable to compile naturalLanguage regular "
5146 "expression: %s.", temp
);
5150 for (i
= 0; i
< attr
->num_values
; i
++)
5152 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5157 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5158 "characters (RFC 2911 section 4.1.8).",
5159 attr
->name
, attr
->values
[i
].string
.text
);
5162 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_LANGUAGE
- 1))
5167 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5168 "length %d (RFC 2911 section 4.1.8).",
5169 attr
->name
, attr
->values
[i
].string
.text
,
5170 (int)strlen(attr
->values
[i
].string
.text
));
5177 case IPP_TAG_MIMETYPE
:
5179 * The following regular expression is derived from the ABNF for
5180 * language tags in RFC 2045 and 4288. All I can say is that this is
5181 * the easiest way to check the values...
5184 if ((i
= regcomp(&re
,
5186 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
5188 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
5189 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
5190 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
5193 REG_NOSUB
| REG_EXTENDED
)) != 0)
5195 char temp
[256]; /* Temporary error string */
5197 regerror(i
, &re
, temp
, sizeof(temp
));
5198 print_fatal_error("Unable to compile mimeMediaType regular "
5199 "expression: %s.", temp
);
5203 for (i
= 0; i
< attr
->num_values
; i
++)
5205 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5210 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5211 "characters (RFC 2911 section 4.1.9).",
5212 attr
->name
, attr
->values
[i
].string
.text
);
5215 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_MIMETYPE
- 1))
5220 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5221 "length %d (RFC 2911 section 4.1.9).",
5222 attr
->name
, attr
->values
[i
].string
.text
,
5223 (int)strlen(attr
->values
[i
].string
.text
));
5239 * 'with_value()' - Test a WITH-VALUE predicate.
5242 static int /* O - 1 on match, 0 on non-match */
5243 with_value(cups_array_t
*errors
, /* I - Errors array */
5244 char *value
, /* I - Value string */
5245 int flags
, /* I - Flags for match */
5246 ipp_attribute_t
*attr
, /* I - Attribute to compare */
5247 char *matchbuf
, /* I - Buffer to hold matching value */
5248 size_t matchlen
) /* I - Length of match buffer */
5250 int i
, /* Looping var */
5252 char *valptr
; /* Pointer into value */
5256 match
= (flags
& _CUPS_WITH_ALL
) ? 1 : 0;
5259 * NULL matches everything.
5262 if (!value
|| !*value
)
5266 * Compare the value string to the attribute value.
5269 switch (attr
->value_tag
)
5271 case IPP_TAG_INTEGER
:
5273 for (i
= 0; i
< attr
->num_values
; i
++)
5275 char op
, /* Comparison operator */
5276 *nextptr
; /* Next pointer */
5277 int intvalue
, /* Integer value */
5278 valmatch
= 0; /* Does the current value match? */
5282 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5283 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5284 *valptr
== '=' || *valptr
== '>')
5287 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5289 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5297 intvalue
= strtol(valptr
, &nextptr
, 0);
5298 if (nextptr
== valptr
)
5302 if ((op
== '=' && attr
->values
[i
].integer
== intvalue
) ||
5303 (op
== '<' && attr
->values
[i
].integer
< intvalue
) ||
5304 (op
== '>' && attr
->values
[i
].integer
> intvalue
))
5307 snprintf(matchbuf
, matchlen
, "%d",
5308 attr
->values
[i
].integer
);
5315 if (flags
& _CUPS_WITH_ALL
)
5330 if (!match
&& errors
)
5332 for (i
= 0; i
< attr
->num_values
; i
++)
5333 add_stringf(errors
, "GOT: %s=%d", attr
->name
,
5334 attr
->values
[i
].integer
);
5338 case IPP_TAG_RANGE
:
5339 for (i
= 0; i
< attr
->num_values
; i
++)
5341 char op
, /* Comparison operator */
5342 *nextptr
; /* Next pointer */
5343 int intvalue
, /* Integer value */
5344 valmatch
= 0; /* Does the current value match? */
5348 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5349 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5350 *valptr
== '=' || *valptr
== '>')
5353 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5355 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5363 intvalue
= strtol(valptr
, &nextptr
, 0);
5364 if (nextptr
== valptr
)
5368 if ((op
== '=' && (attr
->values
[i
].range
.lower
== intvalue
||
5369 attr
->values
[i
].range
.upper
== intvalue
)) ||
5370 (op
== '<' && attr
->values
[i
].range
.upper
< intvalue
) ||
5371 (op
== '>' && attr
->values
[i
].range
.upper
> intvalue
))
5374 snprintf(matchbuf
, matchlen
, "%d-%d",
5375 attr
->values
[0].range
.lower
,
5376 attr
->values
[0].range
.upper
);
5383 if (flags
& _CUPS_WITH_ALL
)
5398 if (!match
&& errors
)
5400 for (i
= 0; i
< attr
->num_values
; i
++)
5401 add_stringf(errors
, "GOT: %s=%d-%d", attr
->name
,
5402 attr
->values
[i
].range
.lower
,
5403 attr
->values
[i
].range
.upper
);
5407 case IPP_TAG_BOOLEAN
:
5408 for (i
= 0; i
< attr
->num_values
; i
++)
5410 if (!strcmp(value
, "true") == attr
->values
[i
].boolean
)
5413 strlcpy(matchbuf
, value
, matchlen
);
5415 if (!(flags
& _CUPS_WITH_ALL
))
5421 else if (flags
& _CUPS_WITH_ALL
)
5428 if (!match
&& errors
)
5430 for (i
= 0; i
< attr
->num_values
; i
++)
5431 add_stringf(errors
, "GOT: %s=%s", attr
->name
,
5432 attr
->values
[i
].boolean
? "true" : "false");
5436 case IPP_TAG_NOVALUE
:
5437 case IPP_TAG_UNKNOWN
:
5440 case IPP_TAG_CHARSET
:
5441 case IPP_TAG_KEYWORD
:
5442 case IPP_TAG_LANGUAGE
:
5443 case IPP_TAG_MIMETYPE
:
5445 case IPP_TAG_NAMELANG
:
5447 case IPP_TAG_TEXTLANG
:
5449 case IPP_TAG_URISCHEME
:
5450 if (flags
& _CUPS_WITH_REGEX
)
5453 * Value is an extended, case-sensitive POSIX regular expression...
5456 regex_t re
; /* Regular expression */
5458 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
5460 char temp
[256]; /* Temporary string */
5462 regerror(i
, &re
, temp
, sizeof(temp
));
5464 print_fatal_error("Unable to compile WITH-VALUE regular expression "
5465 "\"%s\" - %s", value
, temp
);
5470 * See if ALL of the values match the given regular expression.
5473 for (i
= 0; i
< attr
->num_values
; i
++)
5475 if (!regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5478 strlcpy(matchbuf
, attr
->values
[i
].string
.text
, matchlen
);
5480 if (!(flags
& _CUPS_WITH_ALL
))
5486 else if (flags
& _CUPS_WITH_ALL
)
5498 * Value is a literal string, see if the value(s) match...
5501 for (i
= 0; i
< attr
->num_values
; i
++)
5503 if (!strcmp(value
, attr
->values
[i
].string
.text
))
5506 strlcpy(matchbuf
, attr
->values
[i
].string
.text
, matchlen
);
5508 if (!(flags
& _CUPS_WITH_ALL
))
5514 else if (flags
& _CUPS_WITH_ALL
)
5522 if (!match
&& errors
)
5524 for (i
= 0; i
< attr
->num_values
; i
++)
5525 add_stringf(errors
, "GOT: %s=\"%s\"", attr
->name
,
5526 attr
->values
[i
].string
.text
);