4 * ipptool command for CUPS.
6 * Copyright 2007-2013 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
15 * This file is subject to the Apple OS-Developed Software exception.
19 * Include necessary headers...
22 #include <cups/cups-private.h>
23 #include <cups/file-private.h>
33 #endif /* !O_BINARY */
40 typedef enum _cups_transfer_e
/**** How to send request data ****/
42 _CUPS_TRANSFER_AUTO
, /* Chunk for files, length for static */
43 _CUPS_TRANSFER_CHUNKED
, /* Chunk always */
44 _CUPS_TRANSFER_LENGTH
/* Length always */
47 typedef enum _cups_output_e
/**** Output mode ****/
49 _CUPS_OUTPUT_QUIET
, /* No output */
50 _CUPS_OUTPUT_TEST
, /* Traditional CUPS test output */
51 _CUPS_OUTPUT_PLIST
, /* XML plist test output */
52 _CUPS_OUTPUT_LIST
, /* Tabular list output */
53 _CUPS_OUTPUT_CSV
/* Comma-separated values output */
56 typedef enum _cups_with_e
/**** WITH flags ****/
58 _CUPS_WITH_LITERAL
= 0, /* Match string is a literal value */
59 _CUPS_WITH_ALL
= 1, /* Must match all values */
60 _CUPS_WITH_REGEX
= 2, /* Match string is a regular expression */
61 _CUPS_WITH_HOSTNAME
= 4, /* Match string is a URI hostname */
62 _CUPS_WITH_RESOURCE
= 8, /* Match string is a URI resource */
63 _CUPS_WITH_SCHEME
= 16 /* Match string is a URI scheme */
66 typedef struct _cups_expect_s
/**** Expected attribute info ****/
68 int optional
, /* Optional attribute? */
69 not_expect
; /* Don't expect attribute? */
70 char *name
, /* Attribute name */
71 *of_type
, /* Type name */
72 *same_count_as
, /* Parallel attribute name */
73 *if_defined
, /* Only required if variable defined */
74 *if_not_defined
, /* Only required if variable is not defined */
75 *with_value
, /* Attribute must include this value */
76 *define_match
, /* Variable to define on match */
77 *define_no_match
, /* Variable to define on no-match */
78 *define_value
; /* Variable to define with value */
79 int repeat_limit
, /* Maximum number of times to repeat */
80 repeat_match
, /* Repeat test on match */
81 repeat_no_match
, /* Repeat test on no match */
82 with_flags
, /* WITH flags */
83 count
; /* Expected count if > 0 */
84 ipp_tag_t in_group
; /* IN-GROUP value */
87 typedef struct _cups_status_s
/**** Status info ****/
89 ipp_status_t status
; /* Expected status code */
90 char *if_defined
, /* Only if variable is defined */
91 *if_not_defined
, /* Only if variable is not defined */
92 *define_match
, /* Variable to define on match */
93 *define_no_match
, /* Variable to define on no-match */
94 *define_value
; /* Variable to define with value */
95 int repeat_limit
, /* Maximum number of times to repeat */
96 repeat_match
, /* Repeat the test when it does not match */
97 repeat_no_match
; /* Repeat the test when it matches */
100 typedef struct _cups_var_s
/**** Variable ****/
102 char *name
, /* Name of variable */
103 *value
; /* Value of variable */
106 typedef struct _cups_vars_s
/**** Set of variables ****/
108 char *uri
, /* URI for printer */
109 *filename
, /* Filename */
110 scheme
[64], /* Scheme from URI */
111 userpass
[256], /* Username/password from URI */
112 hostname
[256], /* Hostname from URI */
113 resource
[1024]; /* Resource path from URI */
114 int port
; /* Port number from URI */
115 http_encryption_t encryption
; /* Encryption for connection? */
116 double timeout
; /* Timeout for connection */
117 int family
; /* Address family */
118 cups_array_t
*vars
; /* Array of variables */
126 _cups_transfer_t Transfer
= _CUPS_TRANSFER_AUTO
;
127 /* How to transfer requests */
128 _cups_output_t Output
= _CUPS_OUTPUT_LIST
;
130 int Cancel
= 0, /* Cancel test? */
131 IgnoreErrors
= 0, /* Ignore errors? */
132 StopAfterIncludeError
= 0,
133 /* Stop after include errors? */
134 Verbosity
= 0, /* Show all attributes? */
135 Version
= 11, /* Default IPP version */
136 XMLHeader
= 0, /* 1 if header is written */
137 TestCount
= 0, /* Number of tests run */
138 PassCount
= 0, /* Number of passing tests */
139 FailCount
= 0, /* Number of failing tests */
140 SkipCount
= 0; /* Number of skipped tests */
141 char *Password
= NULL
; /* Password from URI */
142 const char * const URIStatusStrings
[] = /* URI status strings */
145 "Bad arguments to function",
146 "Bad resource in URI",
147 "Bad port number in URI",
148 "Bad hostname/address in URI",
149 "Bad username in URI",
153 "Missing scheme in URI",
154 "Unknown scheme in URI",
155 "Missing resource in URI"
163 static void add_stringf(cups_array_t
*a
, const char *s
, ...)
164 __attribute__ ((__format__ (__printf__
, 2, 3)));
165 static int compare_vars(_cups_var_t
*a
, _cups_var_t
*b
);
166 static int do_tests(_cups_vars_t
*vars
, const char *testfile
);
167 static void expand_variables(_cups_vars_t
*vars
, char *dst
, const char *src
,
168 size_t dstsize
) __attribute__((nonnull(1,2,3)));
169 static int expect_matches(_cups_expect_t
*expect
, ipp_tag_t value_tag
);
170 static ipp_t
*get_collection(_cups_vars_t
*vars
, FILE *fp
, int *linenum
);
171 static char *get_filename(const char *testfile
, char *dst
, const char *src
,
173 static char *get_string(ipp_attribute_t
*attr
, int element
, int flags
,
174 char *buffer
, size_t bufsize
);
175 static char *get_token(FILE *fp
, char *buf
, int buflen
,
177 static char *get_variable(_cups_vars_t
*vars
, const char *name
);
178 static char *iso_date(ipp_uchar_t
*date
);
179 static const char *password_cb(const char *prompt
);
180 static void print_attr(ipp_attribute_t
*attr
, ipp_tag_t
*group
);
181 static void print_col(ipp_t
*col
);
182 static void print_csv(ipp_attribute_t
*attr
, int num_displayed
,
183 char **displayed
, size_t *widths
);
184 static void print_fatal_error(const char *s
, ...)
185 __attribute__ ((__format__ (__printf__
, 1, 2)));
186 static void print_line(ipp_attribute_t
*attr
, int num_displayed
,
187 char **displayed
, size_t *widths
);
188 static void print_xml_header(void);
189 static void print_xml_string(const char *element
, const char *s
);
190 static void print_xml_trailer(int success
, const char *message
);
191 static void set_variable(_cups_vars_t
*vars
, const char *name
,
194 static void sigterm_handler(int sig
);
196 static int timeout_cb(http_t
*http
, void *user_data
);
197 static void usage(void) __attribute__((noreturn
));
198 static int validate_attr(cups_array_t
*errors
, ipp_attribute_t
*attr
);
199 static int with_value(cups_array_t
*errors
, char *value
, int flags
,
200 ipp_attribute_t
*attr
, char *matchbuf
,
205 * 'main()' - Parse options and do tests.
208 int /* O - Exit status */
209 main(int argc
, /* I - Number of command-line args */
210 char *argv
[]) /* I - Command-line arguments */
212 int i
; /* Looping var */
213 int status
; /* Status of tests... */
214 char *opt
, /* Current option */
215 name
[1024], /* Name/value buffer */
216 *value
, /* Pointer to value */
217 filename
[1024], /* Real filename */
218 testname
[1024], /* Real test filename */
219 uri
[1024]; /* Copy of printer URI */
220 const char *ext
, /* Extension on filename */
221 *testfile
; /* Test file to use */
222 int interval
, /* Test interval in microseconds */
223 repeat
; /* Repeat count */
224 _cups_vars_t vars
; /* Variables */
225 http_uri_status_t uri_status
; /* URI separation status */
226 _cups_globals_t
*cg
= _cupsGlobals();
232 * Catch SIGINT and SIGTERM...
235 signal(SIGINT
, sigterm_handler
);
236 signal(SIGTERM
, sigterm_handler
);
240 * Initialize the locale and variables...
243 _cupsSetLocale(argv
);
245 memset(&vars
, 0, sizeof(vars
));
246 vars
.family
= AF_UNSPEC
;
247 vars
.vars
= cupsArrayNew((cups_array_func_t
)compare_vars
, NULL
);
252 * ipptool URI testfile
260 for (i
= 1; i
< argc
; i
++)
262 if (!strcmp(argv
[i
], "--help"))
266 else if (!strcmp(argv
[i
], "--stop-after-include-error"))
268 StopAfterIncludeError
= 1;
270 else if (!strcmp(argv
[i
], "--version"))
275 else if (argv
[i
][0] == '-')
277 for (opt
= argv
[i
] + 1; *opt
; opt
++)
281 case '4' : /* Connect using IPv4 only */
282 vars
.family
= AF_INET
;
286 case '6' : /* Connect using IPv6 only */
287 vars
.family
= AF_INET6
;
289 #endif /* AF_INET6 */
291 case 'C' : /* Enable HTTP chunking */
292 Transfer
= _CUPS_TRANSFER_CHUNKED
;
295 case 'E' : /* Encrypt with TLS */
297 vars
.encryption
= HTTP_ENCRYPT_REQUIRED
;
299 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
301 #endif /* HAVE_SSL */
304 case 'I' : /* Ignore errors */
308 case 'L' : /* Disable HTTP chunking */
309 Transfer
= _CUPS_TRANSFER_LENGTH
;
312 case 'S' : /* Encrypt with SSL */
314 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
316 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
318 #endif /* HAVE_SSL */
321 case 'T' : /* Set timeout */
326 _cupsLangPrintf(stderr
,
327 _("%s: Missing timeout for \"-T\"."),
332 vars
.timeout
= _cupsStrScand(argv
[i
], NULL
, localeconv());
335 case 'V' : /* Set IPP version */
340 _cupsLangPrintf(stderr
,
341 _("%s: Missing version for \"-V\"."),
346 if (!strcmp(argv
[i
], "1.0"))
348 else if (!strcmp(argv
[i
], "1.1"))
350 else if (!strcmp(argv
[i
], "2.0"))
352 else if (!strcmp(argv
[i
], "2.1"))
354 else if (!strcmp(argv
[i
], "2.2"))
358 _cupsLangPrintf(stderr
,
359 _("%s: Bad version %s for \"-V\"."),
365 case 'X' : /* Produce XML output */
366 Output
= _CUPS_OUTPUT_PLIST
;
368 if (interval
|| repeat
)
370 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are "
371 "incompatible with -X\"."));
376 case 'c' : /* CSV output */
377 Output
= _CUPS_OUTPUT_CSV
;
380 case 'd' : /* Define a variable */
385 _cupsLangPuts(stderr
,
386 _("ipptool: Missing name=value for \"-d\"."));
390 strlcpy(name
, argv
[i
], sizeof(name
));
391 if ((value
= strchr(name
, '=')) != NULL
)
394 value
= name
+ strlen(name
);
396 set_variable(&vars
, name
, value
);
399 case 'f' : /* Set the default test filename */
404 _cupsLangPuts(stderr
,
405 _("ipptool: Missing filename for \"-f\"."));
412 vars
.filename
= NULL
;
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
]);
437 vars
.filename
= strdup(filename
);
440 vars
.filename
= strdup(filename
);
443 vars
.filename
= strdup(filename
);
446 vars
.filename
= strdup(argv
[i
]);
448 if ((ext
= strrchr(vars
.filename
, '.')) != NULL
)
451 * Guess the MIME media type based on the extension...
454 if (!_cups_strcasecmp(ext
, ".gif"))
455 set_variable(&vars
, "filetype", "image/gif");
456 else if (!_cups_strcasecmp(ext
, ".htm") ||
457 !_cups_strcasecmp(ext
, ".htm.gz") ||
458 !_cups_strcasecmp(ext
, ".html") ||
459 !_cups_strcasecmp(ext
, ".html.gz"))
460 set_variable(&vars
, "filetype", "text/html");
461 else if (!_cups_strcasecmp(ext
, ".jpg"))
462 set_variable(&vars
, "filetype", "image/jpeg");
463 else if (!_cups_strcasecmp(ext
, ".pcl") ||
464 !_cups_strcasecmp(ext
, ".pcl.gz"))
465 set_variable(&vars
, "filetype", "application/vnd.hp-PCL");
466 else if (!_cups_strcasecmp(ext
, ".pdf"))
467 set_variable(&vars
, "filetype", "application/pdf");
468 else if (!_cups_strcasecmp(ext
, ".png"))
469 set_variable(&vars
, "filetype", "image/png");
470 else if (!_cups_strcasecmp(ext
, ".ps") ||
471 !_cups_strcasecmp(ext
, ".ps.gz"))
472 set_variable(&vars
, "filetype", "application/postscript");
473 else if (!_cups_strcasecmp(ext
, ".pwg") ||
474 !_cups_strcasecmp(ext
, ".pwg.gz") ||
475 !_cups_strcasecmp(ext
, ".ras") ||
476 !_cups_strcasecmp(ext
, ".ras.gz"))
477 set_variable(&vars
, "filetype", "image/pwg-raster");
478 else if (!_cups_strcasecmp(ext
, ".txt") ||
479 !_cups_strcasecmp(ext
, ".txt.gz"))
480 set_variable(&vars
, "filetype", "text/plain");
481 else if (!_cups_strcasecmp(ext
, ".xps"))
482 set_variable(&vars
, "filetype", "application/openxps");
484 set_variable(&vars
, "filetype", "application/octet-stream");
489 * Use the "auto-type" MIME media type...
492 set_variable(&vars
, "filetype", "application/octet-stream");
496 case 'i' : /* Test every N seconds */
501 _cupsLangPuts(stderr
,
502 _("ipptool: Missing seconds for \"-i\"."));
507 interval
= (int)(_cupsStrScand(argv
[i
], NULL
, localeconv()) *
511 _cupsLangPuts(stderr
,
512 _("ipptool: Invalid seconds for \"-i\"."));
517 if (Output
== _CUPS_OUTPUT_PLIST
&& interval
)
519 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are "
520 "incompatible with -X\"."));
525 case 'l' : /* List as a table */
526 Output
= _CUPS_OUTPUT_LIST
;
529 case 'n' : /* Repeat count */
534 _cupsLangPuts(stderr
,
535 _("ipptool: Missing count for \"-n\"."));
539 repeat
= atoi(argv
[i
]);
541 if (Output
== _CUPS_OUTPUT_PLIST
&& repeat
)
543 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are "
544 "incompatible with -X\"."));
549 case 'q' : /* Be quiet */
550 Output
= _CUPS_OUTPUT_QUIET
;
553 case 't' : /* CUPS test output */
554 Output
= _CUPS_OUTPUT_TEST
;
557 case 'v' : /* Be verbose */
562 _cupsLangPrintf(stderr
, _("ipptool: Unknown option \"-%c\"."),
569 else if (!strncmp(argv
[i
], "ipp://", 6) || !strncmp(argv
[i
], "http://", 7)
571 || !strncmp(argv
[i
], "ipps://", 7)
572 || !strncmp(argv
[i
], "https://", 8)
573 #endif /* HAVE_SSL */
582 _cupsLangPuts(stderr
, _("ipptool: May only specify a single URI."));
587 if (!strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8))
588 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
589 #endif /* HAVE_SSL */
592 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, vars
.uri
,
593 vars
.scheme
, sizeof(vars
.scheme
),
594 vars
.userpass
, sizeof(vars
.userpass
),
595 vars
.hostname
, sizeof(vars
.hostname
),
597 vars
.resource
, sizeof(vars
.resource
));
599 if (uri_status
!= HTTP_URI_OK
)
601 _cupsLangPrintf(stderr
, _("ipptool: Bad URI - %s."),
602 URIStatusStrings
[uri_status
- HTTP_URI_OVERFLOW
]);
606 if (vars
.userpass
[0])
608 if ((Password
= strchr(vars
.userpass
, ':')) != NULL
)
611 cupsSetUser(vars
.userpass
);
612 cupsSetPasswordCB(password_cb
);
613 set_variable(&vars
, "uriuser", vars
.userpass
);
616 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), vars
.scheme
, NULL
,
617 vars
.hostname
, vars
.port
, vars
.resource
);
628 _cupsLangPuts(stderr
, _("ipptool: URI required before test file."));
632 if (access(argv
[i
], 0) && argv
[i
][0] != '/'
634 && (!isalpha(argv
[i
][0] & 255) || argv
[i
][1] != ':')
638 snprintf(testname
, sizeof(testname
), "%s/ipptool/%s", cg
->cups_datadir
,
640 if (access(testname
, 0))
648 if (!do_tests(&vars
, testfile
))
653 if (!vars
.uri
|| !testfile
)
657 * Loop if the interval is set...
660 if (Output
== _CUPS_OUTPUT_PLIST
)
661 print_xml_trailer(!status
, NULL
);
662 else if (interval
> 0 && repeat
> 0)
667 do_tests(&vars
, testfile
);
671 else if (interval
> 0)
676 do_tests(&vars
, testfile
);
679 else if (Output
== _CUPS_OUTPUT_TEST
&& TestCount
> 1)
682 * Show a summary report if there were multiple tests...
685 printf("\nSummary: %d tests, %d passed, %d failed, %d skipped\n"
686 "Score: %d%%\n", TestCount
, PassCount
, FailCount
, SkipCount
,
687 100 * (PassCount
+ SkipCount
) / TestCount
);
699 * 'add_stringf()' - Add a formatted string to an array.
703 add_stringf(cups_array_t
*a
, /* I - Array */
704 const char *s
, /* I - Printf-style format string */
705 ...) /* I - Additional args as needed */
707 char buffer
[10240]; /* Format buffer */
708 va_list ap
; /* Argument pointer */
712 * Don't bother is the array is NULL...
719 * Format the message...
723 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
727 * Add it to the array...
730 cupsArrayAdd(a
, buffer
);
735 * 'compare_vars()' - Compare two variables.
738 static int /* O - Result of comparison */
739 compare_vars(_cups_var_t
*a
, /* I - First variable */
740 _cups_var_t
*b
) /* I - Second variable */
742 return (_cups_strcasecmp(a
->name
, b
->name
));
747 * 'do_tests()' - Do tests as specified in the test file.
750 static int /* 1 = success, 0 = failure */
751 do_tests(_cups_vars_t
*vars
, /* I - Variables */
752 const char *testfile
) /* I - Test file to use */
754 int i
, /* Looping var */
755 linenum
, /* Current line number */
756 pass
, /* Did we pass the test? */
757 prev_pass
= 1, /* Did we pass the previous test? */
758 request_id
, /* Current request ID */
759 show_header
= 1, /* Show the test header? */
760 ignore_errors
, /* Ignore test failures? */
761 skip_previous
= 0, /* Skip on previous test failure? */
762 repeat_count
, /* Repeat count */
763 repeat_interval
, /* Repeat interval */
764 repeat_prev
, /* Previous repeat interval */
765 repeat_test
; /* Repeat a test? */
766 http_t
*http
= NULL
; /* HTTP connection to server */
767 FILE *fp
= NULL
; /* Test file */
768 char resource
[512], /* Resource for request */
769 token
[1024], /* Token from file */
770 *tokenptr
, /* Pointer into token */
771 temp
[1024], /* Temporary string */
772 buffer
[8192], /* Copy buffer */
773 compression
[16]; /* COMPRESSION value */
774 ipp_t
*request
= NULL
, /* IPP request */
775 *response
= NULL
; /* IPP response */
776 size_t length
; /* Length of IPP request */
777 http_status_t status
; /* HTTP status */
778 cups_file_t
*reqfile
; /* File to send */
779 ssize_t bytes
; /* Bytes read/written */
780 char attr
[128]; /* Attribute name */
781 ipp_op_t op
; /* Operation */
782 ipp_tag_t group
; /* Current group */
783 ipp_tag_t value
; /* Current value type */
784 ipp_attribute_t
*attrptr
, /* Attribute pointer */
785 *found
, /* Found attribute */
786 *lastcol
= NULL
; /* Last collection attribute */
787 char name
[1024], /* Name of test */
788 file_id
[1024], /* File identifier */
789 test_id
[1024]; /* Test identifier */
790 char filename
[1024]; /* Filename */
791 _cups_transfer_t transfer
; /* To chunk or not to chunk */
792 int version
, /* IPP version number to use */
793 skip_test
; /* Skip this test? */
794 int num_statuses
= 0; /* Number of valid status codes */
795 _cups_status_t statuses
[100], /* Valid status codes */
796 *last_status
; /* Last STATUS (for predicates) */
797 int num_expects
= 0; /* Number of expected attributes */
798 _cups_expect_t expects
[200], /* Expected attributes */
799 *expect
, /* Current expected attribute */
800 *last_expect
; /* Last EXPECT (for predicates) */
801 int num_displayed
= 0; /* Number of displayed attributes */
802 char *displayed
[200]; /* Displayed attributes */
803 size_t widths
[200]; /* Width of columns */
804 cups_array_t
*a
, /* Duplicate attribute array */
805 *errors
= NULL
; /* Errors array */
806 const char *error
; /* Current error */
810 * Open the test file...
813 if ((fp
= fopen(testfile
, "r")) == NULL
)
815 print_fatal_error("Unable to open test file %s - %s", testfile
,
822 * Connect to the server...
825 if ((http
= httpConnect2(vars
->hostname
, vars
->port
, NULL
, vars
->family
,
826 vars
->encryption
, 1, 30000, NULL
)) == NULL
)
828 print_fatal_error("Unable to connect to %s on port %d - %s", vars
->hostname
,
829 vars
->port
, cupsLastErrorString());
835 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
,
836 "deflate, gzip, identity");
838 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
, "identity");
839 #endif /* HAVE_LIBZ */
841 if (vars
->timeout
> 0.0)
842 httpSetTimeout(http
, vars
->timeout
, timeout_cb
, NULL
);
848 CUPS_SRAND(time(NULL
));
850 errors
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
,
851 (cups_afree_func_t
)free
);
855 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
857 while (!Cancel
&& get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
860 * Expect an open brace...
863 if (!strcmp(token
, "DEFINE"))
869 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
870 get_token(fp
, temp
, sizeof(temp
), &linenum
))
872 expand_variables(vars
, token
, temp
, sizeof(token
));
873 set_variable(vars
, attr
, token
);
877 print_fatal_error("Missing DEFINE name and/or value on line %d.",
885 else if (!strcmp(token
, "DEFINE-DEFAULT"))
888 * DEFINE-DEFAULT name value
891 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
892 get_token(fp
, temp
, sizeof(temp
), &linenum
))
894 expand_variables(vars
, token
, temp
, sizeof(token
));
895 if (!get_variable(vars
, attr
))
896 set_variable(vars
, attr
, token
);
900 print_fatal_error("Missing DEFINE-DEFAULT name and/or value on line "
908 else if (!strcmp(token
, "FILE-ID"))
914 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
916 expand_variables(vars
, file_id
, temp
, sizeof(file_id
));
920 print_fatal_error("Missing FILE-ID value on line %d.", linenum
);
927 else if (!strcmp(token
, "IGNORE-ERRORS"))
934 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
935 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
937 IgnoreErrors
= !_cups_strcasecmp(temp
, "yes");
941 print_fatal_error("Missing IGNORE-ERRORS value on line %d.", linenum
);
948 else if (!strcmp(token
, "INCLUDE"))
955 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
958 * Map the filename to and then run the tests...
961 if (!do_tests(vars
, get_filename(testfile
, filename
, temp
,
966 if (StopAfterIncludeError
)
972 print_fatal_error("Missing INCLUDE filename on line %d.", linenum
);
980 else if (!strcmp(token
, "INCLUDE-IF-DEFINED"))
983 * INCLUDE-IF-DEFINED name "filename"
984 * INCLUDE-IF-DEFINED name <filename>
987 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
988 get_token(fp
, temp
, sizeof(temp
), &linenum
))
991 * Map the filename to and then run the tests...
994 if (get_variable(vars
, attr
) &&
995 !do_tests(vars
, get_filename(testfile
, filename
, temp
,
1000 if (StopAfterIncludeError
)
1006 print_fatal_error("Missing INCLUDE-IF-DEFINED name or filename on line "
1015 else if (!strcmp(token
, "INCLUDE-IF-NOT-DEFINED"))
1018 * INCLUDE-IF-NOT-DEFINED name "filename"
1019 * INCLUDE-IF-NOT-DEFINED name <filename>
1022 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1023 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1026 * Map the filename to and then run the tests...
1029 if (!get_variable(vars
, attr
) &&
1030 !do_tests(vars
, get_filename(testfile
, filename
, temp
,
1035 if (StopAfterIncludeError
)
1041 print_fatal_error("Missing INCLUDE-IF-NOT-DEFINED name or filename on "
1042 "line %d.", linenum
);
1050 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1053 * SKIP-IF-DEFINED variable
1056 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1058 if (get_variable(vars
, temp
))
1063 print_fatal_error("Missing SKIP-IF-DEFINED variable on line %d.",
1069 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1072 * SKIP-IF-NOT-DEFINED variable
1075 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1077 if (!get_variable(vars
, temp
))
1082 print_fatal_error("Missing SKIP-IF-NOT-DEFINED variable on line %d.",
1088 else if (!strcmp(token
, "STOP-AFTER-INCLUDE-ERROR"))
1091 * STOP-AFTER-INCLUDE-ERROR yes
1092 * STOP-AFTER-INCLUDE-ERROR no
1095 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1096 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1098 StopAfterIncludeError
= !_cups_strcasecmp(temp
, "yes");
1102 print_fatal_error("Missing STOP-AFTER-INCLUDE-ERROR value on line %d.",
1110 else if (!strcmp(token
, "TRANSFER"))
1118 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1120 if (!strcmp(temp
, "auto"))
1121 Transfer
= _CUPS_TRANSFER_AUTO
;
1122 else if (!strcmp(temp
, "chunked"))
1123 Transfer
= _CUPS_TRANSFER_CHUNKED
;
1124 else if (!strcmp(temp
, "length"))
1125 Transfer
= _CUPS_TRANSFER_LENGTH
;
1128 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp
,
1136 print_fatal_error("Missing TRANSFER value on line %d.", linenum
);
1143 else if (!strcmp(token
, "VERSION"))
1145 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1147 if (!strcmp(temp
, "1.0"))
1149 else if (!strcmp(temp
, "1.1"))
1151 else if (!strcmp(temp
, "2.0"))
1153 else if (!strcmp(temp
, "2.1"))
1155 else if (!strcmp(temp
, "2.2"))
1159 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1166 print_fatal_error("Missing VERSION number on line %d.", linenum
);
1173 else if (strcmp(token
, "{"))
1175 print_fatal_error("Unexpected token %s seen on line %d.", token
, linenum
);
1181 * Initialize things...
1186 if (Output
== _CUPS_OUTPUT_PLIST
)
1188 else if (Output
== _CUPS_OUTPUT_TEST
)
1189 printf("\"%s\":\n", testfile
);
1194 strlcpy(resource
, vars
->resource
, sizeof(resource
));
1199 group
= IPP_TAG_ZERO
;
1200 ignore_errors
= IgnoreErrors
;
1208 transfer
= Transfer
;
1209 compression
[0] = '\0';
1211 strlcpy(name
, testfile
, sizeof(name
));
1212 if (strrchr(name
, '.') != NULL
)
1213 *strrchr(name
, '.') = '\0';
1216 * Parse until we see a close brace...
1219 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
1221 if (_cups_strcasecmp(token
, "COUNT") &&
1222 _cups_strcasecmp(token
, "DEFINE-MATCH") &&
1223 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1224 _cups_strcasecmp(token
, "DEFINE-VALUE") &&
1225 _cups_strcasecmp(token
, "IF-DEFINED") &&
1226 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1227 _cups_strcasecmp(token
, "IN-GROUP") &&
1228 _cups_strcasecmp(token
, "OF-TYPE") &&
1229 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1230 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1231 _cups_strcasecmp(token
, "REPEAT-NO-MATCH") &&
1232 _cups_strcasecmp(token
, "SAME-COUNT-AS") &&
1233 _cups_strcasecmp(token
, "WITH-ALL-VALUES") &&
1234 _cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") &&
1235 _cups_strcasecmp(token
, "WITH-ALL-RESOURCES") &&
1236 _cups_strcasecmp(token
, "WITH-ALL-SCHEMES") &&
1237 _cups_strcasecmp(token
, "WITH-HOSTNAME") &&
1238 _cups_strcasecmp(token
, "WITH-RESOURCE") &&
1239 _cups_strcasecmp(token
, "WITH-SCHEME") &&
1240 _cups_strcasecmp(token
, "WITH-VALUE"))
1243 if (_cups_strcasecmp(token
, "DEFINE-MATCH") &&
1244 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1245 _cups_strcasecmp(token
, "IF-DEFINED") &&
1246 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1247 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1248 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1249 _cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
1252 if (!strcmp(token
, "}"))
1254 else if (!strcmp(token
, "{") && lastcol
)
1257 * Another collection value
1260 ipp_t
*col
= get_collection(vars
, fp
, &linenum
);
1261 /* Collection value */
1265 ippSetCollection(request
, &lastcol
, ippGetCount(lastcol
), col
);
1273 else if (!strcmp(token
, "COMPRESSION"))
1277 * COMPRESSION deflate
1281 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1283 expand_variables(vars
, compression
, temp
, sizeof(compression
));
1285 if (strcmp(compression
, "none") && strcmp(compression
, "deflate") &&
1286 strcmp(compression
, "gzip"))
1288 if (strcmp(compression
, "none"))
1289 #endif /* HAVE_LIBZ */
1291 print_fatal_error("Unsupported COMPRESSION value '%s' on line %d.",
1292 compression
, linenum
);
1297 if (!strcmp(compression
, "none"))
1298 compression
[0] = '\0';
1302 print_fatal_error("Missing COMPRESSION value on line %d.", linenum
);
1307 else if (!strcmp(token
, "DEFINE"))
1313 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1314 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1316 expand_variables(vars
, token
, temp
, sizeof(token
));
1317 set_variable(vars
, attr
, token
);
1321 print_fatal_error("Missing DEFINE name and/or value on line %d.",
1327 else if (!strcmp(token
, "IGNORE-ERRORS"))
1334 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1335 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1337 ignore_errors
= !_cups_strcasecmp(temp
, "yes");
1341 print_fatal_error("Missing IGNORE-ERRORS value on line %d.", linenum
);
1348 else if (!_cups_strcasecmp(token
, "NAME"))
1354 get_token(fp
, name
, sizeof(name
), &linenum
);
1356 else if (!strcmp(token
, "REQUEST-ID"))
1363 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1365 if (isdigit(temp
[0] & 255))
1366 request_id
= atoi(temp
);
1367 else if (!_cups_strcasecmp(temp
, "random"))
1368 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
1371 print_fatal_error("Bad REQUEST-ID value \"%s\" on line %d.", temp
,
1379 print_fatal_error("Missing REQUEST-ID value on line %d.", linenum
);
1384 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1387 * SKIP-IF-DEFINED variable
1390 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1392 if (get_variable(vars
, temp
))
1397 print_fatal_error("Missing SKIP-IF-DEFINED value on line %d.",
1403 else if (!strcmp(token
, "SKIP-IF-MISSING"))
1406 * SKIP-IF-MISSING filename
1409 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1411 expand_variables(vars
, token
, temp
, sizeof(token
));
1412 get_filename(testfile
, filename
, token
, sizeof(filename
));
1414 if (access(filename
, R_OK
))
1419 print_fatal_error("Missing SKIP-IF-MISSING filename on line %d.",
1425 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1428 * SKIP-IF-NOT-DEFINED variable
1431 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1433 if (!get_variable(vars
, temp
))
1438 print_fatal_error("Missing SKIP-IF-NOT-DEFINED value on line %d.",
1444 else if (!strcmp(token
, "SKIP-PREVIOUS-ERROR"))
1447 * SKIP-PREVIOUS-ERROR yes
1448 * SKIP-PREVIOUS-ERROR no
1451 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1452 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1454 skip_previous
= !_cups_strcasecmp(temp
, "yes");
1458 print_fatal_error("Missing SKIP-PREVIOUS-ERROR value on line %d.", linenum
);
1465 else if (!strcmp(token
, "TEST-ID"))
1471 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1473 expand_variables(vars
, test_id
, temp
, sizeof(test_id
));
1477 print_fatal_error("Missing TEST-ID value on line %d.", linenum
);
1484 else if (!strcmp(token
, "TRANSFER"))
1492 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1494 if (!strcmp(temp
, "auto"))
1495 transfer
= _CUPS_TRANSFER_AUTO
;
1496 else if (!strcmp(temp
, "chunked"))
1497 transfer
= _CUPS_TRANSFER_CHUNKED
;
1498 else if (!strcmp(temp
, "length"))
1499 transfer
= _CUPS_TRANSFER_LENGTH
;
1502 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp
,
1510 print_fatal_error("Missing TRANSFER value on line %d.", linenum
);
1515 else if (!_cups_strcasecmp(token
, "VERSION"))
1517 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1519 if (!strcmp(temp
, "0.0"))
1521 else if (!strcmp(temp
, "1.0"))
1523 else if (!strcmp(temp
, "1.1"))
1525 else if (!strcmp(temp
, "2.0"))
1527 else if (!strcmp(temp
, "2.1"))
1529 else if (!strcmp(temp
, "2.2"))
1533 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1540 print_fatal_error("Missing VERSION number on line %d.", linenum
);
1545 else if (!_cups_strcasecmp(token
, "RESOURCE"))
1551 if (!get_token(fp
, resource
, sizeof(resource
), &linenum
))
1553 print_fatal_error("Missing RESOURCE path on line %d.", linenum
);
1558 else if (!_cups_strcasecmp(token
, "OPERATION"))
1564 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1566 print_fatal_error("Missing OPERATION code on line %d.", linenum
);
1571 expand_variables(vars
, token
, temp
, sizeof(token
));
1573 if ((op
= ippOpValue(token
)) == (ipp_op_t
)-1 &&
1574 (op
= strtol(token
, NULL
, 0)) == 0)
1576 print_fatal_error("Bad OPERATION code \"%s\" on line %d.", token
,
1582 else if (!_cups_strcasecmp(token
, "GROUP"))
1585 * Attribute group...
1588 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1590 print_fatal_error("Missing GROUP tag on line %d.", linenum
);
1595 if ((value
= ippTagValue(token
)) < 0)
1597 print_fatal_error("Bad GROUP tag \"%s\" on line %d.", token
, linenum
);
1603 ippAddSeparator(request
);
1607 else if (!_cups_strcasecmp(token
, "DELAY"))
1610 * Delay before operation...
1615 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1617 print_fatal_error("Missing DELAY value on line %d.", linenum
);
1622 expand_variables(vars
, token
, temp
, sizeof(token
));
1624 if ((delay
= _cupsStrScand(token
, NULL
, localeconv())) <= 0.0)
1626 print_fatal_error("Bad DELAY value \"%s\" on line %d.", token
,
1633 if (Output
== _CUPS_OUTPUT_TEST
)
1634 printf(" [%g second delay]\n", delay
);
1636 usleep((int)(1000000.0 * delay
));
1639 else if (!_cups_strcasecmp(token
, "ATTR"))
1645 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1647 print_fatal_error("Missing ATTR value tag on line %d.", linenum
);
1652 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
1654 print_fatal_error("Bad ATTR value tag \"%s\" on line %d.", token
,
1660 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
1662 print_fatal_error("Missing ATTR name on line %d.", linenum
);
1667 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1669 print_fatal_error("Missing ATTR value on line %d.", linenum
);
1674 expand_variables(vars
, token
, temp
, sizeof(token
));
1679 case IPP_TAG_BOOLEAN
:
1680 if (!_cups_strcasecmp(token
, "true"))
1681 attrptr
= ippAddBoolean(request
, group
, attr
, 1);
1683 attrptr
= ippAddBoolean(request
, group
, attr
, atoi(token
));
1686 case IPP_TAG_INTEGER
:
1688 if (!strchr(token
, ','))
1689 attrptr
= ippAddInteger(request
, group
, value
, attr
,
1690 strtol(token
, &tokenptr
, 0));
1693 int values
[100], /* Values */
1694 num_values
= 1; /* Number of values */
1696 values
[0] = strtol(token
, &tokenptr
, 10);
1697 while (tokenptr
&& *tokenptr
&&
1698 num_values
< (int)(sizeof(values
) / sizeof(values
[0])))
1700 if (*tokenptr
== ',')
1702 else if (!isdigit(*tokenptr
& 255) && *tokenptr
!= '-')
1705 values
[num_values
] = strtol(tokenptr
, &tokenptr
, 0);
1709 attrptr
= ippAddIntegers(request
, group
, value
, attr
, num_values
, values
);
1712 if (!tokenptr
|| *tokenptr
)
1714 print_fatal_error("Bad %s value \"%s\" on line %d.",
1715 ippTagString(value
), token
, linenum
);
1721 case IPP_TAG_RESOLUTION
:
1723 int xres
, /* X resolution */
1724 yres
; /* Y resolution */
1725 char *ptr
; /* Pointer into value */
1727 xres
= yres
= strtol(token
, (char **)&ptr
, 10);
1728 if (ptr
> token
&& xres
> 0)
1731 yres
= strtol(ptr
+ 1, (char **)&ptr
, 10);
1734 if (ptr
<= token
|| xres
<= 0 || yres
<= 0 || !ptr
||
1735 (_cups_strcasecmp(ptr
, "dpi") &&
1736 _cups_strcasecmp(ptr
, "dpc") &&
1737 _cups_strcasecmp(ptr
, "dpcm") &&
1738 _cups_strcasecmp(ptr
, "other")))
1740 print_fatal_error("Bad resolution value \"%s\" on line %d.",
1746 if (!_cups_strcasecmp(ptr
, "dpi"))
1747 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_INCH
,
1749 else if (!_cups_strcasecmp(ptr
, "dpc") ||
1750 !_cups_strcasecmp(ptr
, "dpcm"))
1751 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_CM
,
1754 attrptr
= ippAddResolution(request
, group
, attr
, (ipp_res_t
)0,
1759 case IPP_TAG_RANGE
:
1761 int lowers
[4], /* Lower value */
1762 uppers
[4], /* Upper values */
1763 num_vals
; /* Number of values */
1766 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
1767 lowers
+ 0, uppers
+ 0,
1768 lowers
+ 1, uppers
+ 1,
1769 lowers
+ 2, uppers
+ 2,
1770 lowers
+ 3, uppers
+ 3);
1772 if ((num_vals
& 1) || num_vals
== 0)
1774 print_fatal_error("Bad rangeOfInteger value \"%s\" on line "
1775 "%d.", token
, linenum
);
1780 attrptr
= ippAddRanges(request
, group
, attr
, num_vals
/ 2, lowers
,
1785 case IPP_TAG_BEGIN_COLLECTION
:
1786 if (!strcmp(token
, "{"))
1788 ipp_t
*col
= get_collection(vars
, fp
, &linenum
);
1789 /* Collection value */
1793 attrptr
= lastcol
= ippAddCollection(request
, group
, attr
, col
);
1804 print_fatal_error("Bad ATTR collection value on line %d.",
1811 case IPP_TAG_STRING
:
1812 attrptr
= ippAddOctetString(request
, group
, attr
, token
,
1817 print_fatal_error("Unsupported ATTR value tag %s on line %d.",
1818 ippTagString(value
), linenum
);
1822 case IPP_TAG_TEXTLANG
:
1823 case IPP_TAG_NAMELANG
:
1826 case IPP_TAG_KEYWORD
:
1828 case IPP_TAG_URISCHEME
:
1829 case IPP_TAG_CHARSET
:
1830 case IPP_TAG_LANGUAGE
:
1831 case IPP_TAG_MIMETYPE
:
1832 if (!strchr(token
, ','))
1833 attrptr
= ippAddString(request
, group
, value
, attr
, NULL
, token
);
1837 * Multiple string values...
1840 int num_values
; /* Number of values */
1841 char *values
[100], /* Values */
1842 *ptr
; /* Pointer to next value */
1848 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
1850 if (ptr
> token
&& ptr
[-1] == '\\')
1851 _cups_strcpy(ptr
- 1, ptr
);
1855 values
[num_values
] = ptr
;
1860 attrptr
= ippAddStrings(request
, group
, value
, attr
, num_values
,
1861 NULL
, (const char **)values
);
1868 print_fatal_error("Unable to add attribute on line %d: %s", linenum
,
1869 cupsLastErrorString());
1874 else if (!_cups_strcasecmp(token
, "FILE"))
1880 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1882 print_fatal_error("Missing FILE filename on line %d.", linenum
);
1887 expand_variables(vars
, token
, temp
, sizeof(token
));
1888 get_filename(testfile
, filename
, token
, sizeof(filename
));
1890 if (access(filename
, R_OK
))
1892 print_fatal_error("Filename \"%s\" on line %d cannot be read.",
1894 print_fatal_error("Filename mapped to \"%s\".", filename
);
1899 else if (!_cups_strcasecmp(token
, "STATUS"))
1905 if (num_statuses
>= (int)(sizeof(statuses
) / sizeof(statuses
[0])))
1907 print_fatal_error("Too many STATUS's on line %d.", linenum
);
1912 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1914 print_fatal_error("Missing STATUS code on line %d.", linenum
);
1919 if ((statuses
[num_statuses
].status
= ippErrorValue(token
))
1920 == (ipp_status_t
)-1 &&
1921 (statuses
[num_statuses
].status
= strtol(token
, NULL
, 0)) == 0)
1923 print_fatal_error("Bad STATUS code \"%s\" on line %d.", token
,
1929 last_status
= statuses
+ num_statuses
;
1932 last_status
->define_match
= NULL
;
1933 last_status
->define_no_match
= NULL
;
1934 last_status
->if_defined
= NULL
;
1935 last_status
->if_not_defined
= NULL
;
1936 last_status
->repeat_limit
= 1000;
1937 last_status
->repeat_match
= 0;
1938 last_status
->repeat_no_match
= 0;
1940 else if (!_cups_strcasecmp(token
, "EXPECT"))
1943 * Expected attributes...
1946 if (num_expects
>= (int)(sizeof(expects
) / sizeof(expects
[0])))
1948 print_fatal_error("Too many EXPECT's on line %d.", linenum
);
1953 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1955 print_fatal_error("Missing EXPECT name on line %d.", linenum
);
1960 last_expect
= expects
+ num_expects
;
1963 memset(last_expect
, 0, sizeof(_cups_expect_t
));
1964 last_expect
->repeat_limit
= 1000;
1966 if (token
[0] == '!')
1968 last_expect
->not_expect
= 1;
1969 last_expect
->name
= strdup(token
+ 1);
1971 else if (token
[0] == '?')
1973 last_expect
->optional
= 1;
1974 last_expect
->name
= strdup(token
+ 1);
1977 last_expect
->name
= strdup(token
);
1979 else if (!_cups_strcasecmp(token
, "COUNT"))
1981 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1983 print_fatal_error("Missing COUNT number on line %d.", linenum
);
1988 if ((i
= atoi(token
)) <= 0)
1990 print_fatal_error("Bad COUNT \"%s\" on line %d.", token
, linenum
);
1996 last_expect
->count
= i
;
1999 print_fatal_error("COUNT without a preceding EXPECT on line %d.",
2005 else if (!_cups_strcasecmp(token
, "DEFINE-MATCH"))
2007 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2009 print_fatal_error("Missing DEFINE-MATCH variable on line %d.",
2016 last_expect
->define_match
= strdup(token
);
2017 else if (last_status
)
2018 last_status
->define_match
= strdup(token
);
2021 print_fatal_error("DEFINE-MATCH without a preceding EXPECT or STATUS "
2022 "on line %d.", linenum
);
2027 else if (!_cups_strcasecmp(token
, "DEFINE-NO-MATCH"))
2029 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2031 print_fatal_error("Missing DEFINE-NO-MATCH variable on line %d.",
2038 last_expect
->define_no_match
= strdup(token
);
2039 else if (last_status
)
2040 last_status
->define_no_match
= strdup(token
);
2043 print_fatal_error("DEFINE-NO-MATCH without a preceding EXPECT or "
2044 "STATUS on line %d.", linenum
);
2049 else if (!_cups_strcasecmp(token
, "DEFINE-VALUE"))
2051 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2053 print_fatal_error("Missing DEFINE-VALUE variable on line %d.",
2060 last_expect
->define_value
= strdup(token
);
2063 print_fatal_error("DEFINE-VALUE without a preceding EXPECT on "
2064 "line %d.", linenum
);
2069 else if (!_cups_strcasecmp(token
, "OF-TYPE"))
2071 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2073 print_fatal_error("Missing OF-TYPE value tag(s) on line %d.",
2080 last_expect
->of_type
= strdup(token
);
2083 print_fatal_error("OF-TYPE without a preceding EXPECT on line %d.",
2089 else if (!_cups_strcasecmp(token
, "IN-GROUP"))
2091 ipp_tag_t in_group
; /* IN-GROUP value */
2094 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2096 print_fatal_error("Missing IN-GROUP group tag on line %d.", linenum
);
2101 if ((in_group
= ippTagValue(token
)) == (ipp_tag_t
)-1)
2104 else if (last_expect
)
2105 last_expect
->in_group
= in_group
;
2108 print_fatal_error("IN-GROUP without a preceding EXPECT on line %d.",
2114 else if (!_cups_strcasecmp(token
, "REPEAT-LIMIT"))
2116 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2118 print_fatal_error("Missing REPEAT-LIMIT value on line %d.", linenum
);
2122 else if (atoi(token
) <= 0)
2124 print_fatal_error("Bad REPEAT-LIMIT value on line %d.", linenum
);
2130 last_status
->repeat_limit
= atoi(token
);
2131 else if (last_expect
)
2132 last_expect
->repeat_limit
= atoi(token
);
2135 print_fatal_error("REPEAT-LIMIT without a preceding EXPECT or STATUS "
2136 "on line %d.", linenum
);
2141 else if (!_cups_strcasecmp(token
, "REPEAT-MATCH"))
2144 last_status
->repeat_match
= 1;
2145 else if (last_expect
)
2146 last_expect
->repeat_match
= 1;
2149 print_fatal_error("REPEAT-MATCH without a preceding EXPECT or STATUS "
2150 "on line %d.", linenum
);
2155 else if (!_cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
2158 last_status
->repeat_no_match
= 1;
2159 else if (last_expect
)
2160 last_expect
->repeat_no_match
= 1;
2163 print_fatal_error("REPEAT-NO-MATCH without a preceding EXPECT or "
2164 "STATUS on ine %d.", linenum
);
2169 else if (!_cups_strcasecmp(token
, "SAME-COUNT-AS"))
2171 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2173 print_fatal_error("Missing SAME-COUNT-AS name on line %d.", linenum
);
2179 last_expect
->same_count_as
= strdup(token
);
2182 print_fatal_error("SAME-COUNT-AS without a preceding EXPECT on line "
2188 else if (!_cups_strcasecmp(token
, "IF-DEFINED"))
2190 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2192 print_fatal_error("Missing IF-DEFINED name on line %d.", linenum
);
2198 last_expect
->if_defined
= strdup(token
);
2199 else if (last_status
)
2200 last_status
->if_defined
= strdup(token
);
2203 print_fatal_error("IF-DEFINED without a preceding EXPECT or STATUS "
2204 "on line %d.", linenum
);
2209 else if (!_cups_strcasecmp(token
, "IF-NOT-DEFINED"))
2211 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2213 print_fatal_error("Missing IF-NOT-DEFINED name on line %d.", linenum
);
2219 last_expect
->if_not_defined
= strdup(token
);
2220 else if (last_status
)
2221 last_status
->if_not_defined
= strdup(token
);
2224 print_fatal_error("IF-NOT-DEFINED without a preceding EXPECT or STATUS "
2225 "on line %d.", linenum
);
2230 else if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") ||
2231 !_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
2232 !_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
2233 !_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
2234 !_cups_strcasecmp(token
, "WITH-HOSTNAME") ||
2235 !_cups_strcasecmp(token
, "WITH-RESOURCE") ||
2236 !_cups_strcasecmp(token
, "WITH-SCHEME") ||
2237 !_cups_strcasecmp(token
, "WITH-VALUE"))
2241 if (!_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
2242 !_cups_strcasecmp(token
, "WITH-HOSTNAME"))
2243 last_expect
->with_flags
= _CUPS_WITH_HOSTNAME
;
2244 else if (!_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
2245 !_cups_strcasecmp(token
, "WITH-RESOURCE"))
2246 last_expect
->with_flags
= _CUPS_WITH_RESOURCE
;
2247 else if (!_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
2248 !_cups_strcasecmp(token
, "WITH-SCHEME"))
2249 last_expect
->with_flags
= _CUPS_WITH_SCHEME
;
2251 if (!_cups_strncasecmp(token
, "WITH-ALL-", 9))
2252 last_expect
->with_flags
|= _CUPS_WITH_ALL
;
2255 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
2257 print_fatal_error("Missing %s value on line %d.", token
, linenum
);
2265 * Expand any variables in the value and then save it.
2268 expand_variables(vars
, token
, temp
, sizeof(token
));
2270 tokenptr
= token
+ strlen(token
) - 1;
2272 if (token
[0] == '/' && tokenptr
> token
&& *tokenptr
== '/')
2275 * WITH-VALUE is a POSIX extended regular expression.
2278 last_expect
->with_value
= calloc(1, tokenptr
- token
);
2279 last_expect
->with_flags
|= _CUPS_WITH_REGEX
;
2281 if (last_expect
->with_value
)
2282 memcpy(last_expect
->with_value
, token
+ 1, tokenptr
- token
- 1);
2287 * WITH-VALUE is a literal value...
2290 char *ptr
; /* Pointer into value */
2292 for (ptr
= token
; *ptr
; ptr
++)
2294 if (*ptr
== '\\' && ptr
[1])
2297 * Remove \ from \foo...
2300 _cups_strcpy(ptr
, ptr
+ 1);
2304 last_expect
->with_value
= strdup(token
);
2305 last_expect
->with_flags
|= _CUPS_WITH_LITERAL
;
2310 print_fatal_error("%s without a preceding EXPECT on line %d.", token
,
2316 else if (!_cups_strcasecmp(token
, "DISPLAY"))
2319 * Display attributes...
2322 if (num_displayed
>= (int)(sizeof(displayed
) / sizeof(displayed
[0])))
2324 print_fatal_error("Too many DISPLAY's on line %d", linenum
);
2329 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2331 print_fatal_error("Missing DISPLAY name on line %d.", linenum
);
2336 displayed
[num_displayed
] = strdup(token
);
2341 print_fatal_error("Unexpected token %s seen on line %d.", token
,
2349 * Submit the IPP request...
2354 request
->request
.op
.version
[0] = version
/ 10;
2355 request
->request
.op
.version
[1] = version
% 10;
2356 request
->request
.op
.operation_id
= op
;
2357 request
->request
.op
.request_id
= request_id
;
2359 if (Output
== _CUPS_OUTPUT_PLIST
)
2362 puts("<key>Name</key>");
2363 print_xml_string("string", name
);
2366 puts("<key>FileId</key>");
2367 print_xml_string("string", file_id
);
2371 puts("<key>TestId</key>");
2372 print_xml_string("string", test_id
);
2374 puts("<key>Version</key>");
2375 printf("<string>%d.%d</string>\n", version
/ 10, version
% 10);
2376 puts("<key>Operation</key>");
2377 print_xml_string("string", ippOpString(op
));
2378 puts("<key>RequestId</key>");
2379 printf("<integer>%d</integer>\n", request_id
);
2380 puts("<key>RequestAttributes</key>");
2385 for (attrptr
= request
->attrs
,
2386 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2388 attrptr
= attrptr
->next
)
2389 print_attr(attrptr
, &group
);
2394 else if (Output
== _CUPS_OUTPUT_TEST
)
2398 printf(" %s:\n", ippOpString(op
));
2400 for (attrptr
= request
->attrs
; attrptr
; attrptr
= attrptr
->next
)
2401 print_attr(attrptr
, NULL
);
2404 printf(" %-68.68s [", name
);
2408 if ((skip_previous
&& !prev_pass
) || skip_test
)
2415 if (Output
== _CUPS_OUTPUT_PLIST
)
2417 puts("<key>Successful</key>");
2419 puts("<key>StatusCode</key>");
2420 print_xml_string("string", "skip");
2421 puts("<key>ResponseAttributes</key>");
2424 else if (Output
== _CUPS_OUTPUT_TEST
)
2431 repeat_interval
= 1;
2440 if (transfer
== _CUPS_TRANSFER_CHUNKED
||
2441 (transfer
== _CUPS_TRANSFER_AUTO
&& filename
[0]))
2444 * Send request using chunking - a 0 length means "chunk".
2452 * Send request using content length...
2455 length
= ippLength(request
);
2457 if (filename
[0] && (reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2460 * Read the file to get the uncompressed file size...
2463 while ((bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
2466 cupsFileClose(reqfile
);
2471 * Send the request...
2478 if (status
!= HTTP_ERROR
)
2480 while (!response
&& !Cancel
&& prev_pass
)
2482 status
= cupsSendRequest(http
, request
, resource
, length
);
2486 httpSetField(http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
2487 #endif /* HAVE_LIBZ */
2489 if (!Cancel
&& status
== HTTP_CONTINUE
&&
2490 request
->state
== IPP_DATA
&& filename
[0])
2492 if ((reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2495 (bytes
= cupsFileRead(reqfile
, buffer
,
2496 sizeof(buffer
))) > 0)
2497 if ((status
= cupsWriteRequestData(http
, buffer
,
2498 bytes
)) != HTTP_CONTINUE
)
2501 cupsFileClose(reqfile
);
2505 snprintf(buffer
, sizeof(buffer
), "%s: %s", filename
,
2507 _cupsSetError(IPP_INTERNAL_ERROR
, buffer
, 0);
2509 status
= HTTP_ERROR
;
2514 * Get the server's response...
2517 if (!Cancel
&& status
!= HTTP_ERROR
)
2519 response
= cupsGetResponse(http
, resource
);
2520 status
= httpGetStatus(http
);
2523 if (!Cancel
&& status
== HTTP_ERROR
&& http
->error
!= EINVAL
&&
2525 http
->error
!= WSAETIMEDOUT
)
2527 http
->error
!= ETIMEDOUT
)
2530 if (httpReconnect(http
))
2533 else if (status
== HTTP_ERROR
)
2538 else if (status
!= HTTP_OK
)
2542 if (status
== HTTP_STATUS_UNAUTHORIZED
)
2550 if (!Cancel
&& status
== HTTP_ERROR
&& http
->error
!= EINVAL
&&
2552 http
->error
!= WSAETIMEDOUT
)
2554 http
->error
!= ETIMEDOUT
)
2557 if (httpReconnect(http
))
2560 else if (status
== HTTP_ERROR
)
2563 httpReconnect(http
);
2567 else if (status
!= HTTP_OK
)
2574 * Check results of request...
2577 cupsArrayClear(errors
);
2579 if (http
->version
!= HTTP_1_1
)
2580 add_stringf(errors
, "Bad HTTP version (%d.%d)", http
->version
/ 100,
2581 http
->version
% 100);
2586 * No response, log error...
2589 add_stringf(errors
, "IPP request failed with status %s (%s)",
2590 ippErrorString(cupsLastError()),
2591 cupsLastErrorString());
2596 * Collect common attribute values...
2599 if ((attrptr
= ippFindAttribute(response
, "job-id",
2600 IPP_TAG_INTEGER
)) != NULL
)
2602 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2603 set_variable(vars
, "job-id", temp
);
2606 if ((attrptr
= ippFindAttribute(response
, "job-uri",
2607 IPP_TAG_URI
)) != NULL
)
2608 set_variable(vars
, "job-uri", attrptr
->values
[0].string
.text
);
2610 if ((attrptr
= ippFindAttribute(response
, "notify-subscription-id",
2611 IPP_TAG_INTEGER
)) != NULL
)
2613 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2614 set_variable(vars
, "notify-subscription-id", temp
);
2618 * Check response, validating groups and attributes and logging errors
2622 if (response
->state
!= IPP_DATA
)
2624 "Missing end-of-attributes-tag in response "
2625 "(RFC 2910 section 3.5.1)");
2628 (response
->request
.status
.version
[0] != (version
/ 10) ||
2629 response
->request
.status
.version
[1] != (version
% 10)))
2631 "Bad version %d.%d in response - expected %d.%d "
2632 "(RFC 2911 section 3.1.8).",
2633 response
->request
.status
.version
[0],
2634 response
->request
.status
.version
[1],
2635 version
/ 10, version
% 10);
2637 if (response
->request
.status
.request_id
!= request_id
)
2639 "Bad request ID %d in response - expected %d "
2640 "(RFC 2911 section 3.1.1)",
2641 response
->request
.status
.request_id
, request_id
);
2643 attrptr
= response
->attrs
;
2646 "Missing first attribute \"attributes-charset "
2647 "(charset)\" in group operation-attributes-tag "
2648 "(RFC 2911 section 3.1.4).");
2651 if (!attrptr
->name
||
2652 attrptr
->value_tag
!= IPP_TAG_CHARSET
||
2653 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2654 attrptr
->num_values
!= 1 ||
2655 strcmp(attrptr
->name
, "attributes-charset"))
2657 "Bad first attribute \"%s (%s%s)\" in group %s, "
2658 "expected \"attributes-charset (charset)\" in "
2659 "group operation-attributes-tag (RFC 2911 section "
2661 attrptr
->name
? attrptr
->name
: "(null)",
2662 attrptr
->num_values
> 1 ? "1setOf " : "",
2663 ippTagString(attrptr
->value_tag
),
2664 ippTagString(attrptr
->group_tag
));
2666 attrptr
= attrptr
->next
;
2669 "Missing second attribute \"attributes-natural-"
2670 "language (naturalLanguage)\" in group "
2671 "operation-attributes-tag (RFC 2911 section "
2673 else if (!attrptr
->name
||
2674 attrptr
->value_tag
!= IPP_TAG_LANGUAGE
||
2675 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2676 attrptr
->num_values
!= 1 ||
2677 strcmp(attrptr
->name
, "attributes-natural-language"))
2679 "Bad first attribute \"%s (%s%s)\" in group %s, "
2680 "expected \"attributes-natural-language "
2681 "(naturalLanguage)\" in group "
2682 "operation-attributes-tag (RFC 2911 section "
2684 attrptr
->name
? attrptr
->name
: "(null)",
2685 attrptr
->num_values
> 1 ? "1setOf " : "",
2686 ippTagString(attrptr
->value_tag
),
2687 ippTagString(attrptr
->group_tag
));
2690 if ((attrptr
= ippFindAttribute(response
, "status-message",
2691 IPP_TAG_ZERO
)) != NULL
)
2693 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2695 "status-message (text(255)) has wrong value tag "
2696 "%s (RFC 2911 section 3.1.6.2).",
2697 ippTagString(attrptr
->value_tag
));
2698 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2700 "status-message (text(255)) has wrong group tag "
2701 "%s (RFC 2911 section 3.1.6.2).",
2702 ippTagString(attrptr
->group_tag
));
2703 if (attrptr
->num_values
!= 1)
2705 "status-message (text(255)) has %d values "
2706 "(RFC 2911 section 3.1.6.2).",
2707 attrptr
->num_values
);
2708 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2709 strlen(attrptr
->values
[0].string
.text
) > 255)
2711 "status-message (text(255)) has bad length %d"
2712 " (RFC 2911 section 3.1.6.2).",
2713 (int)strlen(attrptr
->values
[0].string
.text
));
2716 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
2717 IPP_TAG_ZERO
)) != NULL
)
2719 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2721 "detailed-status-message (text(MAX)) has wrong "
2722 "value tag %s (RFC 2911 section 3.1.6.3).",
2723 ippTagString(attrptr
->value_tag
));
2724 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2726 "detailed-status-message (text(MAX)) has wrong "
2727 "group tag %s (RFC 2911 section 3.1.6.3).",
2728 ippTagString(attrptr
->group_tag
));
2729 if (attrptr
->num_values
!= 1)
2731 "detailed-status-message (text(MAX)) has %d values"
2732 " (RFC 2911 section 3.1.6.3).",
2733 attrptr
->num_values
);
2734 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2735 strlen(attrptr
->values
[0].string
.text
) > 1023)
2737 "detailed-status-message (text(MAX)) has bad "
2738 "length %d (RFC 2911 section 3.1.6.3).",
2739 (int)strlen(attrptr
->values
[0].string
.text
));
2742 a
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2744 for (attrptr
= response
->attrs
,
2745 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2747 attrptr
= attrptr
->next
)
2749 if (attrptr
->group_tag
!= group
)
2751 int out_of_order
= 0; /* Are attribute groups out-of-order? */
2754 switch (attrptr
->group_tag
)
2759 case IPP_TAG_OPERATION
:
2763 case IPP_TAG_UNSUPPORTED_GROUP
:
2764 if (group
!= IPP_TAG_OPERATION
)
2769 case IPP_TAG_PRINTER
:
2770 if (group
!= IPP_TAG_OPERATION
&&
2771 group
!= IPP_TAG_UNSUPPORTED_GROUP
)
2775 case IPP_TAG_SUBSCRIPTION
:
2776 if (group
> attrptr
->group_tag
&&
2777 group
!= IPP_TAG_DOCUMENT
)
2782 if (group
> attrptr
->group_tag
)
2788 add_stringf(errors
, "Attribute groups out of order (%s < %s)",
2789 ippTagString(attrptr
->group_tag
),
2790 ippTagString(group
));
2792 if (attrptr
->group_tag
!= IPP_TAG_ZERO
)
2793 group
= attrptr
->group_tag
;
2796 validate_attr(errors
, attrptr
);
2800 if (cupsArrayFind(a
, attrptr
->name
))
2801 add_stringf(errors
, "Duplicate \"%s\" attribute in %s group",
2802 attrptr
->name
, ippTagString(group
));
2804 cupsArrayAdd(a
, attrptr
->name
);
2811 * Now check the test-defined expected status-code and attribute
2815 for (i
= 0; i
< num_statuses
; i
++)
2817 if (statuses
[i
].if_defined
&&
2818 !get_variable(vars
, statuses
[i
].if_defined
))
2821 if (statuses
[i
].if_not_defined
&&
2822 get_variable(vars
, statuses
[i
].if_not_defined
))
2825 if (response
->request
.status
.status_code
== statuses
[i
].status
)
2827 if (statuses
[i
].repeat_match
&&
2828 repeat_count
< statuses
[i
].repeat_limit
)
2831 if (statuses
[i
].define_match
)
2832 set_variable(vars
, statuses
[i
].define_match
, "1");
2838 if (statuses
[i
].repeat_no_match
&&
2839 repeat_count
< statuses
[i
].repeat_limit
)
2842 if (statuses
[i
].define_no_match
)
2844 set_variable(vars
, statuses
[i
].define_no_match
, "1");
2850 if (i
== num_statuses
&& num_statuses
> 0)
2852 for (i
= 0; i
< num_statuses
; i
++)
2854 if (statuses
[i
].if_defined
&&
2855 !get_variable(vars
, statuses
[i
].if_defined
))
2858 if (statuses
[i
].if_not_defined
&&
2859 get_variable(vars
, statuses
[i
].if_not_defined
))
2862 if (!statuses
[i
].repeat_match
)
2863 add_stringf(errors
, "EXPECTED: STATUS %s (got %s)",
2864 ippErrorString(statuses
[i
].status
),
2865 ippErrorString(cupsLastError()));
2868 if ((attrptr
= ippFindAttribute(response
, "status-message",
2869 IPP_TAG_TEXT
)) != NULL
)
2870 add_stringf(errors
, "status-message=\"%s\"",
2871 attrptr
->values
[0].string
.text
);
2874 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
2876 if (expect
->if_defined
&& !get_variable(vars
, expect
->if_defined
))
2879 if (expect
->if_not_defined
&&
2880 get_variable(vars
, expect
->if_not_defined
))
2883 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
2885 if ((found
&& expect
->not_expect
) ||
2886 (!found
&& !(expect
->not_expect
|| expect
->optional
)) ||
2887 (found
&& !expect_matches(expect
, found
->value_tag
)) ||
2888 (found
&& expect
->in_group
&&
2889 found
->group_tag
!= expect
->in_group
))
2891 if (expect
->define_no_match
)
2892 set_variable(vars
, expect
->define_no_match
, "1");
2893 else if (!expect
->define_match
&& !expect
->define_value
)
2895 if (found
&& expect
->not_expect
)
2896 add_stringf(errors
, "NOT EXPECTED: %s", expect
->name
);
2897 else if (!found
&& !(expect
->not_expect
|| expect
->optional
))
2898 add_stringf(errors
, "EXPECTED: %s", expect
->name
);
2901 if (!expect_matches(expect
, found
->value_tag
))
2902 add_stringf(errors
, "EXPECTED: %s OF-TYPE %s (got %s)",
2903 expect
->name
, expect
->of_type
,
2904 ippTagString(found
->value_tag
));
2906 if (expect
->in_group
&& found
->group_tag
!= expect
->in_group
)
2907 add_stringf(errors
, "EXPECTED: %s IN-GROUP %s (got %s).",
2908 expect
->name
, ippTagString(expect
->in_group
),
2909 ippTagString(found
->group_tag
));
2913 if (expect
->repeat_no_match
&&
2914 repeat_count
< expect
->repeat_limit
)
2921 ippAttributeString(found
, buffer
, sizeof(buffer
));
2924 !with_value(NULL
, expect
->with_value
, expect
->with_flags
, found
,
2925 buffer
, sizeof(buffer
)))
2927 if (expect
->define_no_match
)
2928 set_variable(vars
, expect
->define_no_match
, "1");
2929 else if (!expect
->define_match
&& !expect
->define_value
&&
2930 !expect
->repeat_match
&& !expect
->repeat_no_match
)
2932 if (expect
->with_flags
& _CUPS_WITH_REGEX
)
2933 add_stringf(errors
, "EXPECTED: %s %s /%s/",
2935 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
2936 "WITH-ALL-VALUES" : "WITH-VALUE",
2937 expect
->with_value
);
2939 add_stringf(errors
, "EXPECTED: %s %s \"%s\"",
2941 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
2942 "WITH-ALL-VALUES" : "WITH-VALUE",
2943 expect
->with_value
);
2945 with_value(errors
, expect
->with_value
, expect
->with_flags
, found
,
2946 buffer
, sizeof(buffer
));
2949 if (expect
->repeat_no_match
&&
2950 repeat_count
< expect
->repeat_limit
)
2956 if (found
&& expect
->count
> 0 &&
2957 found
->num_values
!= expect
->count
)
2959 if (expect
->define_no_match
)
2960 set_variable(vars
, expect
->define_no_match
, "1");
2961 else if (!expect
->define_match
&& !expect
->define_value
)
2963 add_stringf(errors
, "EXPECTED: %s COUNT %d (got %d)", expect
->name
,
2964 expect
->count
, found
->num_values
);
2967 if (expect
->repeat_no_match
&&
2968 repeat_count
< expect
->repeat_limit
)
2974 if (found
&& expect
->same_count_as
)
2976 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
2979 if (!attrptr
|| attrptr
->num_values
!= found
->num_values
)
2981 if (expect
->define_no_match
)
2982 set_variable(vars
, expect
->define_no_match
, "1");
2983 else if (!expect
->define_match
&& !expect
->define_value
)
2987 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2988 "(not returned)", expect
->name
,
2989 found
->num_values
, expect
->same_count_as
);
2990 else if (attrptr
->num_values
!= found
->num_values
)
2992 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2993 "(%d values)", expect
->name
, found
->num_values
,
2994 expect
->same_count_as
, attrptr
->num_values
);
2997 if (expect
->repeat_no_match
&&
2998 repeat_count
< expect
->repeat_limit
)
3005 if (found
&& expect
->define_match
)
3006 set_variable(vars
, expect
->define_match
, "1");
3008 if (found
&& expect
->define_value
)
3009 set_variable(vars
, expect
->define_value
, buffer
);
3011 if (found
&& expect
->repeat_match
&&
3012 repeat_count
< expect
->repeat_limit
)
3018 * If we are going to repeat this test, sleep 1 second so we don't flood
3019 * the printer with requests...
3024 if (Output
== _CUPS_OUTPUT_TEST
)
3026 printf("%04d]\n", repeat_count
);
3030 sleep(repeat_interval
);
3031 repeat_interval
= _cupsNextDelay(repeat_interval
, &repeat_prev
);
3033 if (Output
== _CUPS_OUTPUT_TEST
)
3035 printf(" %-68.68s [", name
);
3040 while (repeat_test
);
3046 if (cupsArrayCount(errors
) > 0)
3047 prev_pass
= pass
= 0;
3054 if (Output
== _CUPS_OUTPUT_PLIST
)
3056 puts("<key>Successful</key>");
3057 puts(prev_pass
? "<true />" : "<false />");
3058 puts("<key>StatusCode</key>");
3059 print_xml_string("string", ippErrorString(cupsLastError()));
3060 puts("<key>ResponseAttributes</key>");
3063 for (attrptr
= response
? response
->attrs
: NULL
,
3064 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
3066 attrptr
= attrptr
->next
)
3067 print_attr(attrptr
, &group
);
3071 else if (Output
== _CUPS_OUTPUT_TEST
)
3073 puts(prev_pass
? "PASS]" : "FAIL]");
3075 if (!prev_pass
|| (Verbosity
&& response
))
3077 printf(" RECEIVED: %lu bytes in response\n",
3078 (unsigned long)ippLength(response
));
3079 printf(" status-code = %s (%s)\n", ippErrorString(cupsLastError()),
3080 cupsLastErrorString());
3084 for (attrptr
= response
->attrs
;
3086 attrptr
= attrptr
->next
)
3087 print_attr(attrptr
, NULL
);
3091 else if (!prev_pass
)
3092 fprintf(stderr
, "%s\n", cupsLastErrorString());
3094 if (prev_pass
&& Output
>= _CUPS_OUTPUT_LIST
&& !Verbosity
&&
3097 size_t width
; /* Length of value */
3099 for (i
= 0; i
< num_displayed
; i
++)
3101 widths
[i
] = strlen(displayed
[i
]);
3103 for (attrptr
= ippFindAttribute(response
, displayed
[i
], IPP_TAG_ZERO
);
3105 attrptr
= ippFindNextAttribute(response
, displayed
[i
],
3108 width
= ippAttributeString(attrptr
, NULL
, 0);
3109 if (width
> widths
[i
])
3114 if (Output
== _CUPS_OUTPUT_CSV
)
3115 print_csv(NULL
, num_displayed
, displayed
, widths
);
3117 print_line(NULL
, num_displayed
, displayed
, widths
);
3119 attrptr
= response
->attrs
;
3123 while (attrptr
&& attrptr
->group_tag
<= IPP_TAG_OPERATION
)
3124 attrptr
= attrptr
->next
;
3128 if (Output
== _CUPS_OUTPUT_CSV
)
3129 print_csv(attrptr
, num_displayed
, displayed
, widths
);
3131 print_line(attrptr
, num_displayed
, displayed
, widths
);
3133 while (attrptr
&& attrptr
->group_tag
> IPP_TAG_OPERATION
)
3134 attrptr
= attrptr
->next
;
3138 else if (!prev_pass
)
3140 if (Output
== _CUPS_OUTPUT_PLIST
)
3142 puts("<key>Errors</key>");
3145 for (error
= (char *)cupsArrayFirst(errors
);
3147 error
= (char *)cupsArrayNext(errors
))
3148 print_xml_string("string", error
);
3154 for (error
= (char *)cupsArrayFirst(errors
);
3156 error
= (char *)cupsArrayNext(errors
))
3157 printf(" %s\n", error
);
3161 if (num_displayed
> 0 && !Verbosity
&& response
&&
3162 Output
== _CUPS_OUTPUT_TEST
)
3164 for (attrptr
= response
->attrs
;
3166 attrptr
= attrptr
->next
)
3170 for (i
= 0; i
< num_displayed
; i
++)
3172 if (!strcmp(displayed
[i
], attrptr
->name
))
3174 print_attr(attrptr
, NULL
);
3184 if (Output
== _CUPS_OUTPUT_PLIST
)
3189 ippDelete(response
);
3192 for (i
= 0; i
< num_statuses
; i
++)
3194 if (statuses
[i
].if_defined
)
3195 free(statuses
[i
].if_defined
);
3196 if (statuses
[i
].if_not_defined
)
3197 free(statuses
[i
].if_not_defined
);
3198 if (statuses
[i
].define_match
)
3199 free(statuses
[i
].define_match
);
3200 if (statuses
[i
].define_no_match
)
3201 free(statuses
[i
].define_no_match
);
3205 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3208 if (expect
->of_type
)
3209 free(expect
->of_type
);
3210 if (expect
->same_count_as
)
3211 free(expect
->same_count_as
);
3212 if (expect
->if_defined
)
3213 free(expect
->if_defined
);
3214 if (expect
->if_not_defined
)
3215 free(expect
->if_not_defined
);
3216 if (expect
->with_value
)
3217 free(expect
->with_value
);
3218 if (expect
->define_match
)
3219 free(expect
->define_match
);
3220 if (expect
->define_no_match
)
3221 free(expect
->define_no_match
);
3222 if (expect
->define_value
)
3223 free(expect
->define_value
);
3227 for (i
= 0; i
< num_displayed
; i
++)
3231 if (!ignore_errors
&& !prev_pass
)
3237 cupsArrayDelete(errors
);
3244 ippDelete(response
);
3246 for (i
= 0; i
< num_statuses
; i
++)
3248 if (statuses
[i
].if_defined
)
3249 free(statuses
[i
].if_defined
);
3250 if (statuses
[i
].if_not_defined
)
3251 free(statuses
[i
].if_not_defined
);
3252 if (statuses
[i
].define_match
)
3253 free(statuses
[i
].define_match
);
3254 if (statuses
[i
].define_no_match
)
3255 free(statuses
[i
].define_no_match
);
3258 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3261 if (expect
->of_type
)
3262 free(expect
->of_type
);
3263 if (expect
->same_count_as
)
3264 free(expect
->same_count_as
);
3265 if (expect
->if_defined
)
3266 free(expect
->if_defined
);
3267 if (expect
->if_not_defined
)
3268 free(expect
->if_not_defined
);
3269 if (expect
->with_value
)
3270 free(expect
->with_value
);
3271 if (expect
->define_match
)
3272 free(expect
->define_match
);
3273 if (expect
->define_no_match
)
3274 free(expect
->define_no_match
);
3275 if (expect
->define_value
)
3276 free(expect
->define_value
);
3279 for (i
= 0; i
< num_displayed
; i
++)
3287 * 'expand_variables()' - Expand variables in a string.
3291 expand_variables(_cups_vars_t
*vars
, /* I - Variables */
3292 char *dst
, /* I - Destination string buffer */
3293 const char *src
, /* I - Source string */
3294 size_t dstsize
) /* I - Size of destination buffer */
3296 char *dstptr
, /* Pointer into destination */
3297 *dstend
, /* End of destination */
3298 temp
[256], /* Temporary string */
3299 *tempptr
; /* Pointer into temporary string */
3300 const char *value
; /* Value to substitute */
3304 dstend
= dst
+ dstsize
- 1;
3306 while (*src
&& dstptr
< dstend
)
3311 * Substitute a string/number...
3314 if (!strncmp(src
, "$$", 2))
3319 else if (!strncmp(src
, "$ENV[", 5))
3321 strlcpy(temp
, src
+ 5, sizeof(temp
));
3323 for (tempptr
= temp
; *tempptr
; tempptr
++)
3324 if (*tempptr
== ']')
3330 value
= getenv(temp
);
3331 src
+= tempptr
- temp
+ 5;
3335 strlcpy(temp
, src
+ 1, sizeof(temp
));
3337 for (tempptr
= temp
; *tempptr
; tempptr
++)
3338 if (!isalnum(*tempptr
& 255) && *tempptr
!= '-' && *tempptr
!= '_')
3344 if (!strcmp(temp
, "uri"))
3346 else if (!strcmp(temp
, "filename"))
3347 value
= vars
->filename
;
3348 else if (!strcmp(temp
, "scheme") || !strcmp(temp
, "method"))
3349 value
= vars
->scheme
;
3350 else if (!strcmp(temp
, "username"))
3351 value
= vars
->userpass
;
3352 else if (!strcmp(temp
, "hostname"))
3353 value
= vars
->hostname
;
3354 else if (!strcmp(temp
, "port"))
3356 snprintf(temp
, sizeof(temp
), "%d", vars
->port
);
3359 else if (!strcmp(temp
, "resource"))
3360 value
= vars
->resource
;
3361 else if (!strcmp(temp
, "user"))
3364 value
= get_variable(vars
, temp
);
3366 src
+= tempptr
- temp
+ 1;
3376 strlcpy(dstptr
, value
, dstend
- dstptr
+ 1);
3377 dstptr
+= strlen(dstptr
);
3389 * 'expect_matches()' - Return true if the tag matches the specification.
3392 static int /* O - 1 if matches, 0 otherwise */
3394 _cups_expect_t
*expect
, /* I - Expected attribute */
3395 ipp_tag_t value_tag
) /* I - Value tag for attribute */
3397 int match
; /* Match? */
3398 char *of_type
, /* Type name to match */
3399 *next
, /* Next name to match */
3400 sep
; /* Separator character */
3404 * If we don't expect a particular type, return immediately...
3407 if (!expect
->of_type
)
3411 * Parse the "of_type" value since the string can contain multiple attribute
3412 * types separated by "," or "|"...
3415 for (of_type
= expect
->of_type
, match
= 0; !match
&& *of_type
; of_type
= next
)
3418 * Find the next separator, and set it (temporarily) to nul if present.
3421 for (next
= of_type
; *next
&& *next
!= '|' && *next
!= ','; next
++);
3423 if ((sep
= *next
) != '\0')
3427 * Support some meta-types to make it easier to write the test file.
3430 if (!strcmp(of_type
, "text"))
3431 match
= value_tag
== IPP_TAG_TEXTLANG
|| value_tag
== IPP_TAG_TEXT
;
3432 else if (!strcmp(of_type
, "name"))
3433 match
= value_tag
== IPP_TAG_NAMELANG
|| value_tag
== IPP_TAG_NAME
;
3434 else if (!strcmp(of_type
, "collection"))
3435 match
= value_tag
== IPP_TAG_BEGIN_COLLECTION
;
3437 match
= value_tag
== ippTagValue(of_type
);
3440 * Restore the separator if we have one...
3452 * 'get_collection()' - Get a collection value from the current test file.
3455 static ipp_t
* /* O - Collection value */
3456 get_collection(_cups_vars_t
*vars
, /* I - Variables */
3457 FILE *fp
, /* I - File to read from */
3458 int *linenum
) /* IO - Line number */
3460 char token
[1024], /* Token from file */
3461 temp
[1024], /* Temporary string */
3462 attr
[128]; /* Attribute name */
3463 ipp_tag_t value
; /* Current value type */
3464 ipp_t
*col
= ippNew(); /* Collection value */
3465 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
3468 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
3470 if (!strcmp(token
, "}"))
3472 else if (!strcmp(token
, "{") && lastcol
)
3475 * Another collection value
3478 ipp_t
*subcol
= get_collection(vars
, fp
, linenum
);
3479 /* Collection value */
3482 ippSetCollection(col
, &lastcol
, ippGetCount(lastcol
), subcol
);
3486 else if (!_cups_strcasecmp(token
, "MEMBER"))
3494 if (!get_token(fp
, token
, sizeof(token
), linenum
))
3496 print_fatal_error("Missing MEMBER value tag on line %d.", *linenum
);
3500 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
3502 print_fatal_error("Bad MEMBER value tag \"%s\" on line %d.", token
,
3507 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
3509 print_fatal_error("Missing MEMBER name on line %d.", *linenum
);
3513 if (!get_token(fp
, temp
, sizeof(temp
), linenum
))
3515 print_fatal_error("Missing MEMBER value on line %d.", *linenum
);
3519 expand_variables(vars
, token
, temp
, sizeof(token
));
3523 case IPP_TAG_BOOLEAN
:
3524 if (!_cups_strcasecmp(token
, "true"))
3525 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
3527 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, atoi(token
));
3530 case IPP_TAG_INTEGER
:
3532 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
3535 case IPP_TAG_RESOLUTION
:
3537 int xres
, /* X resolution */
3538 yres
; /* Y resolution */
3539 char units
[6]; /* Units */
3541 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
3542 (_cups_strcasecmp(units
, "dpi") &&
3543 _cups_strcasecmp(units
, "dpc") &&
3544 _cups_strcasecmp(units
, "dpcm") &&
3545 _cups_strcasecmp(units
, "other")))
3547 print_fatal_error("Bad resolution value \"%s\" on line %d.",
3552 if (!_cups_strcasecmp(units
, "dpi"))
3553 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
3555 else if (!_cups_strcasecmp(units
, "dpc") ||
3556 !_cups_strcasecmp(units
, "dpcm"))
3557 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
3560 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
3565 case IPP_TAG_RANGE
:
3567 int lowers
[4], /* Lower value */
3568 uppers
[4], /* Upper values */
3569 num_vals
; /* Number of values */
3572 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
3573 lowers
+ 0, uppers
+ 0,
3574 lowers
+ 1, uppers
+ 1,
3575 lowers
+ 2, uppers
+ 2,
3576 lowers
+ 3, uppers
+ 3);
3578 if ((num_vals
& 1) || num_vals
== 0)
3580 print_fatal_error("Bad rangeOfInteger value \"%s\" on line %d.",
3585 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
3590 case IPP_TAG_BEGIN_COLLECTION
:
3591 if (!strcmp(token
, "{"))
3593 ipp_t
*subcol
= get_collection(vars
, fp
, linenum
);
3594 /* Collection value */
3598 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
3606 print_fatal_error("Bad collection value on line %d.", *linenum
);
3610 case IPP_TAG_STRING
:
3611 ippAddOctetString(col
, IPP_TAG_ZERO
, attr
, token
, strlen(token
));
3615 if (!strchr(token
, ','))
3616 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
3620 * Multiple string values...
3623 int num_values
; /* Number of values */
3624 char *values
[100], /* Values */
3625 *ptr
; /* Pointer to next value */
3631 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
3634 values
[num_values
] = ptr
;
3638 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
3639 NULL
, (const char **)values
);
3649 * If we get here there was a parse error; free memory and return.
3661 * 'get_filename()' - Get a filename based on the current test file.
3664 static char * /* O - Filename */
3665 get_filename(const char *testfile
, /* I - Current test file */
3666 char *dst
, /* I - Destination filename */
3667 const char *src
, /* I - Source filename */
3668 size_t dstsize
) /* I - Size of destination buffer */
3670 char *dstptr
; /* Pointer into destination */
3671 _cups_globals_t
*cg
= _cupsGlobals();
3675 if (*src
== '<' && src
[strlen(src
) - 1] == '>')
3678 * Map <filename> to CUPS_DATADIR/ipptool/filename...
3681 snprintf(dst
, dstsize
, "%s/ipptool/%s", cg
->cups_datadir
, src
+ 1);
3682 dstptr
= dst
+ strlen(dst
) - 1;
3686 else if (*src
== '/' || !strchr(testfile
, '/')
3688 || (isalpha(*src
& 255) && src
[1] == ':')
3693 * Use the path as-is...
3696 strlcpy(dst
, src
, dstsize
);
3701 * Make path relative to testfile...
3704 strlcpy(dst
, testfile
, dstsize
);
3705 if ((dstptr
= strrchr(dst
, '/')) != NULL
)
3708 dstptr
= dst
; /* Should never happen */
3710 strlcpy(dstptr
, src
, dstsize
- (dstptr
- dst
));
3718 * 'get_string()' - Get a pointer to a string value or the portion of interest.
3721 static char * /* O - Pointer to string */
3722 get_string(ipp_attribute_t
*attr
, /* I - IPP attribute */
3723 int element
, /* I - Element to fetch */
3724 int flags
, /* I - Value ("with") flags */
3725 char *buffer
, /* I - Temporary buffer */
3726 size_t bufsize
) /* I - Size of temporary buffer */
3728 char *ptr
, /* Value */
3729 scheme
[256], /* URI scheme */
3730 userpass
[256], /* Username/password */
3731 hostname
[256], /* Hostname */
3732 resource
[1024]; /* Resource */
3733 int port
; /* Port number */
3736 ptr
= attr
->values
[element
].string
.text
;
3738 if (flags
& _CUPS_WITH_HOSTNAME
)
3740 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ptr
, scheme
, sizeof(scheme
),
3741 userpass
, sizeof(userpass
), buffer
, bufsize
, &port
,
3742 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
3747 else if (flags
& _CUPS_WITH_RESOURCE
)
3749 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ptr
, scheme
, sizeof(scheme
),
3750 userpass
, sizeof(userpass
), hostname
, sizeof(hostname
),
3751 &port
, buffer
, bufsize
) < HTTP_URI_STATUS_OK
)
3756 else if (flags
& _CUPS_WITH_SCHEME
)
3758 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ptr
, buffer
, bufsize
,
3759 userpass
, sizeof(userpass
), hostname
, sizeof(hostname
),
3760 &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
3771 * 'get_token()' - Get a token from a file.
3774 static char * /* O - Token from file or NULL on EOF */
3775 get_token(FILE *fp
, /* I - File to read from */
3776 char *buf
, /* I - Buffer to read into */
3777 int buflen
, /* I - Length of buffer */
3778 int *linenum
) /* IO - Current line number */
3780 int ch
, /* Character from file */
3781 quote
; /* Quoting character */
3782 char *bufptr
, /* Pointer into buffer */
3783 *bufend
; /* End of buffer */
3789 * Skip whitespace...
3792 while (isspace(ch
= getc(fp
)))
3804 else if (ch
== '\'' || ch
== '\"')
3807 * Quoted text or regular expression...
3812 bufend
= buf
+ buflen
- 1;
3814 while ((ch
= getc(fp
)) != EOF
)
3819 * Escape next character...
3822 if (bufptr
< bufend
)
3825 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
3828 else if (ch
== quote
)
3830 else if (bufptr
< bufend
)
3844 while ((ch
= getc(fp
)) != EOF
)
3853 * Whitespace delimited text...
3859 bufend
= buf
+ buflen
- 1;
3861 while ((ch
= getc(fp
)) != EOF
)
3862 if (isspace(ch
) || ch
== '#')
3864 else if (bufptr
< bufend
)
3869 else if (ch
== '\n')
3881 * 'get_variable()' - Get the value of a variable.
3884 static char * /* O - Value or NULL */
3885 get_variable(_cups_vars_t
*vars
, /* I - Variables */
3886 const char *name
) /* I - Variable name */
3888 _cups_var_t key
, /* Search key */
3889 *match
; /* Matching variable, if any */
3892 key
.name
= (char *)name
;
3893 match
= cupsArrayFind(vars
->vars
, &key
);
3895 return (match
? match
->value
: NULL
);
3900 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
3904 static char * /* O - ISO 8601 date/time string */
3905 iso_date(ipp_uchar_t
*date
) /* I - IPP (RFC 1903) date/time value */
3907 time_t utctime
; /* UTC time since 1970 */
3908 struct tm
*utcdate
; /* UTC date/time */
3909 static char buffer
[255]; /* String buffer */
3912 utctime
= ippDateToTime(date
);
3913 utcdate
= gmtime(&utctime
);
3915 snprintf(buffer
, sizeof(buffer
), "%04d-%02d-%02dT%02d:%02d:%02dZ",
3916 utcdate
->tm_year
+ 1900, utcdate
->tm_mon
+ 1, utcdate
->tm_mday
,
3917 utcdate
->tm_hour
, utcdate
->tm_min
, utcdate
->tm_sec
);
3924 * 'password_cb()' - Password callback for authenticated tests.
3927 static const char * /* O - Password */
3928 password_cb(const char *prompt
) /* I - Prompt (unused) */
3937 * 'print_attr()' - Print an attribute on the screen.
3941 print_attr(ipp_attribute_t
*attr
, /* I - Attribute to print */
3942 ipp_tag_t
*group
) /* IO - Current group */
3944 int i
; /* Looping var */
3945 ipp_attribute_t
*colattr
; /* Collection attribute */
3948 if (Output
== _CUPS_OUTPUT_PLIST
)
3950 if (!attr
->name
|| (group
&& *group
!= attr
->group_tag
))
3952 if (attr
->group_tag
!= IPP_TAG_ZERO
)
3959 *group
= attr
->group_tag
;
3965 print_xml_string("key", attr
->name
);
3966 if (attr
->num_values
> 1)
3969 switch (attr
->value_tag
)
3971 case IPP_TAG_INTEGER
:
3973 for (i
= 0; i
< attr
->num_values
; i
++)
3974 if (Output
== _CUPS_OUTPUT_PLIST
)
3975 printf("<integer>%d</integer>\n", attr
->values
[i
].integer
);
3977 printf("%d ", attr
->values
[i
].integer
);
3980 case IPP_TAG_BOOLEAN
:
3981 for (i
= 0; i
< attr
->num_values
; i
++)
3982 if (Output
== _CUPS_OUTPUT_PLIST
)
3983 puts(attr
->values
[i
].boolean
? "<true />" : "<false />");
3984 else if (attr
->values
[i
].boolean
)
3985 fputs("true ", stdout
);
3987 fputs("false ", stdout
);
3990 case IPP_TAG_RANGE
:
3991 for (i
= 0; i
< attr
->num_values
; i
++)
3992 if (Output
== _CUPS_OUTPUT_PLIST
)
3993 printf("<dict><key>lower</key><integer>%d</integer>"
3994 "<key>upper</key><integer>%d</integer></dict>\n",
3995 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
3997 printf("%d-%d ", attr
->values
[i
].range
.lower
,
3998 attr
->values
[i
].range
.upper
);
4001 case IPP_TAG_RESOLUTION
:
4002 for (i
= 0; i
< attr
->num_values
; i
++)
4003 if (Output
== _CUPS_OUTPUT_PLIST
)
4004 printf("<dict><key>xres</key><integer>%d</integer>"
4005 "<key>yres</key><integer>%d</integer>"
4006 "<key>units</key><string>%s</string></dict>\n",
4007 attr
->values
[i
].resolution
.xres
,
4008 attr
->values
[i
].resolution
.yres
,
4009 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
4012 printf("%dx%d%s ", attr
->values
[i
].resolution
.xres
,
4013 attr
->values
[i
].resolution
.yres
,
4014 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
4019 for (i
= 0; i
< attr
->num_values
; i
++)
4020 if (Output
== _CUPS_OUTPUT_PLIST
)
4021 printf("<date>%s</date>\n", iso_date(attr
->values
[i
].date
));
4023 printf("%s ", iso_date(attr
->values
[i
].date
));
4026 case IPP_TAG_STRING
:
4027 for (i
= 0; i
< attr
->num_values
; i
++)
4029 if (Output
== _CUPS_OUTPUT_PLIST
)
4031 char buffer
[IPP_MAX_LENGTH
* 5 / 4 + 1];
4034 printf("<data>%s</data>\n",
4035 httpEncode64_2(buffer
, sizeof(buffer
),
4036 attr
->values
[i
].unknown
.data
,
4037 attr
->values
[i
].unknown
.length
));
4041 char *ptr
, /* Pointer into data */
4042 *end
; /* End of data */
4045 for (ptr
= attr
->values
[i
].unknown
.data
,
4046 end
= ptr
+ attr
->values
[i
].unknown
.length
;
4050 if (*ptr
== '\\' || *ptr
== '\"')
4051 printf("\\%c", *ptr
);
4052 else if (!isprint(*ptr
& 255))
4053 printf("\\%03o", *ptr
& 255);
4064 case IPP_TAG_KEYWORD
:
4065 case IPP_TAG_CHARSET
:
4067 case IPP_TAG_MIMETYPE
:
4068 case IPP_TAG_LANGUAGE
:
4069 for (i
= 0; i
< attr
->num_values
; i
++)
4070 if (Output
== _CUPS_OUTPUT_PLIST
)
4071 print_xml_string("string", attr
->values
[i
].string
.text
);
4073 printf("\"%s\" ", attr
->values
[i
].string
.text
);
4076 case IPP_TAG_TEXTLANG
:
4077 case IPP_TAG_NAMELANG
:
4078 for (i
= 0; i
< attr
->num_values
; i
++)
4079 if (Output
== _CUPS_OUTPUT_PLIST
)
4081 fputs("<dict><key>language</key><string>", stdout
);
4082 print_xml_string(NULL
, attr
->values
[i
].string
.language
);
4083 fputs("</string><key>string</key><string>", stdout
);
4084 print_xml_string(NULL
, attr
->values
[i
].string
.text
);
4085 puts("</string></dict>");
4088 printf("\"%s\"[%s] ", attr
->values
[i
].string
.text
,
4089 attr
->values
[i
].string
.language
);
4092 case IPP_TAG_BEGIN_COLLECTION
:
4093 for (i
= 0; i
< attr
->num_values
; i
++)
4095 if (Output
== _CUPS_OUTPUT_PLIST
)
4098 for (colattr
= attr
->values
[i
].collection
->attrs
;
4100 colattr
= colattr
->next
)
4101 print_attr(colattr
, NULL
);
4109 print_col(attr
->values
[i
].collection
);
4115 if (Output
== _CUPS_OUTPUT_PLIST
)
4116 printf("<string><<%s>></string>\n",
4117 ippTagString(attr
->value_tag
));
4119 fputs(ippTagString(attr
->value_tag
), stdout
);
4123 if (attr
->num_values
> 1)
4128 char buffer
[8192]; /* Value buffer */
4130 if (Output
== _CUPS_OUTPUT_TEST
)
4134 puts(" -- separator --");
4138 printf(" %s (%s%s) = ", attr
->name
,
4139 attr
->num_values
> 1 ? "1setOf " : "",
4140 ippTagString(attr
->value_tag
));
4143 ippAttributeString(attr
, buffer
, sizeof(buffer
));
4150 * 'print_col()' - Print a collection attribute on the screen.
4154 print_col(ipp_t
*col
) /* I - Collection attribute to print */
4156 int i
; /* Looping var */
4157 ipp_attribute_t
*attr
; /* Current attribute in collection */
4160 fputs("{ ", stdout
);
4161 for (attr
= col
->attrs
; attr
; attr
= attr
->next
)
4163 printf("%s (%s%s) = ", attr
->name
, attr
->num_values
> 1 ? "1setOf " : "",
4164 ippTagString(attr
->value_tag
));
4166 switch (attr
->value_tag
)
4168 case IPP_TAG_INTEGER
:
4170 for (i
= 0; i
< attr
->num_values
; i
++)
4171 printf("%d ", attr
->values
[i
].integer
);
4174 case IPP_TAG_BOOLEAN
:
4175 for (i
= 0; i
< attr
->num_values
; i
++)
4176 if (attr
->values
[i
].boolean
)
4182 case IPP_TAG_NOVALUE
:
4186 case IPP_TAG_RANGE
:
4187 for (i
= 0; i
< attr
->num_values
; i
++)
4188 printf("%d-%d ", attr
->values
[i
].range
.lower
,
4189 attr
->values
[i
].range
.upper
);
4192 case IPP_TAG_RESOLUTION
:
4193 for (i
= 0; i
< attr
->num_values
; i
++)
4194 printf("%dx%d%s ", attr
->values
[i
].resolution
.xres
,
4195 attr
->values
[i
].resolution
.yres
,
4196 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
4200 case IPP_TAG_STRING
:
4203 case IPP_TAG_KEYWORD
:
4204 case IPP_TAG_CHARSET
:
4206 case IPP_TAG_MIMETYPE
:
4207 case IPP_TAG_LANGUAGE
:
4208 for (i
= 0; i
< attr
->num_values
; i
++)
4209 printf("\"%s\" ", attr
->values
[i
].string
.text
);
4212 case IPP_TAG_TEXTLANG
:
4213 case IPP_TAG_NAMELANG
:
4214 for (i
= 0; i
< attr
->num_values
; i
++)
4215 printf("\"%s\"[%s] ", attr
->values
[i
].string
.text
,
4216 attr
->values
[i
].string
.language
);
4219 case IPP_TAG_BEGIN_COLLECTION
:
4220 for (i
= 0; i
< attr
->num_values
; i
++)
4222 print_col(attr
->values
[i
].collection
);
4228 break; /* anti-compiler-warning-code */
4237 * 'print_csv()' - Print a line of CSV text.
4242 ipp_attribute_t
*attr
, /* I - First attribute for line */
4243 int num_displayed
, /* I - Number of attributes to display */
4244 char **displayed
, /* I - Attributes to display */
4245 size_t *widths
) /* I - Column widths */
4247 int i
; /* Looping var */
4248 size_t maxlength
; /* Max length of all columns */
4249 char *buffer
, /* String buffer */
4250 *bufptr
; /* Pointer into buffer */
4251 ipp_attribute_t
*current
; /* Current attribute */
4255 * Get the maximum string length we have to show and allocate...
4258 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4259 if (widths
[i
] > maxlength
)
4260 maxlength
= widths
[i
];
4264 if ((buffer
= malloc(maxlength
)) == NULL
)
4268 * Loop through the attributes to display...
4273 for (i
= 0; i
< num_displayed
; i
++)
4280 for (current
= attr
; current
; current
= current
->next
)
4284 else if (!strcmp(current
->name
, displayed
[i
]))
4286 ippAttributeString(current
, buffer
, maxlength
);
4291 if (strchr(buffer
, ',') != NULL
|| strchr(buffer
, '\"') != NULL
||
4292 strchr(buffer
, '\\') != NULL
)
4295 for (bufptr
= buffer
; *bufptr
; bufptr
++)
4297 if (*bufptr
== '\\' || *bufptr
== '\"')
4304 fputs(buffer
, stdout
);
4310 for (i
= 0; i
< num_displayed
; i
++)
4315 fputs(displayed
[i
], stdout
);
4325 * 'print_fatal_error()' - Print a fatal error message.
4329 print_fatal_error(const char *s
, /* I - Printf-style format string */
4330 ...) /* I - Additional arguments as needed */
4332 char buffer
[10240]; /* Format buffer */
4333 va_list ap
; /* Pointer to arguments */
4337 * Format the error message...
4341 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
4348 if (Output
== _CUPS_OUTPUT_PLIST
)
4351 print_xml_trailer(0, buffer
);
4354 _cupsLangPrintf(stderr
, "ipptool: %s", buffer
);
4359 * 'print_line()' - Print a line of formatted or CSV text.
4364 ipp_attribute_t
*attr
, /* I - First attribute for line */
4365 int num_displayed
, /* I - Number of attributes to display */
4366 char **displayed
, /* I - Attributes to display */
4367 size_t *widths
) /* I - Column widths */
4369 int i
; /* Looping var */
4370 size_t maxlength
; /* Max length of all columns */
4371 char *buffer
; /* String buffer */
4372 ipp_attribute_t
*current
; /* Current attribute */
4376 * Get the maximum string length we have to show and allocate...
4379 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4380 if (widths
[i
] > maxlength
)
4381 maxlength
= widths
[i
];
4385 if ((buffer
= malloc(maxlength
)) == NULL
)
4389 * Loop through the attributes to display...
4394 for (i
= 0; i
< num_displayed
; i
++)
4401 for (current
= attr
; current
; current
= current
->next
)
4405 else if (!strcmp(current
->name
, displayed
[i
]))
4407 ippAttributeString(current
, buffer
, maxlength
);
4412 printf("%*s", (int)-widths
[i
], buffer
);
4418 for (i
= 0; i
< num_displayed
; i
++)
4423 printf("%*s", (int)-widths
[i
], displayed
[i
]);
4427 for (i
= 0; i
< num_displayed
; i
++)
4432 memset(buffer
, '-', widths
[i
]);
4433 buffer
[widths
[i
]] = '\0';
4434 fputs(buffer
, stdout
);
4444 * 'print_xml_header()' - Print a standard XML plist header.
4448 print_xml_header(void)
4452 puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
4453 puts("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
4454 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
4455 puts("<plist version=\"1.0\">");
4457 puts("<key>ipptoolVersion</key>");
4458 puts("<string>" CUPS_SVERSION
"</string>");
4459 puts("<key>Transfer</key>");
4460 printf("<string>%s</string>\n",
4461 Transfer
== _CUPS_TRANSFER_AUTO
? "auto" :
4462 Transfer
== _CUPS_TRANSFER_CHUNKED
? "chunked" : "length");
4463 puts("<key>Tests</key>");
4472 * 'print_xml_string()' - Print an XML string with escaping.
4476 print_xml_string(const char *element
, /* I - Element name or NULL */
4477 const char *s
) /* I - String to print */
4480 printf("<%s>", element
);
4485 fputs("&", stdout
);
4487 fputs("<", stdout
);
4489 fputs(">", stdout
);
4490 else if ((*s
& 0xe0) == 0xc0)
4493 * Validate UTF-8 two-byte sequence...
4496 if ((s
[1] & 0xc0) != 0x80)
4507 else if ((*s
& 0xf0) == 0xe0)
4510 * Validate UTF-8 three-byte sequence...
4513 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80)
4525 else if ((*s
& 0xf8) == 0xf0)
4528 * Validate UTF-8 four-byte sequence...
4531 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80 ||
4532 (s
[3] & 0xc0) != 0x80)
4545 else if ((*s
& 0x80) || (*s
< ' ' && !isspace(*s
& 255)))
4548 * Invalid control character...
4560 printf("</%s>\n", element
);
4565 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
4569 print_xml_trailer(int success
, /* I - 1 on success, 0 on failure */
4570 const char *message
) /* I - Error message or NULL */
4575 puts("<key>Successful</key>");
4576 puts(success
? "<true />" : "<false />");
4579 puts("<key>ErrorMessage</key>");
4580 print_xml_string("string", message
);
4591 * 'set_variable()' - Set a variable value.
4595 set_variable(_cups_vars_t
*vars
, /* I - Variables */
4596 const char *name
, /* I - Variable name */
4597 const char *value
) /* I - Value string */
4599 _cups_var_t key
, /* Search key */
4600 *var
; /* New variable */
4603 if (!_cups_strcasecmp(name
, "filename"))
4606 free(vars
->filename
);
4608 vars
->filename
= strdup(value
);
4612 key
.name
= (char *)name
;
4613 if ((var
= cupsArrayFind(vars
->vars
, &key
)) != NULL
)
4616 var
->value
= strdup(value
);
4618 else if ((var
= malloc(sizeof(_cups_var_t
))) == NULL
)
4620 print_fatal_error("Unable to allocate memory for variable \"%s\".", name
);
4625 var
->name
= strdup(name
);
4626 var
->value
= strdup(value
);
4628 cupsArrayAdd(vars
->vars
, var
);
4635 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
4639 sigterm_handler(int sig
) /* I - Signal number (unused) */
4645 signal(SIGINT
, SIG_DFL
);
4646 signal(SIGTERM
, SIG_DFL
);
4652 * 'timeout_cb()' - Handle HTTP timeouts.
4655 static int /* O - 1 to continue, 0 to cancel */
4656 timeout_cb(http_t
*http
, /* I - Connection to server */
4657 void *user_data
) /* I - User data (unused) */
4659 int buffered
= 0; /* Bytes buffered but not yet sent */
4665 * If the socket still have data waiting to be sent to the printer (as can
4666 * happen if the printer runs out of paper), continue to wait until the output
4667 * buffer is empty...
4670 #ifdef SO_NWRITE /* OS X and some versions of Linux */
4671 socklen_t len
= sizeof(buffered
); /* Size of return value */
4673 if (getsockopt(httpGetFd(http
), SOL_SOCKET
, SO_NWRITE
, &buffered
, &len
))
4676 #elif defined(SIOCOUTQ) /* Others except Windows */
4677 if (ioctl(httpGetFd(http
), SIOCOUTQ
, &buffered
))
4680 #else /* Windows (not possible) */
4682 #endif /* SO_NWRITE */
4684 return (buffered
> 0);
4689 * 'usage()' - Show program usage.
4695 _cupsLangPuts(stderr
, _("Usage: ipptool [options] URI filename [ ... "
4697 _cupsLangPuts(stderr
, _("Options:"));
4698 _cupsLangPuts(stderr
, _(" -4 Connect using IPv4."));
4699 _cupsLangPuts(stderr
, _(" -6 Connect using IPv6."));
4700 _cupsLangPuts(stderr
, _(" -C Send requests using "
4701 "chunking (default)."));
4702 _cupsLangPuts(stdout
, _(" -E Test with HTTP Upgrade to "
4704 _cupsLangPuts(stderr
, _(" -I Ignore errors."));
4705 _cupsLangPuts(stderr
, _(" -L Send requests using "
4706 "content-length."));
4707 _cupsLangPuts(stderr
, _(" -S Test with SSL "
4709 _cupsLangPuts(stderr
, _(" -T seconds Set the receive/send "
4710 "timeout in seconds."));
4711 _cupsLangPuts(stderr
, _(" -V version Set default IPP "
4713 _cupsLangPuts(stderr
, _(" -X Produce XML plist instead "
4715 _cupsLangPuts(stderr
, _(" -d name=value Set named variable to "
4717 _cupsLangPuts(stderr
, _(" -f filename Set default request "
4719 _cupsLangPuts(stderr
, _(" -i seconds Repeat the last file with "
4720 "the given time interval."));
4721 _cupsLangPuts(stderr
, _(" -n count Repeat the last file the "
4722 "given number of times."));
4723 _cupsLangPuts(stderr
, _(" -q Run silently."));
4724 _cupsLangPuts(stderr
, _(" -t Produce a test report."));
4725 _cupsLangPuts(stderr
, _(" -v Be verbose."));
4732 * 'validate_attr()' - Determine whether an attribute is valid.
4735 static int /* O - 1 if valid, 0 otherwise */
4736 validate_attr(cups_array_t
*errors
, /* I - Errors array */
4737 ipp_attribute_t
*attr
) /* I - Attribute to validate */
4739 int i
; /* Looping var */
4740 char scheme
[64], /* Scheme from URI */
4741 userpass
[256], /* Username/password from URI */
4742 hostname
[256], /* Hostname from URI */
4743 resource
[1024]; /* Resource from URI */
4744 int port
, /* Port number from URI */
4745 uri_status
, /* URI separation status */
4746 valid
= 1; /* Is the attribute valid? */
4747 const char *ptr
; /* Pointer into string */
4748 ipp_attribute_t
*colattr
; /* Collection attribute */
4749 regex_t re
; /* Regular expression */
4750 ipp_uchar_t
*date
; /* Current date value */
4761 * Validate the attribute name.
4764 for (ptr
= attr
->name
; *ptr
; ptr
++)
4765 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' && *ptr
!= '_')
4768 if (*ptr
|| ptr
== attr
->name
)
4773 "\"%s\": Bad attribute name - invalid character "
4774 "(RFC 2911 section 4.1.3).", attr
->name
);
4777 if ((ptr
- attr
->name
) > 255)
4782 "\"%s\": Bad attribute name - bad length "
4783 "(RFC 2911 section 4.1.3).", attr
->name
);
4786 switch (attr
->value_tag
)
4788 case IPP_TAG_INTEGER
:
4791 case IPP_TAG_BOOLEAN
:
4792 for (i
= 0; i
< attr
->num_values
; i
++)
4794 if (attr
->values
[i
].boolean
!= 0 &&
4795 attr
->values
[i
].boolean
!= 1)
4800 "\"%s\": Bad boolen value %d "
4801 "(RFC 2911 section 4.1.11).", attr
->name
,
4802 attr
->values
[i
].boolean
);
4808 for (i
= 0; i
< attr
->num_values
; i
++)
4810 if (attr
->values
[i
].integer
< 1)
4815 "\"%s\": Bad enum value %d - out of range "
4816 "(RFC 2911 section 4.1.4).", attr
->name
,
4817 attr
->values
[i
].integer
);
4822 case IPP_TAG_STRING
:
4823 for (i
= 0; i
< attr
->num_values
; i
++)
4825 if (attr
->values
[i
].unknown
.length
> IPP_MAX_OCTETSTRING
)
4830 "\"%s\": Bad octetString value - bad length %d "
4831 "(RFC 2911 section 4.1.10).", attr
->name
,
4832 attr
->values
[i
].unknown
.length
);
4838 for (i
= 0; i
< attr
->num_values
; i
++)
4840 date
= attr
->values
[i
].date
;
4842 if (date
[2] < 1 || date
[2] > 12)
4847 "\"%s\": Bad dateTime month %u "
4848 "(RFC 2911 section 4.1.14).", attr
->name
, date
[2]);
4851 if (date
[3] < 1 || date
[3] > 31)
4856 "\"%s\": Bad dateTime day %u "
4857 "(RFC 2911 section 4.1.14).", attr
->name
, date
[3]);
4865 "\"%s\": Bad dateTime hours %u "
4866 "(RFC 2911 section 4.1.14).", attr
->name
, date
[4]);
4874 "\"%s\": Bad dateTime minutes %u "
4875 "(RFC 2911 section 4.1.14).", attr
->name
, date
[5]);
4883 "\"%s\": Bad dateTime seconds %u "
4884 "(RFC 2911 section 4.1.14).", attr
->name
, date
[6]);
4892 "\"%s\": Bad dateTime deciseconds %u "
4893 "(RFC 2911 section 4.1.14).", attr
->name
, date
[7]);
4896 if (date
[8] != '-' && date
[8] != '+')
4901 "\"%s\": Bad dateTime UTC sign '%c' "
4902 "(RFC 2911 section 4.1.14).", attr
->name
, date
[8]);
4910 "\"%s\": Bad dateTime UTC hours %u "
4911 "(RFC 2911 section 4.1.14).", attr
->name
, date
[9]);
4919 "\"%s\": Bad dateTime UTC minutes %u "
4920 "(RFC 2911 section 4.1.14).", attr
->name
, date
[10]);
4925 case IPP_TAG_RESOLUTION
:
4926 for (i
= 0; i
< attr
->num_values
; i
++)
4928 if (attr
->values
[i
].resolution
.xres
<= 0)
4933 "\"%s\": Bad resolution value %dx%d%s - cross "
4934 "feed resolution must be positive "
4935 "(RFC 2911 section 4.1.15).", attr
->name
,
4936 attr
->values
[i
].resolution
.xres
,
4937 attr
->values
[i
].resolution
.yres
,
4938 attr
->values
[i
].resolution
.units
==
4939 IPP_RES_PER_INCH
? "dpi" :
4940 attr
->values
[i
].resolution
.units
==
4941 IPP_RES_PER_CM
? "dpcm" : "unknown");
4944 if (attr
->values
[i
].resolution
.yres
<= 0)
4949 "\"%s\": Bad resolution value %dx%d%s - feed "
4950 "resolution must be positive "
4951 "(RFC 2911 section 4.1.15).", attr
->name
,
4952 attr
->values
[i
].resolution
.xres
,
4953 attr
->values
[i
].resolution
.yres
,
4954 attr
->values
[i
].resolution
.units
==
4955 IPP_RES_PER_INCH
? "dpi" :
4956 attr
->values
[i
].resolution
.units
==
4957 IPP_RES_PER_CM
? "dpcm" : "unknown");
4960 if (attr
->values
[i
].resolution
.units
!= IPP_RES_PER_INCH
&&
4961 attr
->values
[i
].resolution
.units
!= IPP_RES_PER_CM
)
4966 "\"%s\": Bad resolution value %dx%d%s - bad "
4967 "units value (RFC 2911 section 4.1.15).",
4968 attr
->name
, attr
->values
[i
].resolution
.xres
,
4969 attr
->values
[i
].resolution
.yres
,
4970 attr
->values
[i
].resolution
.units
==
4971 IPP_RES_PER_INCH
? "dpi" :
4972 attr
->values
[i
].resolution
.units
==
4973 IPP_RES_PER_CM
? "dpcm" : "unknown");
4978 case IPP_TAG_RANGE
:
4979 for (i
= 0; i
< attr
->num_values
; i
++)
4981 if (attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
4986 "\"%s\": Bad rangeOfInteger value %d-%d - lower "
4987 "greater than upper (RFC 2911 section 4.1.13).",
4988 attr
->name
, attr
->values
[i
].range
.lower
,
4989 attr
->values
[i
].range
.upper
);
4994 case IPP_TAG_BEGIN_COLLECTION
:
4995 for (i
= 0; i
< attr
->num_values
; i
++)
4997 for (colattr
= attr
->values
[i
].collection
->attrs
;
4999 colattr
= colattr
->next
)
5001 if (!validate_attr(NULL
, colattr
))
5008 if (colattr
&& errors
)
5010 add_stringf(errors
, "\"%s\": Bad collection value.", attr
->name
);
5014 validate_attr(errors
, colattr
);
5015 colattr
= colattr
->next
;
5022 case IPP_TAG_TEXTLANG
:
5023 for (i
= 0; i
< attr
->num_values
; i
++)
5025 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5027 if ((*ptr
& 0xe0) == 0xc0)
5030 if ((*ptr
& 0xc0) != 0x80)
5033 else if ((*ptr
& 0xf0) == 0xe0)
5036 if ((*ptr
& 0xc0) != 0x80)
5039 if ((*ptr
& 0xc0) != 0x80)
5042 else if ((*ptr
& 0xf8) == 0xf0)
5045 if ((*ptr
& 0xc0) != 0x80)
5048 if ((*ptr
& 0xc0) != 0x80)
5051 if ((*ptr
& 0xc0) != 0x80)
5054 else if (*ptr
& 0x80)
5063 "\"%s\": Bad text value \"%s\" - bad UTF-8 "
5064 "sequence (RFC 2911 section 4.1.1).", attr
->name
,
5065 attr
->values
[i
].string
.text
);
5068 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_TEXT
- 1))
5073 "\"%s\": Bad text value \"%s\" - bad length %d "
5074 "(RFC 2911 section 4.1.1).", attr
->name
,
5075 attr
->values
[i
].string
.text
,
5076 (int)strlen(attr
->values
[i
].string
.text
));
5082 case IPP_TAG_NAMELANG
:
5083 for (i
= 0; i
< attr
->num_values
; i
++)
5085 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5087 if ((*ptr
& 0xe0) == 0xc0)
5090 if ((*ptr
& 0xc0) != 0x80)
5093 else if ((*ptr
& 0xf0) == 0xe0)
5096 if ((*ptr
& 0xc0) != 0x80)
5099 if ((*ptr
& 0xc0) != 0x80)
5102 else if ((*ptr
& 0xf8) == 0xf0)
5105 if ((*ptr
& 0xc0) != 0x80)
5108 if ((*ptr
& 0xc0) != 0x80)
5111 if ((*ptr
& 0xc0) != 0x80)
5114 else if (*ptr
& 0x80)
5123 "\"%s\": Bad name value \"%s\" - bad UTF-8 "
5124 "sequence (RFC 2911 section 4.1.2).", attr
->name
,
5125 attr
->values
[i
].string
.text
);
5128 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_NAME
- 1))
5133 "\"%s\": Bad name value \"%s\" - bad length %d "
5134 "(RFC 2911 section 4.1.2).", attr
->name
,
5135 attr
->values
[i
].string
.text
,
5136 (int)strlen(attr
->values
[i
].string
.text
));
5141 case IPP_TAG_KEYWORD
:
5142 for (i
= 0; i
< attr
->num_values
; i
++)
5144 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5145 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' &&
5149 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5154 "\"%s\": Bad keyword value \"%s\" - invalid "
5155 "character (RFC 2911 section 4.1.3).",
5156 attr
->name
, attr
->values
[i
].string
.text
);
5159 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_KEYWORD
- 1))
5164 "\"%s\": Bad keyword value \"%s\" - bad "
5165 "length %d (RFC 2911 section 4.1.3).",
5166 attr
->name
, attr
->values
[i
].string
.text
,
5167 (int)strlen(attr
->values
[i
].string
.text
));
5173 for (i
= 0; i
< attr
->num_values
; i
++)
5175 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
,
5176 attr
->values
[i
].string
.text
,
5177 scheme
, sizeof(scheme
),
5178 userpass
, sizeof(userpass
),
5179 hostname
, sizeof(hostname
),
5180 &port
, resource
, sizeof(resource
));
5182 if (uri_status
< HTTP_URI_OK
)
5187 "\"%s\": Bad URI value \"%s\" - %s "
5188 "(RFC 2911 section 4.1.5).", attr
->name
,
5189 attr
->values
[i
].string
.text
,
5190 URIStatusStrings
[uri_status
-
5191 HTTP_URI_OVERFLOW
]);
5194 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_URI
- 1))
5199 "\"%s\": Bad URI value \"%s\" - bad length %d "
5200 "(RFC 2911 section 4.1.5).", attr
->name
,
5201 attr
->values
[i
].string
.text
,
5202 (int)strlen(attr
->values
[i
].string
.text
));
5207 case IPP_TAG_URISCHEME
:
5208 for (i
= 0; i
< attr
->num_values
; i
++)
5210 ptr
= attr
->values
[i
].string
.text
;
5211 if (islower(*ptr
& 255))
5213 for (ptr
++; *ptr
; ptr
++)
5214 if (!islower(*ptr
& 255) && !isdigit(*ptr
& 255) &&
5215 *ptr
!= '+' && *ptr
!= '-' && *ptr
!= '.')
5219 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5224 "\"%s\": Bad uriScheme value \"%s\" - bad "
5225 "characters (RFC 2911 section 4.1.6).",
5226 attr
->name
, attr
->values
[i
].string
.text
);
5229 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_URISCHEME
- 1))
5234 "\"%s\": Bad uriScheme value \"%s\" - bad "
5235 "length %d (RFC 2911 section 4.1.6).",
5236 attr
->name
, attr
->values
[i
].string
.text
,
5237 (int)strlen(attr
->values
[i
].string
.text
));
5242 case IPP_TAG_CHARSET
:
5243 for (i
= 0; i
< attr
->num_values
; i
++)
5245 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5246 if (!isprint(*ptr
& 255) || isupper(*ptr
& 255) ||
5247 isspace(*ptr
& 255))
5250 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5255 "\"%s\": Bad charset value \"%s\" - bad "
5256 "characters (RFC 2911 section 4.1.7).",
5257 attr
->name
, attr
->values
[i
].string
.text
);
5260 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_CHARSET
- 1))
5265 "\"%s\": Bad charset value \"%s\" - bad "
5266 "length %d (RFC 2911 section 4.1.7).",
5267 attr
->name
, attr
->values
[i
].string
.text
,
5268 (int)strlen(attr
->values
[i
].string
.text
));
5273 case IPP_TAG_LANGUAGE
:
5275 * The following regular expression is derived from the ABNF for
5276 * language tags in RFC 4646. All I can say is that this is the
5277 * easiest way to check the values...
5280 if ((i
= regcomp(&re
,
5282 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
5284 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
5285 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
5286 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
5287 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
5288 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
5290 "x(-[a-z0-9]{1,8})+" /* privateuse */
5292 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
5294 REG_NOSUB
| REG_EXTENDED
)) != 0)
5296 char temp
[256]; /* Temporary error string */
5298 regerror(i
, &re
, temp
, sizeof(temp
));
5299 print_fatal_error("Unable to compile naturalLanguage regular "
5300 "expression: %s.", temp
);
5304 for (i
= 0; i
< attr
->num_values
; i
++)
5306 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5311 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5312 "characters (RFC 2911 section 4.1.8).",
5313 attr
->name
, attr
->values
[i
].string
.text
);
5316 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_LANGUAGE
- 1))
5321 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5322 "length %d (RFC 2911 section 4.1.8).",
5323 attr
->name
, attr
->values
[i
].string
.text
,
5324 (int)strlen(attr
->values
[i
].string
.text
));
5331 case IPP_TAG_MIMETYPE
:
5333 * The following regular expression is derived from the ABNF for
5334 * language tags in RFC 2045 and 4288. All I can say is that this is
5335 * the easiest way to check the values...
5338 if ((i
= regcomp(&re
,
5340 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
5342 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
5343 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
5344 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
5347 REG_NOSUB
| REG_EXTENDED
)) != 0)
5349 char temp
[256]; /* Temporary error string */
5351 regerror(i
, &re
, temp
, sizeof(temp
));
5352 print_fatal_error("Unable to compile mimeMediaType regular "
5353 "expression: %s.", temp
);
5357 for (i
= 0; i
< attr
->num_values
; i
++)
5359 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5364 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5365 "characters (RFC 2911 section 4.1.9).",
5366 attr
->name
, attr
->values
[i
].string
.text
);
5369 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_MIMETYPE
- 1))
5374 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5375 "length %d (RFC 2911 section 4.1.9).",
5376 attr
->name
, attr
->values
[i
].string
.text
,
5377 (int)strlen(attr
->values
[i
].string
.text
));
5393 * 'with_value()' - Test a WITH-VALUE predicate.
5396 static int /* O - 1 on match, 0 on non-match */
5397 with_value(cups_array_t
*errors
, /* I - Errors array */
5398 char *value
, /* I - Value string */
5399 int flags
, /* I - Flags for match */
5400 ipp_attribute_t
*attr
, /* I - Attribute to compare */
5401 char *matchbuf
, /* I - Buffer to hold matching value */
5402 size_t matchlen
) /* I - Length of match buffer */
5404 int i
, /* Looping var */
5406 char temp
[1024], /* Temporary value string */
5407 *valptr
; /* Pointer into value */
5411 match
= (flags
& _CUPS_WITH_ALL
) ? 1 : 0;
5414 * NULL matches everything.
5417 if (!value
|| !*value
)
5421 * Compare the value string to the attribute value.
5424 switch (attr
->value_tag
)
5426 case IPP_TAG_INTEGER
:
5428 for (i
= 0; i
< attr
->num_values
; i
++)
5430 char op
, /* Comparison operator */
5431 *nextptr
; /* Next pointer */
5432 int intvalue
, /* Integer value */
5433 valmatch
= 0; /* Does the current value match? */
5437 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5438 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5439 *valptr
== '=' || *valptr
== '>')
5442 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5444 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5452 intvalue
= strtol(valptr
, &nextptr
, 0);
5453 if (nextptr
== valptr
)
5457 if ((op
== '=' && attr
->values
[i
].integer
== intvalue
) ||
5458 (op
== '<' && attr
->values
[i
].integer
< intvalue
) ||
5459 (op
== '>' && attr
->values
[i
].integer
> intvalue
))
5462 snprintf(matchbuf
, matchlen
, "%d",
5463 attr
->values
[i
].integer
);
5470 if (flags
& _CUPS_WITH_ALL
)
5485 if (!match
&& errors
)
5487 for (i
= 0; i
< attr
->num_values
; i
++)
5488 add_stringf(errors
, "GOT: %s=%d", attr
->name
,
5489 attr
->values
[i
].integer
);
5493 case IPP_TAG_RANGE
:
5494 for (i
= 0; i
< attr
->num_values
; i
++)
5496 char op
, /* Comparison operator */
5497 *nextptr
; /* Next pointer */
5498 int intvalue
, /* Integer value */
5499 valmatch
= 0; /* Does the current value match? */
5503 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5504 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5505 *valptr
== '=' || *valptr
== '>')
5508 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5510 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5518 intvalue
= strtol(valptr
, &nextptr
, 0);
5519 if (nextptr
== valptr
)
5523 if ((op
== '=' && (attr
->values
[i
].range
.lower
== intvalue
||
5524 attr
->values
[i
].range
.upper
== intvalue
)) ||
5525 (op
== '<' && attr
->values
[i
].range
.upper
< intvalue
) ||
5526 (op
== '>' && attr
->values
[i
].range
.upper
> intvalue
))
5529 snprintf(matchbuf
, matchlen
, "%d-%d",
5530 attr
->values
[0].range
.lower
,
5531 attr
->values
[0].range
.upper
);
5538 if (flags
& _CUPS_WITH_ALL
)
5553 if (!match
&& errors
)
5555 for (i
= 0; i
< attr
->num_values
; i
++)
5556 add_stringf(errors
, "GOT: %s=%d-%d", attr
->name
,
5557 attr
->values
[i
].range
.lower
,
5558 attr
->values
[i
].range
.upper
);
5562 case IPP_TAG_BOOLEAN
:
5563 for (i
= 0; i
< attr
->num_values
; i
++)
5565 if (!strcmp(value
, "true") == attr
->values
[i
].boolean
)
5568 strlcpy(matchbuf
, value
, matchlen
);
5570 if (!(flags
& _CUPS_WITH_ALL
))
5576 else if (flags
& _CUPS_WITH_ALL
)
5583 if (!match
&& errors
)
5585 for (i
= 0; i
< attr
->num_values
; i
++)
5586 add_stringf(errors
, "GOT: %s=%s", attr
->name
,
5587 attr
->values
[i
].boolean
? "true" : "false");
5591 case IPP_TAG_RESOLUTION
:
5592 for (i
= 0; i
< attr
->num_values
; i
++)
5594 if (attr
->values
[i
].resolution
.xres
==
5595 attr
->values
[i
].resolution
.yres
)
5596 snprintf(temp
, sizeof(temp
), "%d%s",
5597 attr
->values
[i
].resolution
.xres
,
5598 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5601 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5602 attr
->values
[i
].resolution
.xres
,
5603 attr
->values
[i
].resolution
.yres
,
5604 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5607 if (!strcmp(value
, temp
))
5610 strlcpy(matchbuf
, value
, matchlen
);
5612 if (!(flags
& _CUPS_WITH_ALL
))
5618 else if (flags
& _CUPS_WITH_ALL
)
5625 if (!match
&& errors
)
5627 for (i
= 0; i
< attr
->num_values
; i
++)
5629 if (attr
->values
[i
].resolution
.xres
==
5630 attr
->values
[i
].resolution
.yres
)
5631 snprintf(temp
, sizeof(temp
), "%d%s",
5632 attr
->values
[i
].resolution
.xres
,
5633 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5636 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5637 attr
->values
[i
].resolution
.xres
,
5638 attr
->values
[i
].resolution
.yres
,
5639 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5642 if (strcmp(value
, temp
))
5643 add_stringf(errors
, "GOT: %s=%s", attr
->name
, temp
);
5648 case IPP_TAG_NOVALUE
:
5649 case IPP_TAG_UNKNOWN
:
5652 case IPP_TAG_CHARSET
:
5653 case IPP_TAG_KEYWORD
:
5654 case IPP_TAG_LANGUAGE
:
5655 case IPP_TAG_MIMETYPE
:
5657 case IPP_TAG_NAMELANG
:
5659 case IPP_TAG_TEXTLANG
:
5661 case IPP_TAG_URISCHEME
:
5662 if (flags
& _CUPS_WITH_REGEX
)
5665 * Value is an extended, case-sensitive POSIX regular expression...
5668 regex_t re
; /* Regular expression */
5670 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
5672 regerror(i
, &re
, temp
, sizeof(temp
));
5674 print_fatal_error("Unable to compile WITH-VALUE regular expression "
5675 "\"%s\" - %s", value
, temp
);
5680 * See if ALL of the values match the given regular expression.
5683 for (i
= 0; i
< attr
->num_values
; i
++)
5685 if (!regexec(&re
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5690 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5693 if (!(flags
& _CUPS_WITH_ALL
))
5699 else if (flags
& _CUPS_WITH_ALL
)
5711 * Value is a literal string, see if the value(s) match...
5714 for (i
= 0; i
< attr
->num_values
; i
++)
5716 if (!strcmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
))))
5720 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5723 if (!(flags
& _CUPS_WITH_ALL
))
5729 else if (flags
& _CUPS_WITH_ALL
)
5737 if (!match
&& errors
)
5739 for (i
= 0; i
< attr
->num_values
; i
++)
5740 add_stringf(errors
, "GOT: %s=\"%s\"", attr
->name
,
5741 attr
->values
[i
].string
.text
);