4 * ipptool command for CUPS.
6 * Copyright 2007-2012 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 int repeat_limit
, /* Maximum number of times to repeat */
118 repeat_match
, /* Repeat the test when it does not match */
119 repeat_no_match
; /* Repeat the test when it matches */
122 typedef struct _cups_var_s
/**** Variable ****/
124 char *name
, /* Name of variable */
125 *value
; /* Value of variable */
128 typedef struct _cups_vars_s
/**** Set of variables ****/
130 char *uri
, /* URI for printer */
131 *filename
, /* Filename */
132 scheme
[64], /* Scheme from URI */
133 userpass
[256], /* Username/password from URI */
134 hostname
[256], /* Hostname from URI */
135 resource
[1024]; /* Resource path from URI */
136 int port
; /* Port number from URI */
137 http_encryption_t encryption
; /* Encryption for connection? */
138 double timeout
; /* Timeout for connection */
139 int family
; /* Address family */
140 cups_array_t
*vars
; /* Array of variables */
148 _cups_transfer_t Transfer
= _CUPS_TRANSFER_AUTO
;
149 /* How to transfer requests */
150 _cups_output_t Output
= _CUPS_OUTPUT_LIST
;
152 int Cancel
= 0, /* Cancel test? */
153 IgnoreErrors
= 0, /* Ignore errors? */
154 Verbosity
= 0, /* Show all attributes? */
155 Version
= 11, /* Default IPP version */
156 XMLHeader
= 0, /* 1 if header is written */
157 TestCount
= 0, /* Number of tests run */
158 PassCount
= 0, /* Number of passing tests */
159 FailCount
= 0, /* Number of failing tests */
160 SkipCount
= 0; /* Number of skipped tests */
161 char *Password
= NULL
; /* Password from URI */
162 const char * const URIStatusStrings
[] = /* URI status strings */
165 "Bad arguments to function",
166 "Bad resource in URI",
167 "Bad port number in URI",
168 "Bad hostname/address in URI",
169 "Bad username in URI",
173 "Missing scheme in URI",
174 "Unknown scheme in URI",
175 "Missing resource in URI"
183 static void add_stringf(cups_array_t
*a
, const char *s
, ...)
184 __attribute__ ((__format__ (__printf__
, 2, 3)));
185 static int compare_vars(_cups_var_t
*a
, _cups_var_t
*b
);
186 static int do_tests(_cups_vars_t
*vars
, const char *testfile
);
187 static void expand_variables(_cups_vars_t
*vars
, char *dst
, const char *src
,
188 size_t dstsize
) __attribute__((nonnull(1,2,3)));
189 static int expect_matches(_cups_expect_t
*expect
, ipp_tag_t value_tag
);
190 static ipp_t
*get_collection(_cups_vars_t
*vars
, FILE *fp
, int *linenum
);
191 static char *get_filename(const char *testfile
, char *dst
, const char *src
,
193 static char *get_token(FILE *fp
, char *buf
, int buflen
,
195 static char *get_variable(_cups_vars_t
*vars
, const char *name
);
196 static char *iso_date(ipp_uchar_t
*date
);
197 static const char *password_cb(const char *prompt
);
198 static void print_attr(ipp_attribute_t
*attr
, ipp_tag_t
*group
);
199 static void print_col(ipp_t
*col
);
200 static void print_csv(ipp_attribute_t
*attr
, int num_displayed
,
201 char **displayed
, size_t *widths
);
202 static void print_fatal_error(const char *s
, ...)
203 __attribute__ ((__format__ (__printf__
, 1, 2)));
204 static void print_line(ipp_attribute_t
*attr
, int num_displayed
,
205 char **displayed
, size_t *widths
);
206 static void print_xml_header(void);
207 static void print_xml_string(const char *element
, const char *s
);
208 static void print_xml_trailer(int success
, const char *message
);
209 static void set_variable(_cups_vars_t
*vars
, const char *name
,
212 static void sigterm_handler(int sig
);
214 static int timeout_cb(http_t
*http
, void *user_data
);
215 static void usage(void) __attribute__((noreturn
));
216 static int validate_attr(cups_array_t
*errors
, ipp_attribute_t
*attr
);
217 static int with_value(cups_array_t
*errors
, char *value
, int flags
,
218 ipp_attribute_t
*attr
, char *matchbuf
,
223 * 'main()' - Parse options and do tests.
226 int /* O - Exit status */
227 main(int argc
, /* I - Number of command-line args */
228 char *argv
[]) /* I - Command-line arguments */
230 int i
; /* Looping var */
231 int status
; /* Status of tests... */
232 char *opt
, /* Current option */
233 name
[1024], /* Name/value buffer */
234 *value
, /* Pointer to value */
235 filename
[1024], /* Real filename */
236 testname
[1024], /* Real test filename */
237 uri
[1024]; /* Copy of printer URI */
238 const char *ext
, /* Extension on filename */
239 *testfile
; /* Test file to use */
240 int interval
, /* Test interval in microseconds */
241 repeat
; /* Repeat count */
242 _cups_vars_t vars
; /* Variables */
243 http_uri_status_t uri_status
; /* URI separation status */
244 _cups_globals_t
*cg
= _cupsGlobals();
250 * Catch SIGINT and SIGTERM...
253 signal(SIGINT
, sigterm_handler
);
254 signal(SIGTERM
, sigterm_handler
);
258 * Initialize the locale and variables...
261 _cupsSetLocale(argv
);
263 memset(&vars
, 0, sizeof(vars
));
264 vars
.family
= AF_UNSPEC
;
265 vars
.vars
= cupsArrayNew((cups_array_func_t
)compare_vars
, NULL
);
270 * ipptool URI testfile
278 for (i
= 1; i
< argc
; i
++)
280 if (argv
[i
][0] == '-')
282 for (opt
= argv
[i
] + 1; *opt
; opt
++)
286 case '4' : /* Connect using IPv4 only */
287 vars
.family
= AF_INET
;
291 case '6' : /* Connect using IPv6 only */
292 vars
.family
= AF_INET6
;
294 #endif /* AF_INET6 */
296 case 'C' : /* Enable HTTP chunking */
297 Transfer
= _CUPS_TRANSFER_CHUNKED
;
300 case 'E' : /* Encrypt with TLS */
302 vars
.encryption
= HTTP_ENCRYPT_REQUIRED
;
304 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
306 #endif /* HAVE_SSL */
309 case 'I' : /* Ignore errors */
313 case 'L' : /* Disable HTTP chunking */
314 Transfer
= _CUPS_TRANSFER_LENGTH
;
317 case 'S' : /* Encrypt with SSL */
319 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
321 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
323 #endif /* HAVE_SSL */
326 case 'T' : /* Set timeout */
331 _cupsLangPuts(stderr
,
332 _("ipptool: Missing timeout for \"-T\"."));
336 vars
.timeout
= _cupsStrScand(argv
[i
], NULL
, localeconv());
339 case 'V' : /* Set IPP version */
344 _cupsLangPuts(stderr
,
345 _("ipptool: Missing version for \"-V\"."));
349 if (!strcmp(argv
[i
], "1.0"))
351 else if (!strcmp(argv
[i
], "1.1"))
353 else if (!strcmp(argv
[i
], "2.0"))
355 else if (!strcmp(argv
[i
], "2.1"))
357 else if (!strcmp(argv
[i
], "2.2"))
361 _cupsLangPrintf(stderr
,
362 _("ipptool: Bad version %s for \"-V\"."),
368 case 'X' : /* Produce XML output */
369 Output
= _CUPS_OUTPUT_PLIST
;
371 if (interval
|| repeat
)
373 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are "
374 "incompatible with -X\"."));
379 case 'c' : /* CSV output */
380 Output
= _CUPS_OUTPUT_CSV
;
383 case 'd' : /* Define a variable */
388 _cupsLangPuts(stderr
,
389 _("ipptool: Missing name=value for \"-d\"."));
393 strlcpy(name
, argv
[i
], sizeof(name
));
394 if ((value
= strchr(name
, '=')) != NULL
)
397 value
= name
+ strlen(name
);
399 set_variable(&vars
, name
, value
);
402 case 'f' : /* Set the default test filename */
407 _cupsLangPuts(stderr
,
408 _("ipptool: Missing filename for \"-f\"."));
415 if (access(argv
[i
], 0))
421 snprintf(filename
, sizeof(filename
), "%s.gz", argv
[i
]);
422 if (access(filename
, 0) && filename
[0] != '/'
424 && (!isalpha(filename
[0] & 255) || filename
[1] != ':')
428 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s",
429 cg
->cups_datadir
, argv
[i
]);
430 if (access(filename
, 0))
432 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s.gz",
433 cg
->cups_datadir
, argv
[i
]);
434 if (access(filename
, 0))
435 vars
.filename
= strdup(argv
[i
]);
438 vars
.filename
= strdup(filename
);
441 vars
.filename
= strdup(filename
);
444 vars
.filename
= strdup(argv
[i
]);
446 if ((ext
= strrchr(vars
.filename
, '.')) != NULL
)
449 * Guess the MIME media type based on the extension...
452 if (!_cups_strcasecmp(ext
, ".gif"))
453 set_variable(&vars
, "filetype", "image/gif");
454 else if (!_cups_strcasecmp(ext
, ".htm") ||
455 !_cups_strcasecmp(ext
, ".htm.gz") ||
456 !_cups_strcasecmp(ext
, ".html") ||
457 !_cups_strcasecmp(ext
, ".html.gz"))
458 set_variable(&vars
, "filetype", "text/html");
459 else if (!_cups_strcasecmp(ext
, ".jpg"))
460 set_variable(&vars
, "filetype", "image/jpeg");
461 else if (!_cups_strcasecmp(ext
, ".pcl") ||
462 !_cups_strcasecmp(ext
, ".pcl.gz"))
463 set_variable(&vars
, "filetype", "application/vnd.hp-PCL");
464 else if (!_cups_strcasecmp(ext
, ".pdf"))
465 set_variable(&vars
, "filetype", "application/pdf");
466 else if (!_cups_strcasecmp(ext
, ".png"))
467 set_variable(&vars
, "filetype", "image/png");
468 else if (!_cups_strcasecmp(ext
, ".ps") ||
469 !_cups_strcasecmp(ext
, ".ps.gz"))
470 set_variable(&vars
, "filetype", "application/postscript");
471 else if (!_cups_strcasecmp(ext
, ".ras") ||
472 !_cups_strcasecmp(ext
, ".ras.gz"))
473 set_variable(&vars
, "filetype", "image/pwg-raster");
474 else if (!_cups_strcasecmp(ext
, ".txt") ||
475 !_cups_strcasecmp(ext
, ".txt.gz"))
476 set_variable(&vars
, "filetype", "text/plain");
477 else if (!_cups_strcasecmp(ext
, ".xps"))
478 set_variable(&vars
, "filetype", "application/openxps");
480 set_variable(&vars
, "filetype", "application/octet-stream");
485 * Use the "auto-type" MIME media type...
488 set_variable(&vars
, "filetype", "application/octet-stream");
492 case 'i' : /* Test every N seconds */
497 _cupsLangPuts(stderr
,
498 _("ipptool: Missing seconds for \"-i\"."));
503 interval
= (int)(_cupsStrScand(argv
[i
], NULL
, localeconv()) *
507 _cupsLangPuts(stderr
,
508 _("ipptool: Invalid seconds for \"-i\"."));
513 if (Output
== _CUPS_OUTPUT_PLIST
&& interval
)
515 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are "
516 "incompatible with -X\"."));
521 case 'l' : /* List as a table */
522 Output
= _CUPS_OUTPUT_LIST
;
525 case 'n' : /* Repeat count */
530 _cupsLangPuts(stderr
,
531 _("ipptool: Missing count for \"-n\"."));
535 repeat
= atoi(argv
[i
]);
537 if (Output
== _CUPS_OUTPUT_PLIST
&& repeat
)
539 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are "
540 "incompatible with -X\"."));
545 case 'q' : /* Be quiet */
546 Output
= _CUPS_OUTPUT_QUIET
;
549 case 't' : /* CUPS test output */
550 Output
= _CUPS_OUTPUT_TEST
;
553 case 'v' : /* Be verbose */
558 _cupsLangPrintf(stderr
, _("ipptool: Unknown option \"-%c\"."),
565 else if (!strncmp(argv
[i
], "ipp://", 6) || !strncmp(argv
[i
], "http://", 7)
567 || !strncmp(argv
[i
], "ipps://", 7)
568 || !strncmp(argv
[i
], "https://", 8)
569 #endif /* HAVE_SSL */
578 _cupsLangPuts(stderr
, _("ipptool: May only specify a single URI."));
583 if (!strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8))
584 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
585 #endif /* HAVE_SSL */
588 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, vars
.uri
,
589 vars
.scheme
, sizeof(vars
.scheme
),
590 vars
.userpass
, sizeof(vars
.userpass
),
591 vars
.hostname
, sizeof(vars
.hostname
),
593 vars
.resource
, sizeof(vars
.resource
));
595 if (uri_status
!= HTTP_URI_OK
)
597 _cupsLangPrintf(stderr
, _("ipptool: Bad URI - %s."),
598 URIStatusStrings
[uri_status
- HTTP_URI_OVERFLOW
]);
602 if (vars
.userpass
[0])
604 if ((Password
= strchr(vars
.userpass
, ':')) != NULL
)
607 cupsSetUser(vars
.userpass
);
608 cupsSetPasswordCB(password_cb
);
609 set_variable(&vars
, "uriuser", vars
.userpass
);
612 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), vars
.scheme
, NULL
,
613 vars
.hostname
, vars
.port
, vars
.resource
);
624 _cupsLangPuts(stderr
, _("ipptool: URI required before test file."));
628 if (access(argv
[i
], 0) && argv
[i
][0] != '/'
630 && (!isalpha(argv
[i
][0] & 255) || argv
[i
][1] != ':')
634 snprintf(testname
, sizeof(testname
), "%s/ipptool/%s", cg
->cups_datadir
,
636 if (access(testname
, 0))
644 if (!do_tests(&vars
, testfile
))
649 if (!vars
.uri
|| !testfile
)
653 * Loop if the interval is set...
656 if (Output
== _CUPS_OUTPUT_PLIST
)
657 print_xml_trailer(!status
, NULL
);
658 else if (interval
> 0 && repeat
> 0)
663 do_tests(&vars
, testfile
);
667 else if (interval
> 0)
672 do_tests(&vars
, testfile
);
675 else if (Output
== _CUPS_OUTPUT_TEST
&& TestCount
> 1)
678 * Show a summary report if there were multiple tests...
681 printf("\nSummary: %d tests, %d passed, %d failed, %d skipped\n"
682 "Score: %d%%\n", TestCount
, PassCount
, FailCount
, SkipCount
,
683 100 * (PassCount
+ SkipCount
) / TestCount
);
695 * 'add_stringf()' - Add a formatted string to an array.
699 add_stringf(cups_array_t
*a
, /* I - Array */
700 const char *s
, /* I - Printf-style format string */
701 ...) /* I - Additional args as needed */
703 char buffer
[10240]; /* Format buffer */
704 va_list ap
; /* Argument pointer */
708 * Don't bother is the array is NULL...
715 * Format the message...
719 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
723 * Add it to the array...
726 cupsArrayAdd(a
, buffer
);
731 * 'compare_vars()' - Compare two variables.
734 static int /* O - Result of comparison */
735 compare_vars(_cups_var_t
*a
, /* I - First variable */
736 _cups_var_t
*b
) /* I - Second variable */
738 return (_cups_strcasecmp(a
->name
, b
->name
));
743 * 'do_tests()' - Do tests as specified in the test file.
746 static int /* 1 = success, 0 = failure */
747 do_tests(_cups_vars_t
*vars
, /* I - Variables */
748 const char *testfile
) /* I - Test file to use */
750 int i
, /* Looping var */
751 linenum
, /* Current line number */
752 pass
, /* Did we pass the test? */
753 prev_pass
= 1, /* Did we pass the previous test? */
754 request_id
, /* Current request ID */
755 show_header
= 1, /* Show the test header? */
756 ignore_errors
, /* Ignore test failures? */
757 skip_previous
= 0, /* Skip on previous test failure? */
758 repeat_count
, /* Repeat count */
759 repeat_interval
, /* Repeat interval */
760 repeat_prev
, /* Previous repeat interval */
761 repeat_test
; /* Repeat a test? */
762 http_t
*http
= NULL
; /* HTTP connection to server */
763 FILE *fp
= NULL
; /* Test file */
764 char resource
[512], /* Resource for request */
765 token
[1024], /* Token from file */
766 *tokenptr
, /* Pointer into token */
767 temp
[1024], /* Temporary string */
768 buffer
[8192], /* Copy buffer */
769 compression
[16]; /* COMPRESSION value */
770 ipp_t
*request
= NULL
, /* IPP request */
771 *response
= NULL
; /* IPP response */
772 size_t length
; /* Length of IPP request */
773 http_status_t status
; /* HTTP status */
774 cups_file_t
*reqfile
; /* File to send */
775 ssize_t bytes
; /* Bytes read/written */
776 char attr
[128]; /* Attribute name */
777 ipp_op_t op
; /* Operation */
778 ipp_tag_t group
; /* Current group */
779 ipp_tag_t value
; /* Current value type */
780 ipp_attribute_t
*attrptr
, /* Attribute pointer */
781 *found
, /* Found attribute */
782 *lastcol
= NULL
; /* Last collection attribute */
783 char name
[1024]; /* Name of test */
784 char filename
[1024]; /* Filename */
785 _cups_transfer_t transfer
; /* To chunk or not to chunk */
786 int version
, /* IPP version number to use */
787 skip_test
; /* Skip this test? */
788 int num_statuses
= 0; /* Number of valid status codes */
789 _cups_status_t statuses
[100], /* Valid status codes */
790 *last_status
; /* Last STATUS (for predicates) */
791 int num_expects
= 0; /* Number of expected attributes */
792 _cups_expect_t expects
[200], /* Expected attributes */
793 *expect
, /* Current expected attribute */
794 *last_expect
; /* Last EXPECT (for predicates) */
795 int num_displayed
= 0; /* Number of displayed attributes */
796 char *displayed
[200]; /* Displayed attributes */
797 size_t widths
[200]; /* Width of columns */
798 cups_array_t
*a
, /* Duplicate attribute array */
799 *errors
= NULL
; /* Errors array */
800 const char *error
; /* Current error */
804 * Open the test file...
807 if ((fp
= fopen(testfile
, "r")) == NULL
)
809 print_fatal_error("Unable to open test file %s - %s", testfile
,
816 * Connect to the server...
819 if ((http
= httpConnect2(vars
->hostname
, vars
->port
, NULL
, vars
->family
,
820 vars
->encryption
, 1, 30000, NULL
)) == NULL
)
822 print_fatal_error("Unable to connect to %s on port %d - %s", vars
->hostname
,
823 vars
->port
, cupsLastErrorString());
829 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
,
830 "deflate, gzip, identity");
832 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
, "identity");
833 #endif /* HAVE_LIBZ */
835 if (vars
->timeout
> 0.0)
836 httpSetTimeout(http
, vars
->timeout
, timeout_cb
, NULL
);
842 CUPS_SRAND(time(NULL
));
844 errors
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
,
845 (cups_afree_func_t
)free
);
848 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
850 while (!Cancel
&& get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
853 * Expect an open brace...
856 if (!strcmp(token
, "DEFINE"))
862 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
863 get_token(fp
, temp
, sizeof(temp
), &linenum
))
865 expand_variables(vars
, token
, temp
, sizeof(token
));
866 set_variable(vars
, attr
, token
);
870 print_fatal_error("Missing DEFINE name and/or value on line %d.",
878 else if (!strcmp(token
, "DEFINE-DEFAULT"))
881 * DEFINE-DEFAULT name value
884 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
885 get_token(fp
, temp
, sizeof(temp
), &linenum
))
887 expand_variables(vars
, token
, temp
, sizeof(token
));
888 if (!get_variable(vars
, attr
))
889 set_variable(vars
, attr
, token
);
893 print_fatal_error("Missing DEFINE-DEFAULT name and/or value on line "
901 else if (!strcmp(token
, "IGNORE-ERRORS"))
908 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
909 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
911 IgnoreErrors
= !_cups_strcasecmp(temp
, "yes");
915 print_fatal_error("Missing IGNORE-ERRORS value on line %d.", linenum
);
922 else if (!strcmp(token
, "INCLUDE"))
929 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
932 * Map the filename to and then run the tests...
935 if (!do_tests(vars
, get_filename(testfile
, filename
, temp
,
946 print_fatal_error("Missing INCLUDE filename on line %d.", linenum
);
954 else if (!strcmp(token
, "INCLUDE-IF-DEFINED"))
957 * INCLUDE-IF-DEFINED name "filename"
958 * INCLUDE-IF-DEFINED name <filename>
961 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
962 get_token(fp
, temp
, sizeof(temp
), &linenum
))
965 * Map the filename to and then run the tests...
968 if (get_variable(vars
, attr
) &&
969 !do_tests(vars
, get_filename(testfile
, filename
, temp
,
980 print_fatal_error("Missing INCLUDE-IF-DEFINED name or filename on line "
989 else if (!strcmp(token
, "INCLUDE-IF-NOT-DEFINED"))
992 * INCLUDE-IF-NOT-DEFINED name "filename"
993 * INCLUDE-IF-NOT-DEFINED name <filename>
996 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
997 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1000 * Map the filename to and then run the tests...
1003 if (!get_variable(vars
, attr
) &&
1004 !do_tests(vars
, get_filename(testfile
, filename
, temp
,
1015 print_fatal_error("Missing INCLUDE-IF-NOT-DEFINED name or filename on "
1016 "line %d.", linenum
);
1024 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1027 * SKIP-IF-DEFINED variable
1030 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1032 if (get_variable(vars
, temp
))
1037 print_fatal_error("Missing SKIP-IF-DEFINED variable on line %d.",
1043 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1046 * SKIP-IF-NOT-DEFINED variable
1049 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1051 if (!get_variable(vars
, temp
))
1056 print_fatal_error("Missing SKIP-IF-NOT-DEFINED variable on line %d.",
1062 else if (!strcmp(token
, "TRANSFER"))
1070 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1072 if (!strcmp(temp
, "auto"))
1073 Transfer
= _CUPS_TRANSFER_AUTO
;
1074 else if (!strcmp(temp
, "chunked"))
1075 Transfer
= _CUPS_TRANSFER_CHUNKED
;
1076 else if (!strcmp(temp
, "length"))
1077 Transfer
= _CUPS_TRANSFER_LENGTH
;
1080 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp
,
1088 print_fatal_error("Missing TRANSFER value on line %d.", linenum
);
1095 else if (!strcmp(token
, "VERSION"))
1097 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1099 if (!strcmp(temp
, "1.0"))
1101 else if (!strcmp(temp
, "1.1"))
1103 else if (!strcmp(temp
, "2.0"))
1105 else if (!strcmp(temp
, "2.1"))
1107 else if (!strcmp(temp
, "2.2"))
1111 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1118 print_fatal_error("Missing VERSION number on line %d.", linenum
);
1125 else if (strcmp(token
, "{"))
1127 print_fatal_error("Unexpected token %s seen on line %d.", token
, linenum
);
1133 * Initialize things...
1138 if (Output
== _CUPS_OUTPUT_PLIST
)
1140 else if (Output
== _CUPS_OUTPUT_TEST
)
1141 printf("\"%s\":\n", testfile
);
1146 strlcpy(resource
, vars
->resource
, sizeof(resource
));
1151 group
= IPP_TAG_ZERO
;
1152 ignore_errors
= IgnoreErrors
;
1159 transfer
= Transfer
;
1160 compression
[0] = '\0';
1162 strlcpy(name
, testfile
, sizeof(name
));
1163 if (strrchr(name
, '.') != NULL
)
1164 *strrchr(name
, '.') = '\0';
1167 * Parse until we see a close brace...
1170 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
1172 if (_cups_strcasecmp(token
, "COUNT") &&
1173 _cups_strcasecmp(token
, "DEFINE-MATCH") &&
1174 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1175 _cups_strcasecmp(token
, "DEFINE-VALUE") &&
1176 _cups_strcasecmp(token
, "IF-DEFINED") &&
1177 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1178 _cups_strcasecmp(token
, "IN-GROUP") &&
1179 _cups_strcasecmp(token
, "OF-TYPE") &&
1180 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1181 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1182 _cups_strcasecmp(token
, "REPEAT-NO-MATCH") &&
1183 _cups_strcasecmp(token
, "SAME-COUNT-AS") &&
1184 _cups_strcasecmp(token
, "WITH-ALL-VALUES") &&
1185 _cups_strcasecmp(token
, "WITH-VALUE"))
1188 if (_cups_strcasecmp(token
, "IF-DEFINED") &&
1189 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1190 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1191 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1192 _cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
1195 if (!strcmp(token
, "}"))
1197 else if (!strcmp(token
, "{") && lastcol
)
1200 * Another collection value
1203 ipp_t
*col
= get_collection(vars
, fp
, &linenum
);
1204 /* Collection value */
1208 ipp_attribute_t
*tempcol
; /* Pointer to new buffer */
1212 * Reallocate memory...
1215 if ((tempcol
= realloc(lastcol
, sizeof(ipp_attribute_t
) +
1216 (lastcol
->num_values
+ 1) *
1217 sizeof(_ipp_value_t
))) == NULL
)
1219 print_fatal_error("Unable to allocate memory on line %d.", linenum
);
1224 if (tempcol
!= lastcol
)
1227 * Reset pointers in the list...
1231 request
->prev
->next
= tempcol
;
1233 request
->attrs
= tempcol
;
1235 lastcol
= request
->current
= request
->last
= tempcol
;
1238 lastcol
->values
[lastcol
->num_values
].collection
= col
;
1239 lastcol
->num_values
++;
1247 else if (!strcmp(token
, "COMPRESSION"))
1251 * COMPRESSION deflate
1255 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1257 expand_variables(vars
, compression
, temp
, sizeof(compression
));
1259 if (strcmp(compression
, "none") && strcmp(compression
, "deflate") &&
1260 strcmp(compression
, "gzip"))
1262 if (strcmp(compression
, "none"))
1263 #endif /* HAVE_LIBZ */
1265 print_fatal_error("Unsupported COMPRESSION value '%s' on line %d.",
1266 compression
, linenum
);
1271 if (!strcmp(compression
, "none"))
1272 compression
[0] = '\0';
1276 print_fatal_error("Missing COMPRESSION value on line %d.", linenum
);
1281 else if (!strcmp(token
, "DEFINE"))
1287 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1288 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1290 expand_variables(vars
, token
, temp
, sizeof(token
));
1291 set_variable(vars
, attr
, token
);
1295 print_fatal_error("Missing DEFINE name and/or value on line %d.",
1301 else if (!strcmp(token
, "IGNORE-ERRORS"))
1308 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1309 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1311 ignore_errors
= !_cups_strcasecmp(temp
, "yes");
1315 print_fatal_error("Missing IGNORE-ERRORS value on line %d.", linenum
);
1322 else if (!_cups_strcasecmp(token
, "NAME"))
1328 get_token(fp
, name
, sizeof(name
), &linenum
);
1330 else if (!strcmp(token
, "REQUEST-ID"))
1337 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1339 if (isdigit(temp
[0] & 255))
1340 request_id
= atoi(temp
);
1341 else if (!_cups_strcasecmp(temp
, "random"))
1342 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
1345 print_fatal_error("Bad REQUEST-ID value \"%s\" on line %d.", temp
,
1353 print_fatal_error("Missing REQUEST-ID value on line %d.", linenum
);
1358 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1361 * SKIP-IF-DEFINED variable
1364 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1366 if (get_variable(vars
, temp
))
1371 print_fatal_error("Missing SKIP-IF-DEFINED value on line %d.",
1377 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1380 * SKIP-IF-NOT-DEFINED variable
1383 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1385 if (!get_variable(vars
, temp
))
1390 print_fatal_error("Missing SKIP-IF-NOT-DEFINED value on line %d.",
1396 else if (!strcmp(token
, "SKIP-PREVIOUS-ERROR"))
1399 * SKIP-PREVIOUS-ERROR yes
1400 * SKIP-PREVIOUS-ERROR no
1403 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1404 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1406 skip_previous
= !_cups_strcasecmp(temp
, "yes");
1410 print_fatal_error("Missing SKIP-PREVIOUS-ERROR value on line %d.", linenum
);
1417 else if (!strcmp(token
, "TRANSFER"))
1425 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1427 if (!strcmp(temp
, "auto"))
1428 transfer
= _CUPS_TRANSFER_AUTO
;
1429 else if (!strcmp(temp
, "chunked"))
1430 transfer
= _CUPS_TRANSFER_CHUNKED
;
1431 else if (!strcmp(temp
, "length"))
1432 transfer
= _CUPS_TRANSFER_LENGTH
;
1435 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp
,
1443 print_fatal_error("Missing TRANSFER value on line %d.", linenum
);
1448 else if (!_cups_strcasecmp(token
, "VERSION"))
1450 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1452 if (!strcmp(temp
, "0.0"))
1454 else if (!strcmp(temp
, "1.0"))
1456 else if (!strcmp(temp
, "1.1"))
1458 else if (!strcmp(temp
, "2.0"))
1460 else if (!strcmp(temp
, "2.1"))
1462 else if (!strcmp(temp
, "2.2"))
1466 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1473 print_fatal_error("Missing VERSION number on line %d.", linenum
);
1478 else if (!_cups_strcasecmp(token
, "RESOURCE"))
1484 if (!get_token(fp
, resource
, sizeof(resource
), &linenum
))
1486 print_fatal_error("Missing RESOURCE path on line %d.", linenum
);
1491 else if (!_cups_strcasecmp(token
, "OPERATION"))
1497 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1499 print_fatal_error("Missing OPERATION code on line %d.", linenum
);
1504 if ((op
= ippOpValue(token
)) == (ipp_op_t
)-1 &&
1505 (op
= strtol(token
, NULL
, 0)) == 0)
1507 print_fatal_error("Bad OPERATION code \"%s\" on line %d.", token
,
1513 else if (!_cups_strcasecmp(token
, "GROUP"))
1516 * Attribute group...
1519 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1521 print_fatal_error("Missing GROUP tag on line %d.", linenum
);
1526 if ((value
= ippTagValue(token
)) < 0)
1528 print_fatal_error("Bad GROUP tag \"%s\" on line %d.", token
, linenum
);
1534 ippAddSeparator(request
);
1538 else if (!_cups_strcasecmp(token
, "DELAY"))
1541 * Delay before operation...
1546 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1548 print_fatal_error("Missing DELAY value on line %d.", linenum
);
1553 if ((delay
= _cupsStrScand(token
, NULL
, localeconv())) <= 0.0)
1555 print_fatal_error("Bad DELAY value \"%s\" on line %d.", token
,
1562 if (Output
== _CUPS_OUTPUT_TEST
)
1563 printf(" [%g second delay]\n", delay
);
1565 usleep((int)(1000000.0 * delay
));
1568 else if (!_cups_strcasecmp(token
, "ATTR"))
1574 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1576 print_fatal_error("Missing ATTR value tag on line %d.", linenum
);
1581 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
1583 print_fatal_error("Bad ATTR value tag \"%s\" on line %d.", token
,
1589 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
1591 print_fatal_error("Missing ATTR name on line %d.", linenum
);
1596 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1598 print_fatal_error("Missing ATTR value on line %d.", linenum
);
1603 expand_variables(vars
, token
, temp
, sizeof(token
));
1608 case IPP_TAG_BOOLEAN
:
1609 if (!_cups_strcasecmp(token
, "true"))
1610 attrptr
= ippAddBoolean(request
, group
, attr
, 1);
1612 attrptr
= ippAddBoolean(request
, group
, attr
, atoi(token
));
1615 case IPP_TAG_INTEGER
:
1617 if (!strchr(token
, ','))
1618 attrptr
= ippAddInteger(request
, group
, value
, attr
,
1619 strtol(token
, &tokenptr
, 0));
1622 int values
[100], /* Values */
1623 num_values
= 1; /* Number of values */
1625 values
[0] = strtol(token
, &tokenptr
, 10);
1626 while (tokenptr
&& *tokenptr
&&
1627 num_values
< (int)(sizeof(values
) / sizeof(values
[0])))
1629 if (*tokenptr
== ',')
1631 else if (!isdigit(*tokenptr
& 255) && *tokenptr
!= '-')
1634 values
[num_values
] = strtol(tokenptr
, &tokenptr
, 0);
1638 attrptr
= ippAddIntegers(request
, group
, value
, attr
, num_values
, values
);
1641 if (!tokenptr
|| *tokenptr
)
1643 print_fatal_error("Bad %s value \"%s\" on line %d.",
1644 ippTagString(value
), token
, linenum
);
1650 case IPP_TAG_RESOLUTION
:
1652 int xres
, /* X resolution */
1653 yres
; /* Y resolution */
1654 char *ptr
; /* Pointer into value */
1656 xres
= yres
= strtol(token
, (char **)&ptr
, 10);
1657 if (ptr
> token
&& xres
> 0)
1660 yres
= strtol(ptr
+ 1, (char **)&ptr
, 10);
1663 if (ptr
<= token
|| xres
<= 0 || yres
<= 0 || !ptr
||
1664 (_cups_strcasecmp(ptr
, "dpi") &&
1665 _cups_strcasecmp(ptr
, "dpc") &&
1666 _cups_strcasecmp(ptr
, "dpcm") &&
1667 _cups_strcasecmp(ptr
, "other")))
1669 print_fatal_error("Bad resolution value \"%s\" on line %d.",
1675 if (!_cups_strcasecmp(ptr
, "dpi"))
1676 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_INCH
,
1678 else if (!_cups_strcasecmp(ptr
, "dpc") ||
1679 !_cups_strcasecmp(ptr
, "dpcm"))
1680 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_CM
,
1683 attrptr
= ippAddResolution(request
, group
, attr
, (ipp_res_t
)0,
1688 case IPP_TAG_RANGE
:
1690 int lowers
[4], /* Lower value */
1691 uppers
[4], /* Upper values */
1692 num_vals
; /* Number of values */
1695 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
1696 lowers
+ 0, uppers
+ 0,
1697 lowers
+ 1, uppers
+ 1,
1698 lowers
+ 2, uppers
+ 2,
1699 lowers
+ 3, uppers
+ 3);
1701 if ((num_vals
& 1) || num_vals
== 0)
1703 print_fatal_error("Bad rangeOfInteger value \"%s\" on line "
1704 "%d.", token
, linenum
);
1709 attrptr
= ippAddRanges(request
, group
, attr
, num_vals
/ 2, lowers
,
1714 case IPP_TAG_BEGIN_COLLECTION
:
1715 if (!strcmp(token
, "{"))
1717 ipp_t
*col
= get_collection(vars
, fp
, &linenum
);
1718 /* Collection value */
1722 attrptr
= lastcol
= ippAddCollection(request
, group
, attr
, col
);
1733 print_fatal_error("Bad ATTR collection value on line %d.",
1740 case IPP_TAG_STRING
:
1741 attrptr
= ippAddOctetString(request
, group
, attr
, token
,
1746 print_fatal_error("Unsupported ATTR value tag %s on line %d.",
1747 ippTagString(value
), linenum
);
1751 case IPP_TAG_TEXTLANG
:
1752 case IPP_TAG_NAMELANG
:
1755 case IPP_TAG_KEYWORD
:
1757 case IPP_TAG_URISCHEME
:
1758 case IPP_TAG_CHARSET
:
1759 case IPP_TAG_LANGUAGE
:
1760 case IPP_TAG_MIMETYPE
:
1761 if (!strchr(token
, ','))
1762 attrptr
= ippAddString(request
, group
, value
, attr
, NULL
, token
);
1766 * Multiple string values...
1769 int num_values
; /* Number of values */
1770 char *values
[100], /* Values */
1771 *ptr
; /* Pointer to next value */
1777 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
1779 if (ptr
> token
&& ptr
[-1] == '\\')
1780 _cups_strcpy(ptr
- 1, ptr
);
1784 values
[num_values
] = ptr
;
1789 attrptr
= ippAddStrings(request
, group
, value
, attr
, num_values
,
1790 NULL
, (const char **)values
);
1797 print_fatal_error("Unable to add attribute on line %d: %s", linenum
,
1798 cupsLastErrorString());
1803 else if (!_cups_strcasecmp(token
, "FILE"))
1809 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1811 print_fatal_error("Missing FILE filename on line %d.", linenum
);
1816 expand_variables(vars
, token
, temp
, sizeof(token
));
1817 get_filename(testfile
, filename
, token
, sizeof(filename
));
1819 else if (!_cups_strcasecmp(token
, "STATUS"))
1825 if (num_statuses
>= (int)(sizeof(statuses
) / sizeof(statuses
[0])))
1827 print_fatal_error("Too many STATUS's on line %d.", linenum
);
1832 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1834 print_fatal_error("Missing STATUS code on line %d.", linenum
);
1839 if ((statuses
[num_statuses
].status
= ippErrorValue(token
))
1840 == (ipp_status_t
)-1 &&
1841 (statuses
[num_statuses
].status
= strtol(token
, NULL
, 0)) == 0)
1843 print_fatal_error("Bad STATUS code \"%s\" on line %d.", token
,
1849 last_status
= statuses
+ num_statuses
;
1852 last_status
->if_defined
= NULL
;
1853 last_status
->if_not_defined
= NULL
;
1854 last_status
->repeat_limit
= 1000;
1855 last_status
->repeat_match
= 0;
1856 last_status
->repeat_no_match
= 0;
1858 else if (!_cups_strcasecmp(token
, "EXPECT"))
1861 * Expected attributes...
1864 if (num_expects
>= (int)(sizeof(expects
) / sizeof(expects
[0])))
1866 print_fatal_error("Too many EXPECT's on line %d.", linenum
);
1871 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1873 print_fatal_error("Missing EXPECT name on line %d.", linenum
);
1878 last_expect
= expects
+ num_expects
;
1881 memset(last_expect
, 0, sizeof(_cups_expect_t
));
1882 last_expect
->repeat_limit
= 1000;
1884 if (token
[0] == '!')
1886 last_expect
->not_expect
= 1;
1887 last_expect
->name
= strdup(token
+ 1);
1889 else if (token
[0] == '?')
1891 last_expect
->optional
= 1;
1892 last_expect
->name
= strdup(token
+ 1);
1895 last_expect
->name
= strdup(token
);
1897 else if (!_cups_strcasecmp(token
, "COUNT"))
1899 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1901 print_fatal_error("Missing COUNT number on line %d.", linenum
);
1906 if ((i
= atoi(token
)) <= 0)
1908 print_fatal_error("Bad COUNT \"%s\" on line %d.", token
, linenum
);
1914 last_expect
->count
= i
;
1917 print_fatal_error("COUNT without a preceding EXPECT on line %d.",
1923 else if (!_cups_strcasecmp(token
, "DEFINE-MATCH"))
1925 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1927 print_fatal_error("Missing DEFINE-MATCH variable on line %d.",
1934 last_expect
->define_match
= strdup(token
);
1937 print_fatal_error("DEFINE-MATCH without a preceding EXPECT on line "
1943 else if (!_cups_strcasecmp(token
, "DEFINE-NO-MATCH"))
1945 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1947 print_fatal_error("Missing DEFINE-NO-MATCH variable on line %d.",
1954 last_expect
->define_no_match
= strdup(token
);
1957 print_fatal_error("DEFINE-NO-MATCH without a preceding EXPECT on "
1958 "line %d.", linenum
);
1963 else if (!_cups_strcasecmp(token
, "DEFINE-VALUE"))
1965 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1967 print_fatal_error("Missing DEFINE-VALUE variable on line %d.",
1974 last_expect
->define_value
= strdup(token
);
1977 print_fatal_error("DEFINE-VALUE without a preceding EXPECT on line "
1983 else if (!_cups_strcasecmp(token
, "OF-TYPE"))
1985 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1987 print_fatal_error("Missing OF-TYPE value tag(s) on line %d.",
1994 last_expect
->of_type
= strdup(token
);
1997 print_fatal_error("OF-TYPE without a preceding EXPECT on line %d.",
2003 else if (!_cups_strcasecmp(token
, "IN-GROUP"))
2005 ipp_tag_t in_group
; /* IN-GROUP value */
2008 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2010 print_fatal_error("Missing IN-GROUP group tag on line %d.", linenum
);
2015 if ((in_group
= ippTagValue(token
)) == (ipp_tag_t
)-1)
2018 else if (last_expect
)
2019 last_expect
->in_group
= in_group
;
2022 print_fatal_error("IN-GROUP without a preceding EXPECT on line %d.",
2028 else if (!_cups_strcasecmp(token
, "REPEAT-LIMIT"))
2030 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2032 print_fatal_error("Missing REPEAT-LIMIT value on line %d.", linenum
);
2036 else if (atoi(token
) <= 0)
2038 print_fatal_error("Bad REPEAT-LIMIT value on line %d.", linenum
);
2044 last_status
->repeat_limit
= atoi(token
);
2045 else if (last_expect
)
2046 last_expect
->repeat_limit
= atoi(token
);
2049 print_fatal_error("REPEAT-LIMIT without a preceding EXPECT or STATUS "
2050 "on line %d.", linenum
);
2055 else if (!_cups_strcasecmp(token
, "REPEAT-MATCH"))
2058 last_status
->repeat_match
= 1;
2059 else if (last_expect
)
2060 last_expect
->repeat_match
= 1;
2063 print_fatal_error("REPEAT-MATCH without a preceding EXPECT or STATUS "
2064 "on line %d.", linenum
);
2069 else if (!_cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
2072 last_status
->repeat_no_match
= 1;
2073 else if (last_expect
)
2074 last_expect
->repeat_no_match
= 1;
2077 print_fatal_error("REPEAT-NO-MATCH without a preceding EXPECT or "
2078 "STATUS on ine %d.", linenum
);
2083 else if (!_cups_strcasecmp(token
, "SAME-COUNT-AS"))
2085 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2087 print_fatal_error("Missing SAME-COUNT-AS name on line %d.", linenum
);
2093 last_expect
->same_count_as
= strdup(token
);
2096 print_fatal_error("SAME-COUNT-AS without a preceding EXPECT on line "
2102 else if (!_cups_strcasecmp(token
, "IF-DEFINED"))
2104 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2106 print_fatal_error("Missing IF-DEFINED name on line %d.", linenum
);
2112 last_expect
->if_defined
= strdup(token
);
2113 else if (last_status
)
2114 last_status
->if_defined
= strdup(token
);
2117 print_fatal_error("IF-DEFINED without a preceding EXPECT or STATUS "
2118 "on line %d.", linenum
);
2123 else if (!_cups_strcasecmp(token
, "IF-NOT-DEFINED"))
2125 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2127 print_fatal_error("Missing IF-NOT-DEFINED name on line %d.", linenum
);
2133 last_expect
->if_not_defined
= strdup(token
);
2134 else if (last_status
)
2135 last_status
->if_not_defined
= strdup(token
);
2138 print_fatal_error("IF-NOT-DEFINED without a preceding EXPECT or STATUS "
2139 "on line %d.", linenum
);
2144 else if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") ||
2145 !_cups_strcasecmp(token
, "WITH-VALUE"))
2147 if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") && last_expect
)
2148 last_expect
->with_flags
= _CUPS_WITH_ALL
;
2150 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
2152 print_fatal_error("Missing %s value on line %d.", token
, linenum
);
2160 * Expand any variables in the value and then save it.
2163 expand_variables(vars
, token
, temp
, sizeof(token
));
2165 tokenptr
= token
+ strlen(token
) - 1;
2167 if (token
[0] == '/' && tokenptr
> token
&& *tokenptr
== '/')
2170 * WITH-VALUE is a POSIX extended regular expression.
2173 last_expect
->with_value
= calloc(1, tokenptr
- token
);
2174 last_expect
->with_flags
|= _CUPS_WITH_REGEX
;
2176 if (last_expect
->with_value
)
2177 memcpy(last_expect
->with_value
, token
+ 1, tokenptr
- token
- 1);
2182 * WITH-VALUE is a literal value...
2185 char *ptr
; /* Pointer into value */
2187 for (ptr
= token
; *ptr
; ptr
++)
2189 if (*ptr
== '\\' && ptr
[1])
2192 * Remove \ from \foo...
2195 _cups_strcpy(ptr
, ptr
+ 1);
2199 last_expect
->with_value
= strdup(token
);
2200 last_expect
->with_flags
|= _CUPS_WITH_LITERAL
;
2205 print_fatal_error("%s without a preceding EXPECT on line %d.", token
,
2211 else if (!_cups_strcasecmp(token
, "DISPLAY"))
2214 * Display attributes...
2217 if (num_displayed
>= (int)(sizeof(displayed
) / sizeof(displayed
[0])))
2219 print_fatal_error("Too many DISPLAY's on line %d", linenum
);
2224 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2226 print_fatal_error("Missing DISPLAY name on line %d.", linenum
);
2231 displayed
[num_displayed
] = strdup(token
);
2236 print_fatal_error("Unexpected token %s seen on line %d.", token
,
2244 * Submit the IPP request...
2249 request
->request
.op
.version
[0] = version
/ 10;
2250 request
->request
.op
.version
[1] = version
% 10;
2251 request
->request
.op
.operation_id
= op
;
2252 request
->request
.op
.request_id
= request_id
;
2254 if (Output
== _CUPS_OUTPUT_PLIST
)
2257 puts("<key>Name</key>");
2258 print_xml_string("string", name
);
2259 puts("<key>Operation</key>");
2260 print_xml_string("string", ippOpString(op
));
2261 puts("<key>RequestAttributes</key>");
2266 for (attrptr
= request
->attrs
, group
= attrptr
->group_tag
;
2268 attrptr
= attrptr
->next
)
2269 print_attr(attrptr
, &group
);
2274 else if (Output
== _CUPS_OUTPUT_TEST
)
2278 printf(" %s:\n", ippOpString(op
));
2280 for (attrptr
= request
->attrs
; attrptr
; attrptr
= attrptr
->next
)
2281 print_attr(attrptr
, NULL
);
2284 printf(" %-68.68s [", name
);
2288 if ((skip_previous
&& !prev_pass
) || skip_test
)
2295 if (Output
== _CUPS_OUTPUT_PLIST
)
2297 puts("<key>Successful</key>");
2299 puts("<key>StatusCode</key>");
2300 print_xml_string("string", "skip");
2301 puts("<key>ResponseAttributes</key>");
2304 else if (Output
== _CUPS_OUTPUT_TEST
)
2311 repeat_interval
= 1;
2320 if (transfer
== _CUPS_TRANSFER_CHUNKED
||
2321 (transfer
== _CUPS_TRANSFER_AUTO
&& filename
[0]))
2324 * Send request using chunking - a 0 length means "chunk".
2332 * Send request using content length...
2335 length
= ippLength(request
);
2337 if (filename
[0] && (reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2340 * Read the file to get the uncompressed file size...
2343 while ((bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
2346 cupsFileClose(reqfile
);
2351 * Send the request...
2358 if (status
!= HTTP_ERROR
)
2360 while (!response
&& !Cancel
&& prev_pass
)
2362 status
= cupsSendRequest(http
, request
, resource
, length
);
2366 httpSetField(http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
2367 #endif /* HAVE_LIBZ */
2369 if (!Cancel
&& status
== HTTP_CONTINUE
&&
2370 request
->state
== IPP_DATA
&& filename
[0])
2372 if ((reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2375 (bytes
= cupsFileRead(reqfile
, buffer
,
2376 sizeof(buffer
))) > 0)
2377 if ((status
= cupsWriteRequestData(http
, buffer
,
2378 bytes
)) != HTTP_CONTINUE
)
2381 cupsFileClose(reqfile
);
2385 snprintf(buffer
, sizeof(buffer
), "%s: %s", filename
,
2387 _cupsSetError(IPP_INTERNAL_ERROR
, buffer
, 0);
2389 status
= HTTP_ERROR
;
2394 * Get the server's response...
2397 if (!Cancel
&& status
!= HTTP_ERROR
)
2399 response
= cupsGetResponse(http
, resource
);
2400 status
= httpGetStatus(http
);
2403 if (!Cancel
&& status
== HTTP_ERROR
&& http
->error
!= EINVAL
&&
2405 http
->error
!= WSAETIMEDOUT
)
2407 http
->error
!= ETIMEDOUT
)
2410 if (httpReconnect(http
))
2413 else if (status
== HTTP_ERROR
)
2418 else if (status
!= HTTP_OK
)
2426 if (!Cancel
&& status
== HTTP_ERROR
&& http
->error
!= EINVAL
&&
2428 http
->error
!= WSAETIMEDOUT
)
2430 http
->error
!= ETIMEDOUT
)
2433 if (httpReconnect(http
))
2436 else if (status
== HTTP_ERROR
)
2439 httpReconnect(http
);
2443 else if (status
!= HTTP_OK
)
2450 * Check results of request...
2453 cupsArrayClear(errors
);
2455 if (http
->version
!= HTTP_1_1
)
2456 add_stringf(errors
, "Bad HTTP version (%d.%d)", http
->version
/ 100,
2457 http
->version
% 100);
2462 * No response, log error...
2465 add_stringf(errors
, "IPP request failed with status %s (%s)",
2466 ippErrorString(cupsLastError()),
2467 cupsLastErrorString());
2472 * Collect common attribute values...
2475 if ((attrptr
= ippFindAttribute(response
, "job-id",
2476 IPP_TAG_INTEGER
)) != NULL
)
2478 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2479 set_variable(vars
, "job-id", temp
);
2482 if ((attrptr
= ippFindAttribute(response
, "job-uri",
2483 IPP_TAG_URI
)) != NULL
)
2484 set_variable(vars
, "job-uri", attrptr
->values
[0].string
.text
);
2486 if ((attrptr
= ippFindAttribute(response
, "notify-subscription-id",
2487 IPP_TAG_INTEGER
)) != NULL
)
2489 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2490 set_variable(vars
, "notify-subscription-id", temp
);
2494 * Check response, validating groups and attributes and logging errors
2498 if (response
->state
!= IPP_DATA
)
2500 "Missing end-of-attributes-tag in response "
2501 "(RFC 2910 section 3.5.1)");
2504 (response
->request
.status
.version
[0] != (version
/ 10) ||
2505 response
->request
.status
.version
[1] != (version
% 10)))
2507 "Bad version %d.%d in response - expected %d.%d "
2508 "(RFC 2911 section 3.1.8).",
2509 response
->request
.status
.version
[0],
2510 response
->request
.status
.version
[1],
2511 version
/ 10, version
% 10);
2513 if (response
->request
.status
.request_id
!= request_id
)
2515 "Bad request ID %d in response - expected %d "
2516 "(RFC 2911 section 3.1.1)",
2517 response
->request
.status
.request_id
, request_id
);
2519 attrptr
= response
->attrs
;
2522 "Missing first attribute \"attributes-charset "
2523 "(charset)\" in group operation-attributes-tag "
2524 "(RFC 2911 section 3.1.4).");
2527 if (!attrptr
->name
||
2528 attrptr
->value_tag
!= IPP_TAG_CHARSET
||
2529 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2530 attrptr
->num_values
!= 1 ||
2531 strcmp(attrptr
->name
, "attributes-charset"))
2533 "Bad first attribute \"%s (%s%s)\" in group %s, "
2534 "expected \"attributes-charset (charset)\" in "
2535 "group operation-attributes-tag (RFC 2911 section "
2537 attrptr
->name
? attrptr
->name
: "(null)",
2538 attrptr
->num_values
> 1 ? "1setOf " : "",
2539 ippTagString(attrptr
->value_tag
),
2540 ippTagString(attrptr
->group_tag
));
2542 attrptr
= attrptr
->next
;
2545 "Missing second attribute \"attributes-natural-"
2546 "language (naturalLanguage)\" in group "
2547 "operation-attributes-tag (RFC 2911 section "
2549 else if (!attrptr
->name
||
2550 attrptr
->value_tag
!= IPP_TAG_LANGUAGE
||
2551 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2552 attrptr
->num_values
!= 1 ||
2553 strcmp(attrptr
->name
, "attributes-natural-language"))
2555 "Bad first attribute \"%s (%s%s)\" in group %s, "
2556 "expected \"attributes-natural-language "
2557 "(naturalLanguage)\" in group "
2558 "operation-attributes-tag (RFC 2911 section "
2560 attrptr
->name
? attrptr
->name
: "(null)",
2561 attrptr
->num_values
> 1 ? "1setOf " : "",
2562 ippTagString(attrptr
->value_tag
),
2563 ippTagString(attrptr
->group_tag
));
2566 if ((attrptr
= ippFindAttribute(response
, "status-message",
2567 IPP_TAG_ZERO
)) != NULL
)
2569 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2571 "status-message (text(255)) has wrong value tag "
2572 "%s (RFC 2911 section 3.1.6.2).",
2573 ippTagString(attrptr
->value_tag
));
2574 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2576 "status-message (text(255)) has wrong group tag "
2577 "%s (RFC 2911 section 3.1.6.2).",
2578 ippTagString(attrptr
->group_tag
));
2579 if (attrptr
->num_values
!= 1)
2581 "status-message (text(255)) has %d values "
2582 "(RFC 2911 section 3.1.6.2).",
2583 attrptr
->num_values
);
2584 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2585 strlen(attrptr
->values
[0].string
.text
) > 255)
2587 "status-message (text(255)) has bad length %d"
2588 " (RFC 2911 section 3.1.6.2).",
2589 (int)strlen(attrptr
->values
[0].string
.text
));
2592 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
2593 IPP_TAG_ZERO
)) != NULL
)
2595 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2597 "detailed-status-message (text(MAX)) has wrong "
2598 "value tag %s (RFC 2911 section 3.1.6.3).",
2599 ippTagString(attrptr
->value_tag
));
2600 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2602 "detailed-status-message (text(MAX)) has wrong "
2603 "group tag %s (RFC 2911 section 3.1.6.3).",
2604 ippTagString(attrptr
->group_tag
));
2605 if (attrptr
->num_values
!= 1)
2607 "detailed-status-message (text(MAX)) has %d values"
2608 " (RFC 2911 section 3.1.6.3).",
2609 attrptr
->num_values
);
2610 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2611 strlen(attrptr
->values
[0].string
.text
) > 1023)
2613 "detailed-status-message (text(MAX)) has bad "
2614 "length %d (RFC 2911 section 3.1.6.3).",
2615 (int)strlen(attrptr
->values
[0].string
.text
));
2618 a
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2620 for (attrptr
= response
->attrs
, group
= attrptr
->group_tag
;
2622 attrptr
= attrptr
->next
)
2624 if (attrptr
->group_tag
!= group
)
2626 int out_of_order
= 0; /* Are attribute groups out-of-order? */
2629 switch (attrptr
->group_tag
)
2634 case IPP_TAG_OPERATION
:
2638 case IPP_TAG_UNSUPPORTED_GROUP
:
2639 if (group
!= IPP_TAG_OPERATION
)
2644 case IPP_TAG_PRINTER
:
2645 if (group
!= IPP_TAG_OPERATION
&&
2646 group
!= IPP_TAG_UNSUPPORTED_GROUP
)
2650 case IPP_TAG_SUBSCRIPTION
:
2651 if (group
> attrptr
->group_tag
&&
2652 group
!= IPP_TAG_DOCUMENT
)
2657 if (group
> attrptr
->group_tag
)
2663 add_stringf(errors
, "Attribute groups out of order (%s < %s)",
2664 ippTagString(attrptr
->group_tag
),
2665 ippTagString(group
));
2667 if (attrptr
->group_tag
!= IPP_TAG_ZERO
)
2668 group
= attrptr
->group_tag
;
2671 validate_attr(errors
, attrptr
);
2675 if (cupsArrayFind(a
, attrptr
->name
))
2676 add_stringf(errors
, "Duplicate \"%s\" attribute in %s group",
2677 attrptr
->name
, ippTagString(group
));
2679 cupsArrayAdd(a
, attrptr
->name
);
2686 * Now check the test-defined expected status-code and attribute
2690 for (i
= 0; i
< num_statuses
; i
++)
2692 if (statuses
[i
].if_defined
&&
2693 !get_variable(vars
, statuses
[i
].if_defined
))
2696 if (statuses
[i
].if_not_defined
&&
2697 get_variable(vars
, statuses
[i
].if_not_defined
))
2700 if (response
->request
.status
.status_code
== statuses
[i
].status
)
2702 if (statuses
[i
].repeat_match
&&
2703 repeat_count
< statuses
[i
].repeat_limit
)
2708 else if (statuses
[i
].repeat_no_match
&&
2709 repeat_count
< statuses
[i
].repeat_limit
)
2713 if (i
== num_statuses
&& num_statuses
> 0)
2715 for (i
= 0; i
< num_statuses
; i
++)
2717 if (statuses
[i
].if_defined
&&
2718 !get_variable(vars
, statuses
[i
].if_defined
))
2721 if (statuses
[i
].if_not_defined
&&
2722 get_variable(vars
, statuses
[i
].if_not_defined
))
2725 if (!statuses
[i
].repeat_match
)
2726 add_stringf(errors
, "EXPECTED: STATUS %s (got %s)",
2727 ippErrorString(statuses
[i
].status
),
2728 ippErrorString(cupsLastError()));
2731 if ((attrptr
= ippFindAttribute(response
, "status-message",
2732 IPP_TAG_TEXT
)) != NULL
)
2733 add_stringf(errors
, "status-message=\"%s\"",
2734 attrptr
->values
[0].string
.text
);
2737 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
2739 if (expect
->if_defined
&& !get_variable(vars
, expect
->if_defined
))
2742 if (expect
->if_not_defined
&&
2743 get_variable(vars
, expect
->if_not_defined
))
2746 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
2748 if ((found
&& expect
->not_expect
) ||
2749 (!found
&& !(expect
->not_expect
|| expect
->optional
)) ||
2750 (found
&& !expect_matches(expect
, found
->value_tag
)) ||
2751 (found
&& expect
->in_group
&&
2752 found
->group_tag
!= expect
->in_group
))
2754 if (expect
->define_no_match
)
2755 set_variable(vars
, expect
->define_no_match
, "1");
2756 else if (!expect
->define_match
&& !expect
->define_value
)
2758 if (found
&& expect
->not_expect
)
2759 add_stringf(errors
, "NOT EXPECTED: %s", expect
->name
);
2760 else if (!found
&& !(expect
->not_expect
|| expect
->optional
))
2761 add_stringf(errors
, "EXPECTED: %s", expect
->name
);
2764 if (!expect_matches(expect
, found
->value_tag
))
2765 add_stringf(errors
, "EXPECTED: %s OF-TYPE %s (got %s)",
2766 expect
->name
, expect
->of_type
,
2767 ippTagString(found
->value_tag
));
2769 if (expect
->in_group
&& found
->group_tag
!= expect
->in_group
)
2770 add_stringf(errors
, "EXPECTED: %s IN-GROUP %s (got %s).",
2771 expect
->name
, ippTagString(expect
->in_group
),
2772 ippTagString(found
->group_tag
));
2776 if (expect
->repeat_no_match
&&
2777 repeat_count
< expect
->repeat_limit
)
2784 ippAttributeString(found
, buffer
, sizeof(buffer
));
2787 !with_value(NULL
, expect
->with_value
, expect
->with_flags
, found
,
2788 buffer
, sizeof(buffer
)))
2790 if (expect
->define_no_match
)
2791 set_variable(vars
, expect
->define_no_match
, "1");
2792 else if (!expect
->define_match
&& !expect
->define_value
&&
2793 !expect
->repeat_match
&& !expect
->repeat_no_match
)
2795 if (expect
->with_flags
& _CUPS_WITH_REGEX
)
2796 add_stringf(errors
, "EXPECTED: %s %s /%s/",
2798 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
2799 "WITH-ALL-VALUES" : "WITH-VALUE",
2800 expect
->with_value
);
2802 add_stringf(errors
, "EXPECTED: %s %s \"%s\"",
2804 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
2805 "WITH-ALL-VALUES" : "WITH-VALUE",
2806 expect
->with_value
);
2808 with_value(errors
, expect
->with_value
, expect
->with_flags
, found
,
2809 buffer
, sizeof(buffer
));
2812 if (expect
->repeat_no_match
&&
2813 repeat_count
< expect
->repeat_limit
)
2819 if (found
&& expect
->count
> 0 &&
2820 found
->num_values
!= expect
->count
)
2822 if (expect
->define_no_match
)
2823 set_variable(vars
, expect
->define_no_match
, "1");
2824 else if (!expect
->define_match
&& !expect
->define_value
)
2826 add_stringf(errors
, "EXPECTED: %s COUNT %d (got %d)", expect
->name
,
2827 expect
->count
, found
->num_values
);
2830 if (expect
->repeat_no_match
&&
2831 repeat_count
< expect
->repeat_limit
)
2837 if (found
&& expect
->same_count_as
)
2839 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
2842 if (!attrptr
|| attrptr
->num_values
!= found
->num_values
)
2844 if (expect
->define_no_match
)
2845 set_variable(vars
, expect
->define_no_match
, "1");
2846 else if (!expect
->define_match
&& !expect
->define_value
)
2850 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2851 "(not returned)", expect
->name
,
2852 found
->num_values
, expect
->same_count_as
);
2853 else if (attrptr
->num_values
!= found
->num_values
)
2855 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2856 "(%d values)", expect
->name
, found
->num_values
,
2857 expect
->same_count_as
, attrptr
->num_values
);
2860 if (expect
->repeat_no_match
&&
2861 repeat_count
< expect
->repeat_limit
)
2868 if (found
&& expect
->define_match
)
2869 set_variable(vars
, expect
->define_match
, "1");
2871 if (found
&& expect
->define_value
)
2872 set_variable(vars
, expect
->define_value
, buffer
);
2874 if (found
&& expect
->repeat_match
&&
2875 repeat_count
< expect
->repeat_limit
)
2881 * If we are going to repeat this test, sleep 1 second so we don't flood
2882 * the printer with requests...
2887 if (Output
== _CUPS_OUTPUT_TEST
)
2889 printf("%04d]\n", repeat_count
);
2893 sleep(repeat_interval
);
2894 repeat_interval
= _cupsNextDelay(repeat_interval
, &repeat_prev
);
2896 if (Output
== _CUPS_OUTPUT_TEST
)
2898 printf(" %-68.68s [", name
);
2903 while (repeat_test
);
2909 if (cupsArrayCount(errors
) > 0)
2910 prev_pass
= pass
= 0;
2917 if (Output
== _CUPS_OUTPUT_PLIST
)
2919 puts("<key>Successful</key>");
2920 puts(prev_pass
? "<true />" : "<false />");
2921 puts("<key>StatusCode</key>");
2922 print_xml_string("string", ippErrorString(cupsLastError()));
2923 puts("<key>ResponseAttributes</key>");
2926 for (attrptr
= response
? response
->attrs
: NULL
,
2927 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2929 attrptr
= attrptr
->next
)
2930 print_attr(attrptr
, &group
);
2934 else if (Output
== _CUPS_OUTPUT_TEST
)
2936 puts(prev_pass
? "PASS]" : "FAIL]");
2938 if (!prev_pass
|| (Verbosity
&& response
))
2940 printf(" RECEIVED: %lu bytes in response\n",
2941 (unsigned long)ippLength(response
));
2942 printf(" status-code = %s (%s)\n", ippErrorString(cupsLastError()),
2943 cupsLastErrorString());
2947 for (attrptr
= response
->attrs
;
2949 attrptr
= attrptr
->next
)
2950 print_attr(attrptr
, NULL
);
2954 else if (!prev_pass
)
2955 fprintf(stderr
, "%s\n", cupsLastErrorString());
2957 if (prev_pass
&& Output
>= _CUPS_OUTPUT_LIST
&& !Verbosity
&&
2960 size_t width
; /* Length of value */
2962 for (i
= 0; i
< num_displayed
; i
++)
2964 widths
[i
] = strlen(displayed
[i
]);
2966 for (attrptr
= ippFindAttribute(response
, displayed
[i
], IPP_TAG_ZERO
);
2968 attrptr
= ippFindNextAttribute(response
, displayed
[i
],
2971 width
= ippAttributeString(attrptr
, NULL
, 0);
2972 if (width
> widths
[i
])
2977 if (Output
== _CUPS_OUTPUT_CSV
)
2978 print_csv(NULL
, num_displayed
, displayed
, widths
);
2980 print_line(NULL
, num_displayed
, displayed
, widths
);
2982 attrptr
= response
->attrs
;
2986 while (attrptr
&& attrptr
->group_tag
<= IPP_TAG_OPERATION
)
2987 attrptr
= attrptr
->next
;
2991 if (Output
== _CUPS_OUTPUT_CSV
)
2992 print_csv(attrptr
, num_displayed
, displayed
, widths
);
2994 print_line(attrptr
, num_displayed
, displayed
, widths
);
2996 while (attrptr
&& attrptr
->group_tag
> IPP_TAG_OPERATION
)
2997 attrptr
= attrptr
->next
;
3001 else if (!prev_pass
)
3003 if (Output
== _CUPS_OUTPUT_PLIST
)
3005 puts("<key>Errors</key>");
3008 for (error
= (char *)cupsArrayFirst(errors
);
3010 error
= (char *)cupsArrayNext(errors
))
3011 print_xml_string("string", error
);
3017 for (error
= (char *)cupsArrayFirst(errors
);
3019 error
= (char *)cupsArrayNext(errors
))
3020 printf(" %s\n", error
);
3024 if (num_displayed
> 0 && !Verbosity
&& response
&&
3025 Output
== _CUPS_OUTPUT_TEST
)
3027 for (attrptr
= response
->attrs
;
3029 attrptr
= attrptr
->next
)
3033 for (i
= 0; i
< num_displayed
; i
++)
3035 if (!strcmp(displayed
[i
], attrptr
->name
))
3037 print_attr(attrptr
, NULL
);
3047 if (Output
== _CUPS_OUTPUT_PLIST
)
3052 ippDelete(response
);
3055 for (i
= 0; i
< num_statuses
; i
++)
3057 if (statuses
[i
].if_defined
)
3058 free(statuses
[i
].if_defined
);
3059 if (statuses
[i
].if_not_defined
)
3060 free(statuses
[i
].if_not_defined
);
3064 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3067 if (expect
->of_type
)
3068 free(expect
->of_type
);
3069 if (expect
->same_count_as
)
3070 free(expect
->same_count_as
);
3071 if (expect
->if_defined
)
3072 free(expect
->if_defined
);
3073 if (expect
->if_not_defined
)
3074 free(expect
->if_not_defined
);
3075 if (expect
->with_value
)
3076 free(expect
->with_value
);
3077 if (expect
->define_match
)
3078 free(expect
->define_match
);
3079 if (expect
->define_no_match
)
3080 free(expect
->define_no_match
);
3081 if (expect
->define_value
)
3082 free(expect
->define_value
);
3086 for (i
= 0; i
< num_displayed
; i
++)
3090 if (!ignore_errors
&& !prev_pass
)
3096 cupsArrayDelete(errors
);
3103 ippDelete(response
);
3105 for (i
= 0; i
< num_statuses
; i
++)
3107 if (statuses
[i
].if_defined
)
3108 free(statuses
[i
].if_defined
);
3109 if (statuses
[i
].if_not_defined
)
3110 free(statuses
[i
].if_not_defined
);
3113 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3116 if (expect
->of_type
)
3117 free(expect
->of_type
);
3118 if (expect
->same_count_as
)
3119 free(expect
->same_count_as
);
3120 if (expect
->if_defined
)
3121 free(expect
->if_defined
);
3122 if (expect
->if_not_defined
)
3123 free(expect
->if_not_defined
);
3124 if (expect
->with_value
)
3125 free(expect
->with_value
);
3126 if (expect
->define_match
)
3127 free(expect
->define_match
);
3128 if (expect
->define_no_match
)
3129 free(expect
->define_no_match
);
3130 if (expect
->define_value
)
3131 free(expect
->define_value
);
3134 for (i
= 0; i
< num_displayed
; i
++)
3142 * 'expand_variables()' - Expand variables in a string.
3146 expand_variables(_cups_vars_t
*vars
, /* I - Variables */
3147 char *dst
, /* I - Destination string buffer */
3148 const char *src
, /* I - Source string */
3149 size_t dstsize
) /* I - Size of destination buffer */
3151 char *dstptr
, /* Pointer into destination */
3152 *dstend
, /* End of destination */
3153 temp
[256], /* Temporary string */
3154 *tempptr
; /* Pointer into temporary string */
3155 const char *value
; /* Value to substitute */
3159 dstend
= dst
+ dstsize
- 1;
3161 while (*src
&& dstptr
< dstend
)
3166 * Substitute a string/number...
3169 if (!strncmp(src
, "$$", 2))
3174 else if (!strncmp(src
, "$ENV[", 5))
3176 strlcpy(temp
, src
+ 5, sizeof(temp
));
3178 for (tempptr
= temp
; *tempptr
; tempptr
++)
3179 if (*tempptr
== ']')
3185 value
= getenv(temp
);
3186 src
+= tempptr
- temp
+ 5;
3190 strlcpy(temp
, src
+ 1, sizeof(temp
));
3192 for (tempptr
= temp
; *tempptr
; tempptr
++)
3193 if (!isalnum(*tempptr
& 255) && *tempptr
!= '-' && *tempptr
!= '_')
3199 if (!strcmp(temp
, "uri"))
3201 else if (!strcmp(temp
, "filename"))
3202 value
= vars
->filename
;
3203 else if (!strcmp(temp
, "scheme") || !strcmp(temp
, "method"))
3204 value
= vars
->scheme
;
3205 else if (!strcmp(temp
, "username"))
3206 value
= vars
->userpass
;
3207 else if (!strcmp(temp
, "hostname"))
3208 value
= vars
->hostname
;
3209 else if (!strcmp(temp
, "port"))
3211 snprintf(temp
, sizeof(temp
), "%d", vars
->port
);
3214 else if (!strcmp(temp
, "resource"))
3215 value
= vars
->resource
;
3216 else if (!strcmp(temp
, "user"))
3219 value
= get_variable(vars
, temp
);
3221 src
+= tempptr
- temp
+ 1;
3231 strlcpy(dstptr
, value
, dstend
- dstptr
+ 1);
3232 dstptr
+= strlen(dstptr
);
3244 * 'expect_matches()' - Return true if the tag matches the specification.
3247 static int /* O - 1 if matches, 0 otherwise */
3249 _cups_expect_t
*expect
, /* I - Expected attribute */
3250 ipp_tag_t value_tag
) /* I - Value tag for attribute */
3252 int match
; /* Match? */
3253 char *of_type
, /* Type name to match */
3254 *next
, /* Next name to match */
3255 sep
; /* Separator character */
3259 * If we don't expect a particular type, return immediately...
3262 if (!expect
->of_type
)
3266 * Parse the "of_type" value since the string can contain multiple attribute
3267 * types separated by "," or "|"...
3270 for (of_type
= expect
->of_type
, match
= 0; !match
&& *of_type
; of_type
= next
)
3273 * Find the next separator, and set it (temporarily) to nul if present.
3276 for (next
= of_type
; *next
&& *next
!= '|' && *next
!= ','; next
++);
3278 if ((sep
= *next
) != '\0')
3282 * Support some meta-types to make it easier to write the test file.
3285 if (!strcmp(of_type
, "text"))
3286 match
= value_tag
== IPP_TAG_TEXTLANG
|| value_tag
== IPP_TAG_TEXT
;
3287 else if (!strcmp(of_type
, "name"))
3288 match
= value_tag
== IPP_TAG_NAMELANG
|| value_tag
== IPP_TAG_NAME
;
3289 else if (!strcmp(of_type
, "collection"))
3290 match
= value_tag
== IPP_TAG_BEGIN_COLLECTION
;
3292 match
= value_tag
== ippTagValue(of_type
);
3295 * Restore the separator if we have one...
3307 * 'get_collection()' - Get a collection value from the current test file.
3310 static ipp_t
* /* O - Collection value */
3311 get_collection(_cups_vars_t
*vars
, /* I - Variables */
3312 FILE *fp
, /* I - File to read from */
3313 int *linenum
) /* IO - Line number */
3315 char token
[1024], /* Token from file */
3316 temp
[1024], /* Temporary string */
3317 attr
[128]; /* Attribute name */
3318 ipp_tag_t value
; /* Current value type */
3319 ipp_t
*col
= ippNew(); /* Collection value */
3320 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
3323 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
3325 if (!strcmp(token
, "}"))
3327 else if (!strcmp(token
, "{") && lastcol
)
3330 * Another collection value
3333 ipp_t
*subcol
= get_collection(vars
, fp
, linenum
);
3334 /* Collection value */
3338 ipp_attribute_t
*tempcol
; /* Pointer to new buffer */
3342 * Reallocate memory...
3345 if ((tempcol
= realloc(lastcol
, sizeof(ipp_attribute_t
) +
3346 (lastcol
->num_values
+ 1) *
3347 sizeof(_ipp_value_t
))) == NULL
)
3349 print_fatal_error("Unable to allocate memory on line %d.", *linenum
);
3353 if (tempcol
!= lastcol
)
3356 * Reset pointers in the list...
3360 col
->prev
->next
= tempcol
;
3362 col
->attrs
= tempcol
;
3364 lastcol
= col
->current
= col
->last
= tempcol
;
3367 lastcol
->values
[lastcol
->num_values
].collection
= subcol
;
3368 lastcol
->num_values
++;
3373 else if (!_cups_strcasecmp(token
, "MEMBER"))
3381 if (!get_token(fp
, token
, sizeof(token
), linenum
))
3383 print_fatal_error("Missing MEMBER value tag on line %d.", *linenum
);
3387 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
3389 print_fatal_error("Bad MEMBER value tag \"%s\" on line %d.", token
,
3394 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
3396 print_fatal_error("Missing MEMBER name on line %d.", *linenum
);
3400 if (!get_token(fp
, temp
, sizeof(temp
), linenum
))
3402 print_fatal_error("Missing MEMBER value on line %d.", *linenum
);
3406 expand_variables(vars
, token
, temp
, sizeof(token
));
3410 case IPP_TAG_BOOLEAN
:
3411 if (!_cups_strcasecmp(token
, "true"))
3412 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
3414 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, atoi(token
));
3417 case IPP_TAG_INTEGER
:
3419 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
3422 case IPP_TAG_RESOLUTION
:
3424 int xres
, /* X resolution */
3425 yres
; /* Y resolution */
3426 char units
[6]; /* Units */
3428 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
3429 (_cups_strcasecmp(units
, "dpi") &&
3430 _cups_strcasecmp(units
, "dpc") &&
3431 _cups_strcasecmp(units
, "dpcm") &&
3432 _cups_strcasecmp(units
, "other")))
3434 print_fatal_error("Bad resolution value \"%s\" on line %d.",
3439 if (!_cups_strcasecmp(units
, "dpi"))
3440 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
3442 else if (!_cups_strcasecmp(units
, "dpc") ||
3443 !_cups_strcasecmp(units
, "dpcm"))
3444 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
3447 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
3452 case IPP_TAG_RANGE
:
3454 int lowers
[4], /* Lower value */
3455 uppers
[4], /* Upper values */
3456 num_vals
; /* Number of values */
3459 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
3460 lowers
+ 0, uppers
+ 0,
3461 lowers
+ 1, uppers
+ 1,
3462 lowers
+ 2, uppers
+ 2,
3463 lowers
+ 3, uppers
+ 3);
3465 if ((num_vals
& 1) || num_vals
== 0)
3467 print_fatal_error("Bad rangeOfInteger value \"%s\" on line %d.",
3472 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
3477 case IPP_TAG_BEGIN_COLLECTION
:
3478 if (!strcmp(token
, "{"))
3480 ipp_t
*subcol
= get_collection(vars
, fp
, linenum
);
3481 /* Collection value */
3485 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
3493 print_fatal_error("Bad collection value on line %d.", *linenum
);
3497 case IPP_TAG_STRING
:
3498 ippAddOctetString(col
, IPP_TAG_ZERO
, attr
, token
, strlen(token
));
3502 if (!strchr(token
, ','))
3503 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
3507 * Multiple string values...
3510 int num_values
; /* Number of values */
3511 char *values
[100], /* Values */
3512 *ptr
; /* Pointer to next value */
3518 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
3521 values
[num_values
] = ptr
;
3525 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
3526 NULL
, (const char **)values
);
3536 * If we get here there was a parse error; free memory and return.
3548 * 'get_filename()' - Get a filename based on the current test file.
3551 static char * /* O - Filename */
3552 get_filename(const char *testfile
, /* I - Current test file */
3553 char *dst
, /* I - Destination filename */
3554 const char *src
, /* I - Source filename */
3555 size_t dstsize
) /* I - Size of destination buffer */
3557 char *dstptr
; /* Pointer into destination */
3558 _cups_globals_t
*cg
= _cupsGlobals();
3562 if (*src
== '<' && src
[strlen(src
) - 1] == '>')
3565 * Map <filename> to CUPS_DATADIR/ipptool/filename...
3568 snprintf(dst
, dstsize
, "%s/ipptool/%s", cg
->cups_datadir
, src
+ 1);
3569 dstptr
= dst
+ strlen(dst
) - 1;
3573 else if (*src
== '/' || !strchr(testfile
, '/')
3575 || (isalpha(*src
& 255) && src
[1] == ':')
3580 * Use the path as-is...
3583 strlcpy(dst
, src
, dstsize
);
3588 * Make path relative to testfile...
3591 strlcpy(dst
, testfile
, dstsize
);
3592 if ((dstptr
= strrchr(dst
, '/')) != NULL
)
3595 dstptr
= dst
; /* Should never happen */
3597 strlcpy(dstptr
, src
, dstsize
- (dstptr
- dst
));
3605 * 'get_token()' - Get a token from a file.
3608 static char * /* O - Token from file or NULL on EOF */
3609 get_token(FILE *fp
, /* I - File to read from */
3610 char *buf
, /* I - Buffer to read into */
3611 int buflen
, /* I - Length of buffer */
3612 int *linenum
) /* IO - Current line number */
3614 int ch
, /* Character from file */
3615 quote
; /* Quoting character */
3616 char *bufptr
, /* Pointer into buffer */
3617 *bufend
; /* End of buffer */
3623 * Skip whitespace...
3626 while (isspace(ch
= getc(fp
)))
3638 else if (ch
== '\'' || ch
== '\"')
3641 * Quoted text or regular expression...
3646 bufend
= buf
+ buflen
- 1;
3648 while ((ch
= getc(fp
)) != EOF
)
3653 * Escape next character...
3656 if (bufptr
< bufend
)
3659 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
3662 else if (ch
== quote
)
3664 else if (bufptr
< bufend
)
3678 while ((ch
= getc(fp
)) != EOF
)
3687 * Whitespace delimited text...
3693 bufend
= buf
+ buflen
- 1;
3695 while ((ch
= getc(fp
)) != EOF
)
3696 if (isspace(ch
) || ch
== '#')
3698 else if (bufptr
< bufend
)
3703 else if (ch
== '\n')
3715 * 'get_variable()' - Get the value of a variable.
3718 static char * /* O - Value or NULL */
3719 get_variable(_cups_vars_t
*vars
, /* I - Variables */
3720 const char *name
) /* I - Variable name */
3722 _cups_var_t key
, /* Search key */
3723 *match
; /* Matching variable, if any */
3726 key
.name
= (char *)name
;
3727 match
= cupsArrayFind(vars
->vars
, &key
);
3729 return (match
? match
->value
: NULL
);
3734 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
3738 static char * /* O - ISO 8601 date/time string */
3739 iso_date(ipp_uchar_t
*date
) /* I - IPP (RFC 1903) date/time value */
3741 time_t utctime
; /* UTC time since 1970 */
3742 struct tm
*utcdate
; /* UTC date/time */
3743 static char buffer
[255]; /* String buffer */
3746 utctime
= ippDateToTime(date
);
3747 utcdate
= gmtime(&utctime
);
3749 snprintf(buffer
, sizeof(buffer
), "%04d-%02d-%02dT%02d:%02d:%02dZ",
3750 utcdate
->tm_year
+ 1900, utcdate
->tm_mon
+ 1, utcdate
->tm_mday
,
3751 utcdate
->tm_hour
, utcdate
->tm_min
, utcdate
->tm_sec
);
3758 * 'password_cb()' - Password callback for authenticated tests.
3761 static const char * /* O - Password */
3762 password_cb(const char *prompt
) /* I - Prompt (unused) */
3771 * 'print_attr()' - Print an attribute on the screen.
3775 print_attr(ipp_attribute_t
*attr
, /* I - Attribute to print */
3776 ipp_tag_t
*group
) /* IO - Current group */
3778 int i
; /* Looping var */
3779 ipp_attribute_t
*colattr
; /* Collection attribute */
3782 if (Output
== _CUPS_OUTPUT_PLIST
)
3784 if (!attr
->name
|| (group
&& *group
!= attr
->group_tag
))
3786 if (attr
->group_tag
!= IPP_TAG_ZERO
)
3793 *group
= attr
->group_tag
;
3799 print_xml_string("key", attr
->name
);
3800 if (attr
->num_values
> 1)
3803 switch (attr
->value_tag
)
3805 case IPP_TAG_INTEGER
:
3807 for (i
= 0; i
< attr
->num_values
; i
++)
3808 if (Output
== _CUPS_OUTPUT_PLIST
)
3809 printf("<integer>%d</integer>\n", attr
->values
[i
].integer
);
3811 printf("%d ", attr
->values
[i
].integer
);
3814 case IPP_TAG_BOOLEAN
:
3815 for (i
= 0; i
< attr
->num_values
; i
++)
3816 if (Output
== _CUPS_OUTPUT_PLIST
)
3817 puts(attr
->values
[i
].boolean
? "<true />" : "<false />");
3818 else if (attr
->values
[i
].boolean
)
3819 fputs("true ", stdout
);
3821 fputs("false ", stdout
);
3824 case IPP_TAG_RANGE
:
3825 for (i
= 0; i
< attr
->num_values
; i
++)
3826 if (Output
== _CUPS_OUTPUT_PLIST
)
3827 printf("<dict><key>lower</key><integer>%d</integer>"
3828 "<key>upper</key><integer>%d</integer></dict>\n",
3829 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
3831 printf("%d-%d ", attr
->values
[i
].range
.lower
,
3832 attr
->values
[i
].range
.upper
);
3835 case IPP_TAG_RESOLUTION
:
3836 for (i
= 0; i
< attr
->num_values
; i
++)
3837 if (Output
== _CUPS_OUTPUT_PLIST
)
3838 printf("<dict><key>xres</key><integer>%d</integer>"
3839 "<key>yres</key><integer>%d</integer>"
3840 "<key>units</key><string>%s</string></dict>\n",
3841 attr
->values
[i
].resolution
.xres
,
3842 attr
->values
[i
].resolution
.yres
,
3843 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
3846 printf("%dx%d%s ", attr
->values
[i
].resolution
.xres
,
3847 attr
->values
[i
].resolution
.yres
,
3848 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
3853 for (i
= 0; i
< attr
->num_values
; i
++)
3854 if (Output
== _CUPS_OUTPUT_PLIST
)
3855 printf("<date>%s</date>\n", iso_date(attr
->values
[i
].date
));
3857 printf("%s ", iso_date(attr
->values
[i
].date
));
3860 case IPP_TAG_STRING
:
3861 for (i
= 0; i
< attr
->num_values
; i
++)
3863 if (Output
== _CUPS_OUTPUT_PLIST
)
3865 char buffer
[IPP_MAX_LENGTH
* 5 / 4 + 1];
3868 printf("<data>%s</data>\n",
3869 httpEncode64_2(buffer
, sizeof(buffer
),
3870 attr
->values
[i
].unknown
.data
,
3871 attr
->values
[i
].unknown
.length
));
3875 char *ptr
, /* Pointer into data */
3876 *end
; /* End of data */
3879 for (ptr
= attr
->values
[i
].unknown
.data
,
3880 end
= ptr
+ attr
->values
[i
].unknown
.length
;
3884 if (*ptr
== '\\' || *ptr
== '\"')
3885 printf("\\%c", *ptr
);
3886 else if (!isprint(*ptr
& 255))
3887 printf("\\%03o", *ptr
& 255);
3898 case IPP_TAG_KEYWORD
:
3899 case IPP_TAG_CHARSET
:
3901 case IPP_TAG_MIMETYPE
:
3902 case IPP_TAG_LANGUAGE
:
3903 for (i
= 0; i
< attr
->num_values
; i
++)
3904 if (Output
== _CUPS_OUTPUT_PLIST
)
3905 print_xml_string("string", attr
->values
[i
].string
.text
);
3907 printf("\"%s\" ", attr
->values
[i
].string
.text
);
3910 case IPP_TAG_TEXTLANG
:
3911 case IPP_TAG_NAMELANG
:
3912 for (i
= 0; i
< attr
->num_values
; i
++)
3913 if (Output
== _CUPS_OUTPUT_PLIST
)
3915 fputs("<dict><key>language</key><string>", stdout
);
3916 print_xml_string(NULL
, attr
->values
[i
].string
.language
);
3917 fputs("</string><key>string</key><string>", stdout
);
3918 print_xml_string(NULL
, attr
->values
[i
].string
.text
);
3919 puts("</string></dict>");
3922 printf("\"%s\"[%s] ", attr
->values
[i
].string
.text
,
3923 attr
->values
[i
].string
.language
);
3926 case IPP_TAG_BEGIN_COLLECTION
:
3927 for (i
= 0; i
< attr
->num_values
; i
++)
3929 if (Output
== _CUPS_OUTPUT_PLIST
)
3932 for (colattr
= attr
->values
[i
].collection
->attrs
;
3934 colattr
= colattr
->next
)
3935 print_attr(colattr
, NULL
);
3943 print_col(attr
->values
[i
].collection
);
3949 if (Output
== _CUPS_OUTPUT_PLIST
)
3950 printf("<string><<%s>></string>\n",
3951 ippTagString(attr
->value_tag
));
3953 fputs(ippTagString(attr
->value_tag
), stdout
);
3957 if (attr
->num_values
> 1)
3962 char buffer
[8192]; /* Value buffer */
3964 if (Output
== _CUPS_OUTPUT_TEST
)
3968 puts(" -- separator --");
3972 printf(" %s (%s%s) = ", attr
->name
,
3973 attr
->num_values
> 1 ? "1setOf " : "",
3974 ippTagString(attr
->value_tag
));
3977 ippAttributeString(attr
, buffer
, sizeof(buffer
));
3984 * 'print_col()' - Print a collection attribute on the screen.
3988 print_col(ipp_t
*col
) /* I - Collection attribute to print */
3990 int i
; /* Looping var */
3991 ipp_attribute_t
*attr
; /* Current attribute in collection */
3994 fputs("{ ", stdout
);
3995 for (attr
= col
->attrs
; attr
; attr
= attr
->next
)
3997 printf("%s (%s%s) = ", attr
->name
, attr
->num_values
> 1 ? "1setOf " : "",
3998 ippTagString(attr
->value_tag
));
4000 switch (attr
->value_tag
)
4002 case IPP_TAG_INTEGER
:
4004 for (i
= 0; i
< attr
->num_values
; i
++)
4005 printf("%d ", attr
->values
[i
].integer
);
4008 case IPP_TAG_BOOLEAN
:
4009 for (i
= 0; i
< attr
->num_values
; i
++)
4010 if (attr
->values
[i
].boolean
)
4016 case IPP_TAG_NOVALUE
:
4020 case IPP_TAG_RANGE
:
4021 for (i
= 0; i
< attr
->num_values
; i
++)
4022 printf("%d-%d ", attr
->values
[i
].range
.lower
,
4023 attr
->values
[i
].range
.upper
);
4026 case IPP_TAG_RESOLUTION
:
4027 for (i
= 0; i
< attr
->num_values
; i
++)
4028 printf("%dx%d%s ", attr
->values
[i
].resolution
.xres
,
4029 attr
->values
[i
].resolution
.yres
,
4030 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
4034 case IPP_TAG_STRING
:
4037 case IPP_TAG_KEYWORD
:
4038 case IPP_TAG_CHARSET
:
4040 case IPP_TAG_MIMETYPE
:
4041 case IPP_TAG_LANGUAGE
:
4042 for (i
= 0; i
< attr
->num_values
; i
++)
4043 printf("\"%s\" ", attr
->values
[i
].string
.text
);
4046 case IPP_TAG_TEXTLANG
:
4047 case IPP_TAG_NAMELANG
:
4048 for (i
= 0; i
< attr
->num_values
; i
++)
4049 printf("\"%s\"[%s] ", attr
->values
[i
].string
.text
,
4050 attr
->values
[i
].string
.language
);
4053 case IPP_TAG_BEGIN_COLLECTION
:
4054 for (i
= 0; i
< attr
->num_values
; i
++)
4056 print_col(attr
->values
[i
].collection
);
4062 break; /* anti-compiler-warning-code */
4071 * 'print_csv()' - Print a line of CSV text.
4076 ipp_attribute_t
*attr
, /* I - First attribute for line */
4077 int num_displayed
, /* I - Number of attributes to display */
4078 char **displayed
, /* I - Attributes to display */
4079 size_t *widths
) /* I - Column widths */
4081 int i
; /* Looping var */
4082 size_t maxlength
; /* Max length of all columns */
4083 char *buffer
, /* String buffer */
4084 *bufptr
; /* Pointer into buffer */
4085 ipp_attribute_t
*current
; /* Current attribute */
4089 * Get the maximum string length we have to show and allocate...
4092 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4093 if (widths
[i
] > maxlength
)
4094 maxlength
= widths
[i
];
4098 if ((buffer
= malloc(maxlength
)) == NULL
)
4102 * Loop through the attributes to display...
4107 for (i
= 0; i
< num_displayed
; i
++)
4114 for (current
= attr
; current
; current
= current
->next
)
4118 else if (!strcmp(current
->name
, displayed
[i
]))
4120 ippAttributeString(current
, buffer
, maxlength
);
4125 if (strchr(buffer
, ',') != NULL
|| strchr(buffer
, '\"') != NULL
||
4126 strchr(buffer
, '\\') != NULL
)
4129 for (bufptr
= buffer
; *bufptr
; bufptr
++)
4131 if (*bufptr
== '\\' || *bufptr
== '\"')
4138 fputs(buffer
, stdout
);
4144 for (i
= 0; i
< num_displayed
; i
++)
4149 fputs(displayed
[i
], stdout
);
4159 * 'print_fatal_error()' - Print a fatal error message.
4163 print_fatal_error(const char *s
, /* I - Printf-style format string */
4164 ...) /* I - Additional arguments as needed */
4166 char buffer
[10240]; /* Format buffer */
4167 va_list ap
; /* Pointer to arguments */
4171 * Format the error message...
4175 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
4182 if (Output
== _CUPS_OUTPUT_PLIST
)
4185 print_xml_trailer(0, buffer
);
4188 _cupsLangPrintf(stderr
, "ipptool: %s", buffer
);
4193 * 'print_line()' - Print a line of formatted or CSV text.
4198 ipp_attribute_t
*attr
, /* I - First attribute for line */
4199 int num_displayed
, /* I - Number of attributes to display */
4200 char **displayed
, /* I - Attributes to display */
4201 size_t *widths
) /* I - Column widths */
4203 int i
; /* Looping var */
4204 size_t maxlength
; /* Max length of all columns */
4205 char *buffer
; /* String buffer */
4206 ipp_attribute_t
*current
; /* Current attribute */
4210 * Get the maximum string length we have to show and allocate...
4213 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4214 if (widths
[i
] > maxlength
)
4215 maxlength
= widths
[i
];
4219 if ((buffer
= malloc(maxlength
)) == NULL
)
4223 * Loop through the attributes to display...
4228 for (i
= 0; i
< num_displayed
; i
++)
4235 for (current
= attr
; current
; current
= current
->next
)
4239 else if (!strcmp(current
->name
, displayed
[i
]))
4241 ippAttributeString(current
, buffer
, maxlength
);
4246 printf("%*s", (int)-widths
[i
], buffer
);
4252 for (i
= 0; i
< num_displayed
; i
++)
4257 printf("%*s", (int)-widths
[i
], displayed
[i
]);
4261 for (i
= 0; i
< num_displayed
; i
++)
4266 memset(buffer
, '-', widths
[i
]);
4267 buffer
[widths
[i
]] = '\0';
4268 fputs(buffer
, stdout
);
4278 * 'print_xml_header()' - Print a standard XML plist header.
4282 print_xml_header(void)
4286 puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
4287 puts("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
4288 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
4289 puts("<plist version=\"1.0\">");
4291 puts("<key>Transfer</key>");
4292 printf("<string>%s</string>\n",
4293 Transfer
== _CUPS_TRANSFER_AUTO
? "auto" :
4294 Transfer
== _CUPS_TRANSFER_CHUNKED
? "chunked" : "length");
4295 puts("<key>Tests</key>");
4304 * 'print_xml_string()' - Print an XML string with escaping.
4308 print_xml_string(const char *element
, /* I - Element name or NULL */
4309 const char *s
) /* I - String to print */
4312 printf("<%s>", element
);
4317 fputs("&", stdout
);
4319 fputs("<", stdout
);
4321 fputs(">", stdout
);
4322 else if ((*s
& 0xe0) == 0xc0)
4325 * Validate UTF-8 two-byte sequence...
4328 if ((s
[1] & 0xc0) != 0x80)
4339 else if ((*s
& 0xf0) == 0xe0)
4342 * Validate UTF-8 three-byte sequence...
4345 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80)
4357 else if ((*s
& 0xf8) == 0xf0)
4360 * Validate UTF-8 four-byte sequence...
4363 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80 ||
4364 (s
[3] & 0xc0) != 0x80)
4377 else if ((*s
& 0x80) || (*s
< ' ' && !isspace(*s
& 255)))
4380 * Invalid control character...
4392 printf("</%s>\n", element
);
4397 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
4401 print_xml_trailer(int success
, /* I - 1 on success, 0 on failure */
4402 const char *message
) /* I - Error message or NULL */
4407 puts("<key>Successful</key>");
4408 puts(success
? "<true />" : "<false />");
4411 puts("<key>ErrorMessage</key>");
4412 print_xml_string("string", message
);
4423 * 'set_variable()' - Set a variable value.
4427 set_variable(_cups_vars_t
*vars
, /* I - Variables */
4428 const char *name
, /* I - Variable name */
4429 const char *value
) /* I - Value string */
4431 _cups_var_t key
, /* Search key */
4432 *var
; /* New variable */
4435 if (!_cups_strcasecmp(name
, "filename"))
4438 free(vars
->filename
);
4440 vars
->filename
= strdup(value
);
4444 key
.name
= (char *)name
;
4445 if ((var
= cupsArrayFind(vars
->vars
, &key
)) != NULL
)
4448 var
->value
= strdup(value
);
4450 else if ((var
= malloc(sizeof(_cups_var_t
))) == NULL
)
4452 print_fatal_error("Unable to allocate memory for variable \"%s\".", name
);
4457 var
->name
= strdup(name
);
4458 var
->value
= strdup(value
);
4460 cupsArrayAdd(vars
->vars
, var
);
4467 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
4471 sigterm_handler(int sig
) /* I - Signal number (unused) */
4477 signal(SIGINT
, SIG_DFL
);
4478 signal(SIGTERM
, SIG_DFL
);
4484 * 'timeout_cb()' - Handle HTTP timeouts.
4487 static int /* O - 1 to continue, 0 to cancel */
4488 timeout_cb(http_t
*http
, /* I - Connection to server (unused) */
4489 void *user_data
) /* I - User data (unused) */
4494 /* Always cancel on timeout */
4500 * 'usage()' - Show program usage.
4506 _cupsLangPuts(stderr
, _("Usage: ipptool [options] URI filename [ ... "
4508 _cupsLangPuts(stderr
, _("Options:"));
4509 _cupsLangPuts(stderr
, _(" -4 Connect using IPv4."));
4510 _cupsLangPuts(stderr
, _(" -6 Connect using IPv6."));
4511 _cupsLangPuts(stderr
, _(" -C Send requests using "
4512 "chunking (default)."));
4513 _cupsLangPuts(stdout
, _(" -E Test with HTTP Upgrade to "
4515 _cupsLangPuts(stderr
, _(" -I Ignore errors."));
4516 _cupsLangPuts(stderr
, _(" -L Send requests using "
4517 "content-length."));
4518 _cupsLangPuts(stderr
, _(" -S Test with SSL "
4520 _cupsLangPuts(stderr
, _(" -T seconds Set the receive/send "
4521 "timeout in seconds."));
4522 _cupsLangPuts(stderr
, _(" -V version Set default IPP "
4524 _cupsLangPuts(stderr
, _(" -X Produce XML plist instead "
4526 _cupsLangPuts(stderr
, _(" -d name=value Set named variable to "
4528 _cupsLangPuts(stderr
, _(" -f filename Set default request "
4530 _cupsLangPuts(stderr
, _(" -i seconds Repeat the last file with "
4531 "the given time interval."));
4532 _cupsLangPuts(stderr
, _(" -n count Repeat the last file the "
4533 "given number of times."));
4534 _cupsLangPuts(stderr
, _(" -q Run silently."));
4535 _cupsLangPuts(stderr
, _(" -t Produce a test report."));
4536 _cupsLangPuts(stderr
, _(" -v Be verbose."));
4543 * 'validate_attr()' - Determine whether an attribute is valid.
4546 static int /* O - 1 if valid, 0 otherwise */
4547 validate_attr(cups_array_t
*errors
, /* I - Errors array */
4548 ipp_attribute_t
*attr
) /* I - Attribute to validate */
4550 int i
; /* Looping var */
4551 char scheme
[64], /* Scheme from URI */
4552 userpass
[256], /* Username/password from URI */
4553 hostname
[256], /* Hostname from URI */
4554 resource
[1024]; /* Resource from URI */
4555 int port
, /* Port number from URI */
4556 uri_status
, /* URI separation status */
4557 valid
= 1; /* Is the attribute valid? */
4558 const char *ptr
; /* Pointer into string */
4559 ipp_attribute_t
*colattr
; /* Collection attribute */
4560 regex_t re
; /* Regular expression */
4561 ipp_uchar_t
*date
; /* Current date value */
4572 * Validate the attribute name.
4575 for (ptr
= attr
->name
; *ptr
; ptr
++)
4576 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' && *ptr
!= '_')
4579 if (*ptr
|| ptr
== attr
->name
)
4584 "\"%s\": Bad attribute name - invalid character "
4585 "(RFC 2911 section 4.1.3).", attr
->name
);
4588 if ((ptr
- attr
->name
) > 255)
4593 "\"%s\": Bad attribute name - bad length "
4594 "(RFC 2911 section 4.1.3).", attr
->name
);
4597 switch (attr
->value_tag
)
4599 case IPP_TAG_INTEGER
:
4602 case IPP_TAG_BOOLEAN
:
4603 for (i
= 0; i
< attr
->num_values
; i
++)
4605 if (attr
->values
[i
].boolean
!= 0 &&
4606 attr
->values
[i
].boolean
!= 1)
4611 "\"%s\": Bad boolen value %d "
4612 "(RFC 2911 section 4.1.11).", attr
->name
,
4613 attr
->values
[i
].boolean
);
4619 for (i
= 0; i
< attr
->num_values
; i
++)
4621 if (attr
->values
[i
].integer
< 1)
4626 "\"%s\": Bad enum value %d - out of range "
4627 "(RFC 2911 section 4.1.4).", attr
->name
,
4628 attr
->values
[i
].integer
);
4633 case IPP_TAG_STRING
:
4634 for (i
= 0; i
< attr
->num_values
; i
++)
4636 if (attr
->values
[i
].unknown
.length
> IPP_MAX_OCTETSTRING
)
4641 "\"%s\": Bad octetString value - bad length %d "
4642 "(RFC 2911 section 4.1.10).", attr
->name
,
4643 attr
->values
[i
].unknown
.length
);
4649 for (i
= 0; i
< attr
->num_values
; i
++)
4651 date
= attr
->values
[i
].date
;
4653 if (date
[2] < 1 || date
[2] > 12)
4658 "\"%s\": Bad dateTime month %u "
4659 "(RFC 2911 section 4.1.14).", attr
->name
, date
[2]);
4662 if (date
[3] < 1 || date
[3] > 31)
4667 "\"%s\": Bad dateTime day %u "
4668 "(RFC 2911 section 4.1.14).", attr
->name
, date
[3]);
4676 "\"%s\": Bad dateTime hours %u "
4677 "(RFC 2911 section 4.1.14).", attr
->name
, date
[4]);
4685 "\"%s\": Bad dateTime minutes %u "
4686 "(RFC 2911 section 4.1.14).", attr
->name
, date
[5]);
4694 "\"%s\": Bad dateTime seconds %u "
4695 "(RFC 2911 section 4.1.14).", attr
->name
, date
[6]);
4703 "\"%s\": Bad dateTime deciseconds %u "
4704 "(RFC 2911 section 4.1.14).", attr
->name
, date
[7]);
4707 if (date
[8] != '-' && date
[8] != '+')
4712 "\"%s\": Bad dateTime UTC sign '%c' "
4713 "(RFC 2911 section 4.1.14).", attr
->name
, date
[8]);
4721 "\"%s\": Bad dateTime UTC hours %u "
4722 "(RFC 2911 section 4.1.14).", attr
->name
, date
[9]);
4730 "\"%s\": Bad dateTime UTC minutes %u "
4731 "(RFC 2911 section 4.1.14).", attr
->name
, date
[10]);
4736 case IPP_TAG_RESOLUTION
:
4737 for (i
= 0; i
< attr
->num_values
; i
++)
4739 if (attr
->values
[i
].resolution
.xres
<= 0)
4744 "\"%s\": Bad resolution value %dx%d%s - cross "
4745 "feed resolution must be positive "
4746 "(RFC 2911 section 4.1.15).", attr
->name
,
4747 attr
->values
[i
].resolution
.xres
,
4748 attr
->values
[i
].resolution
.yres
,
4749 attr
->values
[i
].resolution
.units
==
4750 IPP_RES_PER_INCH
? "dpi" :
4751 attr
->values
[i
].resolution
.units
==
4752 IPP_RES_PER_CM
? "dpcm" : "unknown");
4755 if (attr
->values
[i
].resolution
.yres
<= 0)
4760 "\"%s\": Bad resolution value %dx%d%s - feed "
4761 "resolution must be positive "
4762 "(RFC 2911 section 4.1.15).", attr
->name
,
4763 attr
->values
[i
].resolution
.xres
,
4764 attr
->values
[i
].resolution
.yres
,
4765 attr
->values
[i
].resolution
.units
==
4766 IPP_RES_PER_INCH
? "dpi" :
4767 attr
->values
[i
].resolution
.units
==
4768 IPP_RES_PER_CM
? "dpcm" : "unknown");
4771 if (attr
->values
[i
].resolution
.units
!= IPP_RES_PER_INCH
&&
4772 attr
->values
[i
].resolution
.units
!= IPP_RES_PER_CM
)
4777 "\"%s\": Bad resolution value %dx%d%s - bad "
4778 "units value (RFC 2911 section 4.1.15).",
4779 attr
->name
, attr
->values
[i
].resolution
.xres
,
4780 attr
->values
[i
].resolution
.yres
,
4781 attr
->values
[i
].resolution
.units
==
4782 IPP_RES_PER_INCH
? "dpi" :
4783 attr
->values
[i
].resolution
.units
==
4784 IPP_RES_PER_CM
? "dpcm" : "unknown");
4789 case IPP_TAG_RANGE
:
4790 for (i
= 0; i
< attr
->num_values
; i
++)
4792 if (attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
4797 "\"%s\": Bad rangeOfInteger value %d-%d - lower "
4798 "greater than upper (RFC 2911 section 4.1.13).",
4799 attr
->name
, attr
->values
[i
].range
.lower
,
4800 attr
->values
[i
].range
.upper
);
4805 case IPP_TAG_BEGIN_COLLECTION
:
4806 for (i
= 0; i
< attr
->num_values
; i
++)
4808 for (colattr
= attr
->values
[i
].collection
->attrs
;
4810 colattr
= colattr
->next
)
4812 if (!validate_attr(NULL
, colattr
))
4819 if (colattr
&& errors
)
4821 add_stringf(errors
, "\"%s\": Bad collection value.", attr
->name
);
4825 validate_attr(errors
, colattr
);
4826 colattr
= colattr
->next
;
4833 case IPP_TAG_TEXTLANG
:
4834 for (i
= 0; i
< attr
->num_values
; i
++)
4836 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4838 if ((*ptr
& 0xe0) == 0xc0)
4841 if ((*ptr
& 0xc0) != 0x80)
4844 else if ((*ptr
& 0xf0) == 0xe0)
4847 if ((*ptr
& 0xc0) != 0x80)
4850 if ((*ptr
& 0xc0) != 0x80)
4853 else if ((*ptr
& 0xf8) == 0xf0)
4856 if ((*ptr
& 0xc0) != 0x80)
4859 if ((*ptr
& 0xc0) != 0x80)
4862 if ((*ptr
& 0xc0) != 0x80)
4865 else if (*ptr
& 0x80)
4874 "\"%s\": Bad text value \"%s\" - bad UTF-8 "
4875 "sequence (RFC 2911 section 4.1.1).", attr
->name
,
4876 attr
->values
[i
].string
.text
);
4879 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_TEXT
- 1))
4884 "\"%s\": Bad text value \"%s\" - bad length %d "
4885 "(RFC 2911 section 4.1.1).", attr
->name
,
4886 attr
->values
[i
].string
.text
,
4887 (int)strlen(attr
->values
[i
].string
.text
));
4893 case IPP_TAG_NAMELANG
:
4894 for (i
= 0; i
< attr
->num_values
; i
++)
4896 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4898 if ((*ptr
& 0xe0) == 0xc0)
4901 if ((*ptr
& 0xc0) != 0x80)
4904 else if ((*ptr
& 0xf0) == 0xe0)
4907 if ((*ptr
& 0xc0) != 0x80)
4910 if ((*ptr
& 0xc0) != 0x80)
4913 else if ((*ptr
& 0xf8) == 0xf0)
4916 if ((*ptr
& 0xc0) != 0x80)
4919 if ((*ptr
& 0xc0) != 0x80)
4922 if ((*ptr
& 0xc0) != 0x80)
4925 else if (*ptr
& 0x80)
4934 "\"%s\": Bad name value \"%s\" - bad UTF-8 "
4935 "sequence (RFC 2911 section 4.1.2).", attr
->name
,
4936 attr
->values
[i
].string
.text
);
4939 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_NAME
- 1))
4944 "\"%s\": Bad name value \"%s\" - bad length %d "
4945 "(RFC 2911 section 4.1.2).", attr
->name
,
4946 attr
->values
[i
].string
.text
,
4947 (int)strlen(attr
->values
[i
].string
.text
));
4952 case IPP_TAG_KEYWORD
:
4953 for (i
= 0; i
< attr
->num_values
; i
++)
4955 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4956 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' &&
4960 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
4965 "\"%s\": Bad keyword value \"%s\" - invalid "
4966 "character (RFC 2911 section 4.1.3).",
4967 attr
->name
, attr
->values
[i
].string
.text
);
4970 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_KEYWORD
- 1))
4975 "\"%s\": Bad keyword value \"%s\" - bad "
4976 "length %d (RFC 2911 section 4.1.3).",
4977 attr
->name
, attr
->values
[i
].string
.text
,
4978 (int)strlen(attr
->values
[i
].string
.text
));
4984 for (i
= 0; i
< attr
->num_values
; i
++)
4986 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
,
4987 attr
->values
[i
].string
.text
,
4988 scheme
, sizeof(scheme
),
4989 userpass
, sizeof(userpass
),
4990 hostname
, sizeof(hostname
),
4991 &port
, resource
, sizeof(resource
));
4993 if (uri_status
< HTTP_URI_OK
)
4998 "\"%s\": Bad URI value \"%s\" - %s "
4999 "(RFC 2911 section 4.1.5).", attr
->name
,
5000 attr
->values
[i
].string
.text
,
5001 URIStatusStrings
[uri_status
-
5002 HTTP_URI_OVERFLOW
]);
5005 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_URI
- 1))
5010 "\"%s\": Bad URI value \"%s\" - bad length %d "
5011 "(RFC 2911 section 4.1.5).", attr
->name
,
5012 attr
->values
[i
].string
.text
,
5013 (int)strlen(attr
->values
[i
].string
.text
));
5018 case IPP_TAG_URISCHEME
:
5019 for (i
= 0; i
< attr
->num_values
; i
++)
5021 ptr
= attr
->values
[i
].string
.text
;
5022 if (islower(*ptr
& 255))
5024 for (ptr
++; *ptr
; ptr
++)
5025 if (!islower(*ptr
& 255) && !isdigit(*ptr
& 255) &&
5026 *ptr
!= '+' && *ptr
!= '-' && *ptr
!= '.')
5030 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5035 "\"%s\": Bad uriScheme value \"%s\" - bad "
5036 "characters (RFC 2911 section 4.1.6).",
5037 attr
->name
, attr
->values
[i
].string
.text
);
5040 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_URISCHEME
- 1))
5045 "\"%s\": Bad uriScheme value \"%s\" - bad "
5046 "length %d (RFC 2911 section 4.1.6).",
5047 attr
->name
, attr
->values
[i
].string
.text
,
5048 (int)strlen(attr
->values
[i
].string
.text
));
5053 case IPP_TAG_CHARSET
:
5054 for (i
= 0; i
< attr
->num_values
; i
++)
5056 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5057 if (!isprint(*ptr
& 255) || isupper(*ptr
& 255) ||
5058 isspace(*ptr
& 255))
5061 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5066 "\"%s\": Bad charset value \"%s\" - bad "
5067 "characters (RFC 2911 section 4.1.7).",
5068 attr
->name
, attr
->values
[i
].string
.text
);
5071 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_CHARSET
- 1))
5076 "\"%s\": Bad charset value \"%s\" - bad "
5077 "length %d (RFC 2911 section 4.1.7).",
5078 attr
->name
, attr
->values
[i
].string
.text
,
5079 (int)strlen(attr
->values
[i
].string
.text
));
5084 case IPP_TAG_LANGUAGE
:
5086 * The following regular expression is derived from the ABNF for
5087 * language tags in RFC 4646. All I can say is that this is the
5088 * easiest way to check the values...
5091 if ((i
= regcomp(&re
,
5093 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
5095 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
5096 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
5097 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
5098 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
5099 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
5101 "x(-[a-z0-9]{1,8})+" /* privateuse */
5103 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
5105 REG_NOSUB
| REG_EXTENDED
)) != 0)
5107 char temp
[256]; /* Temporary error string */
5109 regerror(i
, &re
, temp
, sizeof(temp
));
5110 print_fatal_error("Unable to compile naturalLanguage regular "
5111 "expression: %s.", temp
);
5115 for (i
= 0; i
< attr
->num_values
; i
++)
5117 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5122 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5123 "characters (RFC 2911 section 4.1.8).",
5124 attr
->name
, attr
->values
[i
].string
.text
);
5127 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_LANGUAGE
- 1))
5132 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5133 "length %d (RFC 2911 section 4.1.8).",
5134 attr
->name
, attr
->values
[i
].string
.text
,
5135 (int)strlen(attr
->values
[i
].string
.text
));
5142 case IPP_TAG_MIMETYPE
:
5144 * The following regular expression is derived from the ABNF for
5145 * language tags in RFC 2045 and 4288. All I can say is that this is
5146 * the easiest way to check the values...
5149 if ((i
= regcomp(&re
,
5151 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
5153 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
5154 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
5155 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
5158 REG_NOSUB
| REG_EXTENDED
)) != 0)
5160 char temp
[256]; /* Temporary error string */
5162 regerror(i
, &re
, temp
, sizeof(temp
));
5163 print_fatal_error("Unable to compile mimeMediaType regular "
5164 "expression: %s.", temp
);
5168 for (i
= 0; i
< attr
->num_values
; i
++)
5170 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5175 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5176 "characters (RFC 2911 section 4.1.9).",
5177 attr
->name
, attr
->values
[i
].string
.text
);
5180 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_MIMETYPE
- 1))
5185 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5186 "length %d (RFC 2911 section 4.1.9).",
5187 attr
->name
, attr
->values
[i
].string
.text
,
5188 (int)strlen(attr
->values
[i
].string
.text
));
5204 * 'with_value()' - Test a WITH-VALUE predicate.
5207 static int /* O - 1 on match, 0 on non-match */
5208 with_value(cups_array_t
*errors
, /* I - Errors array */
5209 char *value
, /* I - Value string */
5210 int flags
, /* I - Flags for match */
5211 ipp_attribute_t
*attr
, /* I - Attribute to compare */
5212 char *matchbuf
, /* I - Buffer to hold matching value */
5213 size_t matchlen
) /* I - Length of match buffer */
5215 int i
, /* Looping var */
5217 char *valptr
; /* Pointer into value */
5221 match
= (flags
& _CUPS_WITH_ALL
) ? 1 : 0;
5224 * NULL matches everything.
5227 if (!value
|| !*value
)
5231 * Compare the value string to the attribute value.
5234 switch (attr
->value_tag
)
5236 case IPP_TAG_INTEGER
:
5238 for (i
= 0; i
< attr
->num_values
; i
++)
5240 char op
, /* Comparison operator */
5241 *nextptr
; /* Next pointer */
5242 int intvalue
, /* Integer value */
5243 valmatch
= 0; /* Does the current value match? */
5247 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5248 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5249 *valptr
== '=' || *valptr
== '>')
5252 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5254 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5262 intvalue
= strtol(valptr
, &nextptr
, 0);
5263 if (nextptr
== valptr
)
5267 if ((op
== '=' && attr
->values
[i
].integer
== intvalue
) ||
5268 (op
== '<' && attr
->values
[i
].integer
< intvalue
) ||
5269 (op
== '>' && attr
->values
[i
].integer
> intvalue
))
5272 snprintf(matchbuf
, matchlen
, "%d",
5273 attr
->values
[i
].integer
);
5280 if (flags
& _CUPS_WITH_ALL
)
5295 if (!match
&& errors
)
5297 for (i
= 0; i
< attr
->num_values
; i
++)
5298 add_stringf(errors
, "GOT: %s=%d", attr
->name
,
5299 attr
->values
[i
].integer
);
5303 case IPP_TAG_RANGE
:
5304 for (i
= 0; i
< attr
->num_values
; i
++)
5306 char op
, /* Comparison operator */
5307 *nextptr
; /* Next pointer */
5308 int intvalue
, /* Integer value */
5309 valmatch
= 0; /* Does the current value match? */
5313 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5314 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5315 *valptr
== '=' || *valptr
== '>')
5318 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5320 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5328 intvalue
= strtol(valptr
, &nextptr
, 0);
5329 if (nextptr
== valptr
)
5333 if ((op
== '=' && (attr
->values
[i
].range
.lower
== intvalue
||
5334 attr
->values
[i
].range
.upper
== intvalue
)) ||
5335 (op
== '<' && attr
->values
[i
].range
.upper
< intvalue
) ||
5336 (op
== '>' && attr
->values
[i
].range
.upper
> intvalue
))
5339 snprintf(matchbuf
, matchlen
, "%d-%d",
5340 attr
->values
[0].range
.lower
,
5341 attr
->values
[0].range
.upper
);
5348 if (flags
& _CUPS_WITH_ALL
)
5363 if (!match
&& errors
)
5365 for (i
= 0; i
< attr
->num_values
; i
++)
5366 add_stringf(errors
, "GOT: %s=%d-%d", attr
->name
,
5367 attr
->values
[i
].range
.lower
,
5368 attr
->values
[i
].range
.upper
);
5372 case IPP_TAG_BOOLEAN
:
5373 for (i
= 0; i
< attr
->num_values
; i
++)
5375 if (!strcmp(value
, "true") == attr
->values
[i
].boolean
)
5378 strlcpy(matchbuf
, value
, matchlen
);
5380 if (!(flags
& _CUPS_WITH_ALL
))
5386 else if (flags
& _CUPS_WITH_ALL
)
5393 if (!match
&& errors
)
5395 for (i
= 0; i
< attr
->num_values
; i
++)
5396 add_stringf(errors
, "GOT: %s=%s", attr
->name
,
5397 attr
->values
[i
].boolean
? "true" : "false");
5401 case IPP_TAG_NOVALUE
:
5402 case IPP_TAG_UNKNOWN
:
5405 case IPP_TAG_CHARSET
:
5406 case IPP_TAG_KEYWORD
:
5407 case IPP_TAG_LANGUAGE
:
5408 case IPP_TAG_MIMETYPE
:
5410 case IPP_TAG_NAMELANG
:
5412 case IPP_TAG_TEXTLANG
:
5414 case IPP_TAG_URISCHEME
:
5415 if (flags
& _CUPS_WITH_REGEX
)
5418 * Value is an extended, case-sensitive POSIX regular expression...
5421 regex_t re
; /* Regular expression */
5423 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
5425 char temp
[256]; /* Temporary string */
5427 regerror(i
, &re
, temp
, sizeof(temp
));
5429 print_fatal_error("Unable to compile WITH-VALUE regular expression "
5430 "\"%s\" - %s", value
, temp
);
5435 * See if ALL of the values match the given regular expression.
5438 for (i
= 0; i
< attr
->num_values
; i
++)
5440 if (!regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5443 strlcpy(matchbuf
, attr
->values
[i
].string
.text
, matchlen
);
5445 if (!(flags
& _CUPS_WITH_ALL
))
5451 else if (flags
& _CUPS_WITH_ALL
)
5463 * Value is a literal string, see if the value(s) match...
5466 for (i
= 0; i
< attr
->num_values
; i
++)
5468 if (!strcmp(value
, attr
->values
[i
].string
.text
))
5471 strlcpy(matchbuf
, attr
->values
[i
].string
.text
, matchlen
);
5473 if (!(flags
& _CUPS_WITH_ALL
))
5479 else if (flags
& _CUPS_WITH_ALL
)
5487 if (!match
&& errors
)
5489 for (i
= 0; i
< attr
->num_values
; i
++)
5490 add_stringf(errors
, "GOT: %s=\"%s\"", attr
->name
,
5491 attr
->values
[i
].string
.text
);