4 * ipptool command for CUPS.
6 * Copyright 2007-2014 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 static _cups_transfer_t Transfer
= _CUPS_TRANSFER_AUTO
;
127 /* How to transfer requests */
128 static _cups_output_t Output
= _CUPS_OUTPUT_LIST
;
130 static 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 static char *Username
= NULL
, /* Username from URI */
142 *Password
= NULL
; /* Password from URI */
143 static int PasswordTries
= 0; /* Number of tries with password */
150 static void add_stringf(cups_array_t
*a
, const char *s
, ...)
151 __attribute__ ((__format__ (__printf__
, 2, 3)));
152 static int compare_vars(_cups_var_t
*a
, _cups_var_t
*b
);
153 static int do_tests(_cups_vars_t
*vars
, const char *testfile
);
154 static void expand_variables(_cups_vars_t
*vars
, char *dst
, const char *src
,
155 size_t dstsize
) __attribute__((nonnull(1,2,3)));
156 static int expect_matches(_cups_expect_t
*expect
, ipp_tag_t value_tag
);
157 static ipp_t
*get_collection(_cups_vars_t
*vars
, FILE *fp
, int *linenum
);
158 static char *get_filename(const char *testfile
, char *dst
, const char *src
,
160 static char *get_string(ipp_attribute_t
*attr
, int element
, int flags
,
161 char *buffer
, size_t bufsize
);
162 static char *get_token(FILE *fp
, char *buf
, int buflen
,
164 static char *get_variable(_cups_vars_t
*vars
, const char *name
);
165 static char *iso_date(ipp_uchar_t
*date
);
166 static const char *password_cb(const char *prompt
);
167 static void print_attr(ipp_attribute_t
*attr
, ipp_tag_t
*group
);
168 static void print_col(ipp_t
*col
);
169 static void print_csv(ipp_attribute_t
*attr
, int num_displayed
,
170 char **displayed
, size_t *widths
);
171 static void print_fatal_error(const char *s
, ...)
172 __attribute__ ((__format__ (__printf__
, 1, 2)));
173 static void print_line(ipp_attribute_t
*attr
, int num_displayed
,
174 char **displayed
, size_t *widths
);
175 static void print_xml_header(void);
176 static void print_xml_string(const char *element
, const char *s
);
177 static void print_xml_trailer(int success
, const char *message
);
178 static void set_variable(_cups_vars_t
*vars
, const char *name
,
181 static void sigterm_handler(int sig
);
183 static int timeout_cb(http_t
*http
, void *user_data
);
184 static void usage(void) __attribute__((noreturn
));
185 static int validate_attr(cups_array_t
*errors
, ipp_attribute_t
*attr
);
186 static int with_value(cups_array_t
*errors
, char *value
, int flags
,
187 ipp_attribute_t
*attr
, char *matchbuf
,
192 * 'main()' - Parse options and do tests.
195 int /* O - Exit status */
196 main(int argc
, /* I - Number of command-line args */
197 char *argv
[]) /* I - Command-line arguments */
199 int i
; /* Looping var */
200 int status
; /* Status of tests... */
201 char *opt
, /* Current option */
202 name
[1024], /* Name/value buffer */
203 *value
, /* Pointer to value */
204 filename
[1024], /* Real filename */
205 testname
[1024], /* Real test filename */
206 uri
[1024]; /* Copy of printer URI */
207 const char *ext
, /* Extension on filename */
208 *testfile
; /* Test file to use */
209 int interval
, /* Test interval in microseconds */
210 repeat
; /* Repeat count */
211 _cups_vars_t vars
; /* Variables */
212 http_uri_status_t uri_status
; /* URI separation status */
213 _cups_globals_t
*cg
= _cupsGlobals();
219 * Catch SIGINT and SIGTERM...
222 signal(SIGINT
, sigterm_handler
);
223 signal(SIGTERM
, sigterm_handler
);
227 * Initialize the locale and variables...
230 _cupsSetLocale(argv
);
232 memset(&vars
, 0, sizeof(vars
));
233 vars
.family
= AF_UNSPEC
;
234 vars
.vars
= cupsArrayNew((cups_array_func_t
)compare_vars
, NULL
);
239 * ipptool URI testfile
247 for (i
= 1; i
< argc
; i
++)
249 if (!strcmp(argv
[i
], "--help"))
253 else if (!strcmp(argv
[i
], "--stop-after-include-error"))
255 StopAfterIncludeError
= 1;
257 else if (!strcmp(argv
[i
], "--version"))
262 else if (argv
[i
][0] == '-')
264 for (opt
= argv
[i
] + 1; *opt
; opt
++)
268 case '4' : /* Connect using IPv4 only */
269 vars
.family
= AF_INET
;
273 case '6' : /* Connect using IPv6 only */
274 vars
.family
= AF_INET6
;
276 #endif /* AF_INET6 */
278 case 'C' : /* Enable HTTP chunking */
279 Transfer
= _CUPS_TRANSFER_CHUNKED
;
282 case 'E' : /* Encrypt with TLS */
284 vars
.encryption
= HTTP_ENCRYPT_REQUIRED
;
286 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
288 #endif /* HAVE_SSL */
291 case 'I' : /* Ignore errors */
295 case 'L' : /* Disable HTTP chunking */
296 Transfer
= _CUPS_TRANSFER_LENGTH
;
299 case 'S' : /* Encrypt with SSL */
301 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
303 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
305 #endif /* HAVE_SSL */
308 case 'T' : /* Set timeout */
313 _cupsLangPrintf(stderr
,
314 _("%s: Missing timeout for \"-T\"."),
319 vars
.timeout
= _cupsStrScand(argv
[i
], NULL
, localeconv());
322 case 'V' : /* Set IPP version */
327 _cupsLangPrintf(stderr
,
328 _("%s: Missing version for \"-V\"."),
333 if (!strcmp(argv
[i
], "1.0"))
335 else if (!strcmp(argv
[i
], "1.1"))
337 else if (!strcmp(argv
[i
], "2.0"))
339 else if (!strcmp(argv
[i
], "2.1"))
341 else if (!strcmp(argv
[i
], "2.2"))
345 _cupsLangPrintf(stderr
,
346 _("%s: Bad version %s for \"-V\"."),
352 case 'X' : /* Produce XML output */
353 Output
= _CUPS_OUTPUT_PLIST
;
355 if (interval
|| repeat
)
357 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are "
358 "incompatible with -X\"."));
363 case 'c' : /* CSV output */
364 Output
= _CUPS_OUTPUT_CSV
;
367 case 'd' : /* Define a variable */
372 _cupsLangPuts(stderr
,
373 _("ipptool: Missing name=value for \"-d\"."));
377 strlcpy(name
, argv
[i
], sizeof(name
));
378 if ((value
= strchr(name
, '=')) != NULL
)
381 value
= name
+ strlen(name
);
383 set_variable(&vars
, name
, value
);
386 case 'f' : /* Set the default test filename */
391 _cupsLangPuts(stderr
,
392 _("ipptool: Missing filename for \"-f\"."));
399 vars
.filename
= NULL
;
402 if (access(argv
[i
], 0))
408 snprintf(filename
, sizeof(filename
), "%s.gz", argv
[i
]);
409 if (access(filename
, 0) && filename
[0] != '/'
411 && (!isalpha(filename
[0] & 255) || filename
[1] != ':')
415 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s",
416 cg
->cups_datadir
, argv
[i
]);
417 if (access(filename
, 0))
419 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s.gz",
420 cg
->cups_datadir
, argv
[i
]);
421 if (access(filename
, 0))
422 vars
.filename
= strdup(argv
[i
]);
424 vars
.filename
= strdup(filename
);
427 vars
.filename
= strdup(filename
);
430 vars
.filename
= strdup(filename
);
433 vars
.filename
= strdup(argv
[i
]);
435 if ((ext
= strrchr(vars
.filename
, '.')) != NULL
)
438 * Guess the MIME media type based on the extension...
441 if (!_cups_strcasecmp(ext
, ".gif"))
442 set_variable(&vars
, "filetype", "image/gif");
443 else if (!_cups_strcasecmp(ext
, ".htm") ||
444 !_cups_strcasecmp(ext
, ".htm.gz") ||
445 !_cups_strcasecmp(ext
, ".html") ||
446 !_cups_strcasecmp(ext
, ".html.gz"))
447 set_variable(&vars
, "filetype", "text/html");
448 else if (!_cups_strcasecmp(ext
, ".jpg"))
449 set_variable(&vars
, "filetype", "image/jpeg");
450 else if (!_cups_strcasecmp(ext
, ".pcl") ||
451 !_cups_strcasecmp(ext
, ".pcl.gz"))
452 set_variable(&vars
, "filetype", "application/vnd.hp-PCL");
453 else if (!_cups_strcasecmp(ext
, ".pdf"))
454 set_variable(&vars
, "filetype", "application/pdf");
455 else if (!_cups_strcasecmp(ext
, ".png"))
456 set_variable(&vars
, "filetype", "image/png");
457 else if (!_cups_strcasecmp(ext
, ".ps") ||
458 !_cups_strcasecmp(ext
, ".ps.gz"))
459 set_variable(&vars
, "filetype", "application/postscript");
460 else if (!_cups_strcasecmp(ext
, ".pwg") ||
461 !_cups_strcasecmp(ext
, ".pwg.gz") ||
462 !_cups_strcasecmp(ext
, ".ras") ||
463 !_cups_strcasecmp(ext
, ".ras.gz"))
464 set_variable(&vars
, "filetype", "image/pwg-raster");
465 else if (!_cups_strcasecmp(ext
, ".txt") ||
466 !_cups_strcasecmp(ext
, ".txt.gz"))
467 set_variable(&vars
, "filetype", "text/plain");
468 else if (!_cups_strcasecmp(ext
, ".xps"))
469 set_variable(&vars
, "filetype", "application/openxps");
471 set_variable(&vars
, "filetype", "application/octet-stream");
476 * Use the "auto-type" MIME media type...
479 set_variable(&vars
, "filetype", "application/octet-stream");
483 case 'i' : /* Test every N seconds */
488 _cupsLangPuts(stderr
,
489 _("ipptool: Missing seconds for \"-i\"."));
494 interval
= (int)(_cupsStrScand(argv
[i
], NULL
, localeconv()) *
498 _cupsLangPuts(stderr
,
499 _("ipptool: Invalid seconds for \"-i\"."));
504 if (Output
== _CUPS_OUTPUT_PLIST
&& interval
)
506 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are "
507 "incompatible with -X\"."));
512 case 'l' : /* List as a table */
513 Output
= _CUPS_OUTPUT_LIST
;
516 case 'n' : /* Repeat count */
521 _cupsLangPuts(stderr
,
522 _("ipptool: Missing count for \"-n\"."));
526 repeat
= atoi(argv
[i
]);
528 if (Output
== _CUPS_OUTPUT_PLIST
&& repeat
)
530 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are "
531 "incompatible with -X\"."));
536 case 'q' : /* Be quiet */
537 Output
= _CUPS_OUTPUT_QUIET
;
540 case 't' : /* CUPS test output */
541 Output
= _CUPS_OUTPUT_TEST
;
544 case 'v' : /* Be verbose */
549 _cupsLangPrintf(stderr
, _("ipptool: Unknown option \"-%c\"."),
555 else if (!strncmp(argv
[i
], "ipp://", 6) || !strncmp(argv
[i
], "http://", 7)
557 || !strncmp(argv
[i
], "ipps://", 7)
558 || !strncmp(argv
[i
], "https://", 8)
559 #endif /* HAVE_SSL */
568 _cupsLangPuts(stderr
, _("ipptool: May only specify a single URI."));
573 if (!strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8))
574 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
575 #endif /* HAVE_SSL */
578 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, vars
.uri
,
579 vars
.scheme
, sizeof(vars
.scheme
),
580 vars
.userpass
, sizeof(vars
.userpass
),
581 vars
.hostname
, sizeof(vars
.hostname
),
583 vars
.resource
, sizeof(vars
.resource
));
585 if (uri_status
!= HTTP_URI_OK
)
587 _cupsLangPrintf(stderr
, _("ipptool: Bad URI - %s."), httpURIStatusString(uri_status
));
591 if (vars
.userpass
[0])
593 if ((Password
= strchr(vars
.userpass
, ':')) != NULL
)
596 Username
= vars
.userpass
;
597 cupsSetPasswordCB(password_cb
);
598 set_variable(&vars
, "uriuser", vars
.userpass
);
601 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), vars
.scheme
, NULL
,
602 vars
.hostname
, vars
.port
, vars
.resource
);
613 _cupsLangPuts(stderr
, _("ipptool: URI required before test file."));
617 if (access(argv
[i
], 0) && argv
[i
][0] != '/'
619 && (!isalpha(argv
[i
][0] & 255) || argv
[i
][1] != ':')
623 snprintf(testname
, sizeof(testname
), "%s/ipptool/%s", cg
->cups_datadir
,
625 if (access(testname
, 0))
633 if (!do_tests(&vars
, testfile
))
638 if (!vars
.uri
|| !testfile
)
642 * Loop if the interval is set...
645 if (Output
== _CUPS_OUTPUT_PLIST
)
646 print_xml_trailer(!status
, NULL
);
647 else if (interval
> 0 && repeat
> 0)
651 usleep((useconds_t
)interval
);
652 do_tests(&vars
, testfile
);
656 else if (interval
> 0)
660 usleep((useconds_t
)interval
);
661 do_tests(&vars
, testfile
);
664 else if (Output
== _CUPS_OUTPUT_TEST
&& TestCount
> 1)
667 * Show a summary report if there were multiple tests...
670 printf("\nSummary: %d tests, %d passed, %d failed, %d skipped\n"
671 "Score: %d%%\n", TestCount
, PassCount
, FailCount
, SkipCount
,
672 100 * (PassCount
+ SkipCount
) / TestCount
);
684 * 'add_stringf()' - Add a formatted string to an array.
688 add_stringf(cups_array_t
*a
, /* I - Array */
689 const char *s
, /* I - Printf-style format string */
690 ...) /* I - Additional args as needed */
692 char buffer
[10240]; /* Format buffer */
693 va_list ap
; /* Argument pointer */
697 * Don't bother is the array is NULL...
704 * Format the message...
708 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
712 * Add it to the array...
715 cupsArrayAdd(a
, buffer
);
720 * 'compare_vars()' - Compare two variables.
723 static int /* O - Result of comparison */
724 compare_vars(_cups_var_t
*a
, /* I - First variable */
725 _cups_var_t
*b
) /* I - Second variable */
727 return (_cups_strcasecmp(a
->name
, b
->name
));
732 * 'do_tests()' - Do tests as specified in the test file.
735 static int /* 1 = success, 0 = failure */
736 do_tests(_cups_vars_t
*vars
, /* I - Variables */
737 const char *testfile
) /* I - Test file to use */
739 int i
, /* Looping var */
740 linenum
, /* Current line number */
741 pass
, /* Did we pass the test? */
742 prev_pass
= 1, /* Did we pass the previous test? */
743 request_id
, /* Current request ID */
744 show_header
= 1, /* Show the test header? */
745 ignore_errors
, /* Ignore test failures? */
746 skip_previous
= 0, /* Skip on previous test failure? */
747 repeat_count
, /* Repeat count */
748 repeat_interval
, /* Repeat interval */
749 repeat_prev
, /* Previous repeat interval */
750 repeat_test
; /* Repeat a test? */
751 http_t
*http
= NULL
; /* HTTP connection to server */
752 FILE *fp
= NULL
; /* Test file */
753 char resource
[512], /* Resource for request */
754 token
[1024], /* Token from file */
755 *tokenptr
, /* Pointer into token */
756 temp
[1024], /* Temporary string */
757 buffer
[8192], /* Copy buffer */
758 compression
[16]; /* COMPRESSION value */
759 ipp_t
*request
= NULL
, /* IPP request */
760 *response
= NULL
; /* IPP response */
761 size_t length
; /* Length of IPP request */
762 http_status_t status
; /* HTTP status */
763 cups_file_t
*reqfile
; /* File to send */
764 ssize_t bytes
; /* Bytes read/written */
765 char attr
[128]; /* Attribute name */
766 ipp_op_t op
; /* Operation */
767 ipp_tag_t group
; /* Current group */
768 ipp_tag_t value
; /* Current value type */
769 ipp_attribute_t
*attrptr
, /* Attribute pointer */
770 *found
, /* Found attribute */
771 *lastcol
= NULL
; /* Last collection attribute */
772 char name
[1024], /* Name of test */
773 file_id
[1024], /* File identifier */
774 test_id
[1024]; /* Test identifier */
775 char filename
[1024]; /* Filename */
776 _cups_transfer_t transfer
; /* To chunk or not to chunk */
777 int version
, /* IPP version number to use */
778 skip_test
; /* Skip this test? */
779 int num_statuses
= 0; /* Number of valid status codes */
780 _cups_status_t statuses
[100], /* Valid status codes */
781 *last_status
; /* Last STATUS (for predicates) */
782 int num_expects
= 0; /* Number of expected attributes */
783 _cups_expect_t expects
[200], /* Expected attributes */
784 *expect
, /* Current expected attribute */
785 *last_expect
; /* Last EXPECT (for predicates) */
786 int num_displayed
= 0; /* Number of displayed attributes */
787 char *displayed
[200]; /* Displayed attributes */
788 size_t widths
[200]; /* Width of columns */
789 cups_array_t
*a
, /* Duplicate attribute array */
790 *errors
= NULL
; /* Errors array */
791 const char *error
; /* Current error */
795 * Open the test file...
798 if ((fp
= fopen(testfile
, "r")) == NULL
)
800 print_fatal_error("Unable to open test file %s - %s", testfile
,
807 * Connect to the server...
810 if ((http
= httpConnect2(vars
->hostname
, vars
->port
, NULL
, vars
->family
,
811 vars
->encryption
, 1, 30000, NULL
)) == NULL
)
813 print_fatal_error("Unable to connect to %s on port %d - %s", vars
->hostname
,
814 vars
->port
, cupsLastErrorString());
820 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
,
821 "deflate, gzip, identity");
823 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
, "identity");
824 #endif /* HAVE_LIBZ */
826 if (vars
->timeout
> 0.0)
827 httpSetTimeout(http
, vars
->timeout
, timeout_cb
, NULL
);
833 CUPS_SRAND(time(NULL
));
835 errors
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
,
836 (cups_afree_func_t
)free
);
840 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
842 while (!Cancel
&& get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
845 * Expect an open brace...
848 if (!strcmp(token
, "DEFINE"))
854 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
855 get_token(fp
, temp
, sizeof(temp
), &linenum
))
857 expand_variables(vars
, token
, temp
, sizeof(token
));
858 set_variable(vars
, attr
, token
);
862 print_fatal_error("Missing DEFINE name and/or value on line %d.",
870 else if (!strcmp(token
, "DEFINE-DEFAULT"))
873 * DEFINE-DEFAULT name value
876 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
877 get_token(fp
, temp
, sizeof(temp
), &linenum
))
879 expand_variables(vars
, token
, temp
, sizeof(token
));
880 if (!get_variable(vars
, attr
))
881 set_variable(vars
, attr
, token
);
885 print_fatal_error("Missing DEFINE-DEFAULT name and/or value on line "
893 else if (!strcmp(token
, "FILE-ID"))
899 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
901 expand_variables(vars
, file_id
, temp
, sizeof(file_id
));
905 print_fatal_error("Missing FILE-ID value on line %d.", linenum
);
912 else if (!strcmp(token
, "IGNORE-ERRORS"))
919 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
920 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
922 IgnoreErrors
= !_cups_strcasecmp(temp
, "yes");
926 print_fatal_error("Missing IGNORE-ERRORS value on line %d.", linenum
);
933 else if (!strcmp(token
, "INCLUDE"))
940 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
943 * Map the filename to and then run the tests...
946 if (!do_tests(vars
, get_filename(testfile
, filename
, temp
,
951 if (StopAfterIncludeError
)
957 print_fatal_error("Missing INCLUDE filename on line %d.", linenum
);
965 else if (!strcmp(token
, "INCLUDE-IF-DEFINED"))
968 * INCLUDE-IF-DEFINED name "filename"
969 * INCLUDE-IF-DEFINED name <filename>
972 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
973 get_token(fp
, temp
, sizeof(temp
), &linenum
))
976 * Map the filename to and then run the tests...
979 if (get_variable(vars
, attr
) &&
980 !do_tests(vars
, get_filename(testfile
, filename
, temp
,
985 if (StopAfterIncludeError
)
991 print_fatal_error("Missing INCLUDE-IF-DEFINED name or filename on line "
1000 else if (!strcmp(token
, "INCLUDE-IF-NOT-DEFINED"))
1003 * INCLUDE-IF-NOT-DEFINED name "filename"
1004 * INCLUDE-IF-NOT-DEFINED name <filename>
1007 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1008 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1011 * Map the filename to and then run the tests...
1014 if (!get_variable(vars
, attr
) &&
1015 !do_tests(vars
, get_filename(testfile
, filename
, temp
,
1020 if (StopAfterIncludeError
)
1026 print_fatal_error("Missing INCLUDE-IF-NOT-DEFINED name or filename on "
1027 "line %d.", linenum
);
1035 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1038 * SKIP-IF-DEFINED variable
1041 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1043 if (get_variable(vars
, temp
))
1048 print_fatal_error("Missing SKIP-IF-DEFINED variable on line %d.",
1054 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1057 * SKIP-IF-NOT-DEFINED variable
1060 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1062 if (!get_variable(vars
, temp
))
1067 print_fatal_error("Missing SKIP-IF-NOT-DEFINED variable on line %d.",
1073 else if (!strcmp(token
, "STOP-AFTER-INCLUDE-ERROR"))
1076 * STOP-AFTER-INCLUDE-ERROR yes
1077 * STOP-AFTER-INCLUDE-ERROR no
1080 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1081 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1083 StopAfterIncludeError
= !_cups_strcasecmp(temp
, "yes");
1087 print_fatal_error("Missing STOP-AFTER-INCLUDE-ERROR value on line %d.",
1095 else if (!strcmp(token
, "TRANSFER"))
1103 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1105 if (!strcmp(temp
, "auto"))
1106 Transfer
= _CUPS_TRANSFER_AUTO
;
1107 else if (!strcmp(temp
, "chunked"))
1108 Transfer
= _CUPS_TRANSFER_CHUNKED
;
1109 else if (!strcmp(temp
, "length"))
1110 Transfer
= _CUPS_TRANSFER_LENGTH
;
1113 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp
,
1121 print_fatal_error("Missing TRANSFER value on line %d.", linenum
);
1128 else if (!strcmp(token
, "VERSION"))
1130 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1132 if (!strcmp(temp
, "1.0"))
1134 else if (!strcmp(temp
, "1.1"))
1136 else if (!strcmp(temp
, "2.0"))
1138 else if (!strcmp(temp
, "2.1"))
1140 else if (!strcmp(temp
, "2.2"))
1144 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1151 print_fatal_error("Missing VERSION number on line %d.", linenum
);
1158 else if (strcmp(token
, "{"))
1160 print_fatal_error("Unexpected token %s seen on line %d.", token
, linenum
);
1166 * Initialize things...
1171 if (Output
== _CUPS_OUTPUT_PLIST
)
1173 else if (Output
== _CUPS_OUTPUT_TEST
)
1174 printf("\"%s\":\n", testfile
);
1179 strlcpy(resource
, vars
->resource
, sizeof(resource
));
1184 group
= IPP_TAG_ZERO
;
1185 ignore_errors
= IgnoreErrors
;
1193 transfer
= Transfer
;
1194 compression
[0] = '\0';
1196 strlcpy(name
, testfile
, sizeof(name
));
1197 if (strrchr(name
, '.') != NULL
)
1198 *strrchr(name
, '.') = '\0';
1201 * Parse until we see a close brace...
1204 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
1206 if (_cups_strcasecmp(token
, "COUNT") &&
1207 _cups_strcasecmp(token
, "DEFINE-MATCH") &&
1208 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1209 _cups_strcasecmp(token
, "DEFINE-VALUE") &&
1210 _cups_strcasecmp(token
, "IF-DEFINED") &&
1211 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1212 _cups_strcasecmp(token
, "IN-GROUP") &&
1213 _cups_strcasecmp(token
, "OF-TYPE") &&
1214 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1215 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1216 _cups_strcasecmp(token
, "REPEAT-NO-MATCH") &&
1217 _cups_strcasecmp(token
, "SAME-COUNT-AS") &&
1218 _cups_strcasecmp(token
, "WITH-ALL-VALUES") &&
1219 _cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") &&
1220 _cups_strcasecmp(token
, "WITH-ALL-RESOURCES") &&
1221 _cups_strcasecmp(token
, "WITH-ALL-SCHEMES") &&
1222 _cups_strcasecmp(token
, "WITH-HOSTNAME") &&
1223 _cups_strcasecmp(token
, "WITH-RESOURCE") &&
1224 _cups_strcasecmp(token
, "WITH-SCHEME") &&
1225 _cups_strcasecmp(token
, "WITH-VALUE"))
1228 if (_cups_strcasecmp(token
, "DEFINE-MATCH") &&
1229 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1230 _cups_strcasecmp(token
, "IF-DEFINED") &&
1231 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1232 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1233 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1234 _cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
1237 if (!strcmp(token
, "}"))
1239 else if (!strcmp(token
, "{") && lastcol
)
1242 * Another collection value
1245 ipp_t
*col
= get_collection(vars
, fp
, &linenum
);
1246 /* Collection value */
1250 ippSetCollection(request
, &lastcol
, ippGetCount(lastcol
), col
);
1258 else if (!strcmp(token
, "COMPRESSION"))
1262 * COMPRESSION deflate
1266 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1268 expand_variables(vars
, compression
, temp
, sizeof(compression
));
1270 if (strcmp(compression
, "none") && strcmp(compression
, "deflate") &&
1271 strcmp(compression
, "gzip"))
1273 if (strcmp(compression
, "none"))
1274 #endif /* HAVE_LIBZ */
1276 print_fatal_error("Unsupported COMPRESSION value '%s' on line %d.",
1277 compression
, linenum
);
1282 if (!strcmp(compression
, "none"))
1283 compression
[0] = '\0';
1287 print_fatal_error("Missing COMPRESSION value on line %d.", linenum
);
1292 else if (!strcmp(token
, "DEFINE"))
1298 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1299 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1301 expand_variables(vars
, token
, temp
, sizeof(token
));
1302 set_variable(vars
, attr
, token
);
1306 print_fatal_error("Missing DEFINE name and/or value on line %d.",
1312 else if (!strcmp(token
, "IGNORE-ERRORS"))
1319 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1320 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1322 ignore_errors
= !_cups_strcasecmp(temp
, "yes");
1326 print_fatal_error("Missing IGNORE-ERRORS value on line %d.", linenum
);
1333 else if (!_cups_strcasecmp(token
, "NAME"))
1339 get_token(fp
, name
, sizeof(name
), &linenum
);
1341 else if (!strcmp(token
, "REQUEST-ID"))
1348 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1350 if (isdigit(temp
[0] & 255))
1351 request_id
= atoi(temp
);
1352 else if (!_cups_strcasecmp(temp
, "random"))
1353 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
1356 print_fatal_error("Bad REQUEST-ID value \"%s\" on line %d.", temp
,
1364 print_fatal_error("Missing REQUEST-ID value on line %d.", linenum
);
1369 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1372 * SKIP-IF-DEFINED variable
1375 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1377 if (get_variable(vars
, temp
))
1382 print_fatal_error("Missing SKIP-IF-DEFINED value on line %d.",
1388 else if (!strcmp(token
, "SKIP-IF-MISSING"))
1391 * SKIP-IF-MISSING filename
1394 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1396 expand_variables(vars
, token
, temp
, sizeof(token
));
1397 get_filename(testfile
, filename
, token
, sizeof(filename
));
1399 if (access(filename
, R_OK
))
1404 print_fatal_error("Missing SKIP-IF-MISSING filename on line %d.",
1410 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1413 * SKIP-IF-NOT-DEFINED variable
1416 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1418 if (!get_variable(vars
, temp
))
1423 print_fatal_error("Missing SKIP-IF-NOT-DEFINED value on line %d.",
1429 else if (!strcmp(token
, "SKIP-PREVIOUS-ERROR"))
1432 * SKIP-PREVIOUS-ERROR yes
1433 * SKIP-PREVIOUS-ERROR no
1436 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1437 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1439 skip_previous
= !_cups_strcasecmp(temp
, "yes");
1443 print_fatal_error("Missing SKIP-PREVIOUS-ERROR value on line %d.", linenum
);
1450 else if (!strcmp(token
, "TEST-ID"))
1456 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1458 expand_variables(vars
, test_id
, temp
, sizeof(test_id
));
1462 print_fatal_error("Missing TEST-ID value on line %d.", linenum
);
1469 else if (!strcmp(token
, "TRANSFER"))
1477 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1479 if (!strcmp(temp
, "auto"))
1480 transfer
= _CUPS_TRANSFER_AUTO
;
1481 else if (!strcmp(temp
, "chunked"))
1482 transfer
= _CUPS_TRANSFER_CHUNKED
;
1483 else if (!strcmp(temp
, "length"))
1484 transfer
= _CUPS_TRANSFER_LENGTH
;
1487 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp
,
1495 print_fatal_error("Missing TRANSFER value on line %d.", linenum
);
1500 else if (!_cups_strcasecmp(token
, "VERSION"))
1502 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1504 if (!strcmp(temp
, "0.0"))
1506 else if (!strcmp(temp
, "1.0"))
1508 else if (!strcmp(temp
, "1.1"))
1510 else if (!strcmp(temp
, "2.0"))
1512 else if (!strcmp(temp
, "2.1"))
1514 else if (!strcmp(temp
, "2.2"))
1518 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1525 print_fatal_error("Missing VERSION number on line %d.", linenum
);
1530 else if (!_cups_strcasecmp(token
, "RESOURCE"))
1536 if (!get_token(fp
, resource
, sizeof(resource
), &linenum
))
1538 print_fatal_error("Missing RESOURCE path on line %d.", linenum
);
1543 else if (!_cups_strcasecmp(token
, "OPERATION"))
1549 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1551 print_fatal_error("Missing OPERATION code on line %d.", linenum
);
1556 expand_variables(vars
, token
, temp
, sizeof(token
));
1558 if ((op
= ippOpValue(token
)) == (ipp_op_t
)-1 &&
1559 (op
= (ipp_op_t
)strtol(token
, NULL
, 0)) == 0)
1561 print_fatal_error("Bad OPERATION code \"%s\" on line %d.", token
,
1567 else if (!_cups_strcasecmp(token
, "GROUP"))
1570 * Attribute group...
1573 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1575 print_fatal_error("Missing GROUP tag on line %d.", linenum
);
1580 if ((value
= ippTagValue(token
)) < 0)
1582 print_fatal_error("Bad GROUP tag \"%s\" on line %d.", token
, linenum
);
1588 ippAddSeparator(request
);
1592 else if (!_cups_strcasecmp(token
, "DELAY"))
1595 * Delay before operation...
1600 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1602 print_fatal_error("Missing DELAY value on line %d.", linenum
);
1607 expand_variables(vars
, token
, temp
, sizeof(token
));
1609 if ((delay
= _cupsStrScand(token
, NULL
, localeconv())) <= 0.0)
1611 print_fatal_error("Bad DELAY value \"%s\" on line %d.", token
,
1618 if (Output
== _CUPS_OUTPUT_TEST
)
1619 printf(" [%g second delay]\n", delay
);
1621 usleep((useconds_t
)(1000000.0 * delay
));
1624 else if (!_cups_strcasecmp(token
, "ATTR"))
1630 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1632 print_fatal_error("Missing ATTR value tag on line %d.", linenum
);
1637 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
1639 print_fatal_error("Bad ATTR value tag \"%s\" on line %d.", token
,
1645 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
1647 print_fatal_error("Missing ATTR name on line %d.", linenum
);
1652 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1654 print_fatal_error("Missing ATTR value on line %d.", linenum
);
1659 expand_variables(vars
, token
, temp
, sizeof(token
));
1664 case IPP_TAG_BOOLEAN
:
1665 if (!_cups_strcasecmp(token
, "true"))
1666 attrptr
= ippAddBoolean(request
, group
, attr
, 1);
1668 attrptr
= ippAddBoolean(request
, group
, attr
, (char)atoi(token
));
1671 case IPP_TAG_INTEGER
:
1673 if (!strchr(token
, ','))
1674 attrptr
= ippAddInteger(request
, group
, value
, attr
, (int)strtol(token
, &tokenptr
, 0));
1677 int values
[100], /* Values */
1678 num_values
= 1; /* Number of values */
1680 values
[0] = (int)strtol(token
, &tokenptr
, 10);
1681 while (tokenptr
&& *tokenptr
&&
1682 num_values
< (int)(sizeof(values
) / sizeof(values
[0])))
1684 if (*tokenptr
== ',')
1686 else if (!isdigit(*tokenptr
& 255) && *tokenptr
!= '-')
1689 values
[num_values
] = (int)strtol(tokenptr
, &tokenptr
, 0);
1693 attrptr
= ippAddIntegers(request
, group
, value
, attr
, num_values
, values
);
1696 if (!tokenptr
|| *tokenptr
)
1698 print_fatal_error("Bad %s value \"%s\" on line %d.",
1699 ippTagString(value
), token
, linenum
);
1705 case IPP_TAG_RESOLUTION
:
1707 int xres
, /* X resolution */
1708 yres
; /* Y resolution */
1709 char *ptr
; /* Pointer into value */
1711 xres
= yres
= (int)strtol(token
, (char **)&ptr
, 10);
1712 if (ptr
> token
&& xres
> 0)
1715 yres
= (int)strtol(ptr
+ 1, (char **)&ptr
, 10);
1718 if (ptr
<= token
|| xres
<= 0 || yres
<= 0 || !ptr
||
1719 (_cups_strcasecmp(ptr
, "dpi") &&
1720 _cups_strcasecmp(ptr
, "dpc") &&
1721 _cups_strcasecmp(ptr
, "dpcm") &&
1722 _cups_strcasecmp(ptr
, "other")))
1724 print_fatal_error("Bad resolution value \"%s\" on line %d.",
1730 if (!_cups_strcasecmp(ptr
, "dpi"))
1731 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
1732 else if (!_cups_strcasecmp(ptr
, "dpc") ||
1733 !_cups_strcasecmp(ptr
, "dpcm"))
1734 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_CM
, xres
, yres
);
1736 attrptr
= ippAddResolution(request
, group
, attr
, (ipp_res_t
)0, xres
, yres
);
1740 case IPP_TAG_RANGE
:
1742 int lowers
[4], /* Lower value */
1743 uppers
[4], /* Upper values */
1744 num_vals
; /* Number of values */
1747 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
1748 lowers
+ 0, uppers
+ 0,
1749 lowers
+ 1, uppers
+ 1,
1750 lowers
+ 2, uppers
+ 2,
1751 lowers
+ 3, uppers
+ 3);
1753 if ((num_vals
& 1) || num_vals
== 0)
1755 print_fatal_error("Bad rangeOfInteger value \"%s\" on line "
1756 "%d.", token
, linenum
);
1761 attrptr
= ippAddRanges(request
, group
, attr
, num_vals
/ 2, lowers
,
1766 case IPP_TAG_BEGIN_COLLECTION
:
1767 if (!strcmp(token
, "{"))
1769 ipp_t
*col
= get_collection(vars
, fp
, &linenum
);
1770 /* Collection value */
1774 attrptr
= lastcol
= ippAddCollection(request
, group
, attr
, col
);
1785 print_fatal_error("Bad ATTR collection value on line %d.",
1793 ipp_t
*col
; /* Collection value */
1794 long pos
= ftell(fp
); /* Save position of file */
1796 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1799 if (strcmp(token
, ","))
1801 fseek(fp
, pos
, SEEK_SET
);
1805 if (!get_token(fp
, token
, sizeof(token
), &linenum
) || strcmp(token
, "{"))
1807 print_fatal_error("Unexpected \"%s\" on line %d.", token
, linenum
);
1813 if ((col
= get_collection(vars
, fp
, &linenum
)) == NULL
)
1816 ippSetCollection(request
, &attrptr
, ippGetCount(attrptr
), col
);
1819 while (!strcmp(token
, "{"));
1822 case IPP_TAG_STRING
:
1823 attrptr
= ippAddOctetString(request
, group
, attr
, token
, (int)strlen(token
));
1827 print_fatal_error("Unsupported ATTR value tag %s on line %d.",
1828 ippTagString(value
), linenum
);
1832 case IPP_TAG_TEXTLANG
:
1833 case IPP_TAG_NAMELANG
:
1836 case IPP_TAG_KEYWORD
:
1838 case IPP_TAG_URISCHEME
:
1839 case IPP_TAG_CHARSET
:
1840 case IPP_TAG_LANGUAGE
:
1841 case IPP_TAG_MIMETYPE
:
1842 if (!strchr(token
, ','))
1843 attrptr
= ippAddString(request
, group
, value
, attr
, NULL
, token
);
1847 * Multiple string values...
1850 int num_values
; /* Number of values */
1851 char *values
[100], /* Values */
1852 *ptr
; /* Pointer to next value */
1858 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
1860 if (ptr
> token
&& ptr
[-1] == '\\')
1861 _cups_strcpy(ptr
- 1, ptr
);
1865 values
[num_values
] = ptr
;
1870 attrptr
= ippAddStrings(request
, group
, value
, attr
, num_values
,
1871 NULL
, (const char **)values
);
1878 print_fatal_error("Unable to add attribute on line %d: %s", linenum
,
1879 cupsLastErrorString());
1884 else if (!_cups_strcasecmp(token
, "FILE"))
1890 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1892 print_fatal_error("Missing FILE filename on line %d.", linenum
);
1897 expand_variables(vars
, token
, temp
, sizeof(token
));
1898 get_filename(testfile
, filename
, token
, sizeof(filename
));
1900 if (access(filename
, R_OK
))
1902 print_fatal_error("Filename \"%s\" on line %d cannot be read.",
1904 print_fatal_error("Filename mapped to \"%s\".", filename
);
1909 else if (!_cups_strcasecmp(token
, "STATUS"))
1915 if (num_statuses
>= (int)(sizeof(statuses
) / sizeof(statuses
[0])))
1917 print_fatal_error("Too many STATUS's on line %d.", linenum
);
1922 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1924 print_fatal_error("Missing STATUS code on line %d.", linenum
);
1929 if ((statuses
[num_statuses
].status
= ippErrorValue(token
))
1930 == (ipp_status_t
)-1 &&
1931 (statuses
[num_statuses
].status
= (ipp_status_t
)strtol(token
, NULL
, 0)) == 0)
1933 print_fatal_error("Bad STATUS code \"%s\" on line %d.", token
,
1939 last_status
= statuses
+ num_statuses
;
1942 last_status
->define_match
= NULL
;
1943 last_status
->define_no_match
= NULL
;
1944 last_status
->if_defined
= NULL
;
1945 last_status
->if_not_defined
= NULL
;
1946 last_status
->repeat_limit
= 1000;
1947 last_status
->repeat_match
= 0;
1948 last_status
->repeat_no_match
= 0;
1950 else if (!_cups_strcasecmp(token
, "EXPECT"))
1953 * Expected attributes...
1956 if (num_expects
>= (int)(sizeof(expects
) / sizeof(expects
[0])))
1958 print_fatal_error("Too many EXPECT's on line %d.", linenum
);
1963 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1965 print_fatal_error("Missing EXPECT name on line %d.", linenum
);
1970 last_expect
= expects
+ num_expects
;
1973 memset(last_expect
, 0, sizeof(_cups_expect_t
));
1974 last_expect
->repeat_limit
= 1000;
1976 if (token
[0] == '!')
1978 last_expect
->not_expect
= 1;
1979 last_expect
->name
= strdup(token
+ 1);
1981 else if (token
[0] == '?')
1983 last_expect
->optional
= 1;
1984 last_expect
->name
= strdup(token
+ 1);
1987 last_expect
->name
= strdup(token
);
1989 else if (!_cups_strcasecmp(token
, "COUNT"))
1991 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1993 print_fatal_error("Missing COUNT number on line %d.", linenum
);
1998 if ((i
= atoi(token
)) <= 0)
2000 print_fatal_error("Bad COUNT \"%s\" on line %d.", token
, linenum
);
2006 last_expect
->count
= i
;
2009 print_fatal_error("COUNT without a preceding EXPECT on line %d.",
2015 else if (!_cups_strcasecmp(token
, "DEFINE-MATCH"))
2017 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2019 print_fatal_error("Missing DEFINE-MATCH variable on line %d.",
2026 last_expect
->define_match
= strdup(token
);
2027 else if (last_status
)
2028 last_status
->define_match
= strdup(token
);
2031 print_fatal_error("DEFINE-MATCH without a preceding EXPECT or STATUS "
2032 "on line %d.", linenum
);
2037 else if (!_cups_strcasecmp(token
, "DEFINE-NO-MATCH"))
2039 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2041 print_fatal_error("Missing DEFINE-NO-MATCH variable on line %d.",
2048 last_expect
->define_no_match
= strdup(token
);
2049 else if (last_status
)
2050 last_status
->define_no_match
= strdup(token
);
2053 print_fatal_error("DEFINE-NO-MATCH without a preceding EXPECT or "
2054 "STATUS on line %d.", linenum
);
2059 else if (!_cups_strcasecmp(token
, "DEFINE-VALUE"))
2061 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2063 print_fatal_error("Missing DEFINE-VALUE variable on line %d.",
2070 last_expect
->define_value
= strdup(token
);
2073 print_fatal_error("DEFINE-VALUE without a preceding EXPECT on "
2074 "line %d.", linenum
);
2079 else if (!_cups_strcasecmp(token
, "OF-TYPE"))
2081 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2083 print_fatal_error("Missing OF-TYPE value tag(s) on line %d.",
2090 last_expect
->of_type
= strdup(token
);
2093 print_fatal_error("OF-TYPE without a preceding EXPECT on line %d.",
2099 else if (!_cups_strcasecmp(token
, "IN-GROUP"))
2101 ipp_tag_t in_group
; /* IN-GROUP value */
2104 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2106 print_fatal_error("Missing IN-GROUP group tag on line %d.", linenum
);
2111 if ((in_group
= ippTagValue(token
)) == (ipp_tag_t
)-1)
2114 else if (last_expect
)
2115 last_expect
->in_group
= in_group
;
2118 print_fatal_error("IN-GROUP without a preceding EXPECT on line %d.",
2124 else if (!_cups_strcasecmp(token
, "REPEAT-LIMIT"))
2126 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2128 print_fatal_error("Missing REPEAT-LIMIT value on line %d.", linenum
);
2132 else if (atoi(token
) <= 0)
2134 print_fatal_error("Bad REPEAT-LIMIT value on line %d.", linenum
);
2140 last_status
->repeat_limit
= atoi(token
);
2141 else if (last_expect
)
2142 last_expect
->repeat_limit
= atoi(token
);
2145 print_fatal_error("REPEAT-LIMIT without a preceding EXPECT or STATUS "
2146 "on line %d.", linenum
);
2151 else if (!_cups_strcasecmp(token
, "REPEAT-MATCH"))
2154 last_status
->repeat_match
= 1;
2155 else if (last_expect
)
2156 last_expect
->repeat_match
= 1;
2159 print_fatal_error("REPEAT-MATCH without a preceding EXPECT or STATUS "
2160 "on line %d.", linenum
);
2165 else if (!_cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
2168 last_status
->repeat_no_match
= 1;
2169 else if (last_expect
)
2170 last_expect
->repeat_no_match
= 1;
2173 print_fatal_error("REPEAT-NO-MATCH without a preceding EXPECT or "
2174 "STATUS on ine %d.", linenum
);
2179 else if (!_cups_strcasecmp(token
, "SAME-COUNT-AS"))
2181 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2183 print_fatal_error("Missing SAME-COUNT-AS name on line %d.", linenum
);
2189 last_expect
->same_count_as
= strdup(token
);
2192 print_fatal_error("SAME-COUNT-AS without a preceding EXPECT on line "
2198 else if (!_cups_strcasecmp(token
, "IF-DEFINED"))
2200 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2202 print_fatal_error("Missing IF-DEFINED name on line %d.", linenum
);
2208 last_expect
->if_defined
= strdup(token
);
2209 else if (last_status
)
2210 last_status
->if_defined
= strdup(token
);
2213 print_fatal_error("IF-DEFINED without a preceding EXPECT or STATUS "
2214 "on line %d.", linenum
);
2219 else if (!_cups_strcasecmp(token
, "IF-NOT-DEFINED"))
2221 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2223 print_fatal_error("Missing IF-NOT-DEFINED name on line %d.", linenum
);
2229 last_expect
->if_not_defined
= strdup(token
);
2230 else if (last_status
)
2231 last_status
->if_not_defined
= strdup(token
);
2234 print_fatal_error("IF-NOT-DEFINED without a preceding EXPECT or STATUS "
2235 "on line %d.", linenum
);
2240 else if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") ||
2241 !_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
2242 !_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
2243 !_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
2244 !_cups_strcasecmp(token
, "WITH-HOSTNAME") ||
2245 !_cups_strcasecmp(token
, "WITH-RESOURCE") ||
2246 !_cups_strcasecmp(token
, "WITH-SCHEME") ||
2247 !_cups_strcasecmp(token
, "WITH-VALUE"))
2251 if (!_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
2252 !_cups_strcasecmp(token
, "WITH-HOSTNAME"))
2253 last_expect
->with_flags
= _CUPS_WITH_HOSTNAME
;
2254 else if (!_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
2255 !_cups_strcasecmp(token
, "WITH-RESOURCE"))
2256 last_expect
->with_flags
= _CUPS_WITH_RESOURCE
;
2257 else if (!_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
2258 !_cups_strcasecmp(token
, "WITH-SCHEME"))
2259 last_expect
->with_flags
= _CUPS_WITH_SCHEME
;
2261 if (!_cups_strncasecmp(token
, "WITH-ALL-", 9))
2262 last_expect
->with_flags
|= _CUPS_WITH_ALL
;
2265 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
2267 print_fatal_error("Missing %s value on line %d.", token
, linenum
);
2275 * Expand any variables in the value and then save it.
2278 expand_variables(vars
, token
, temp
, sizeof(token
));
2280 tokenptr
= token
+ strlen(token
) - 1;
2282 if (token
[0] == '/' && tokenptr
> token
&& *tokenptr
== '/')
2285 * WITH-VALUE is a POSIX extended regular expression.
2288 last_expect
->with_value
= calloc(1, (size_t)(tokenptr
- token
));
2289 last_expect
->with_flags
|= _CUPS_WITH_REGEX
;
2291 if (last_expect
->with_value
)
2292 memcpy(last_expect
->with_value
, token
+ 1, (size_t)(tokenptr
- token
- 1));
2297 * WITH-VALUE is a literal value...
2300 char *ptr
; /* Pointer into value */
2302 for (ptr
= token
; *ptr
; ptr
++)
2304 if (*ptr
== '\\' && ptr
[1])
2307 * Remove \ from \foo...
2310 _cups_strcpy(ptr
, ptr
+ 1);
2314 last_expect
->with_value
= strdup(token
);
2315 last_expect
->with_flags
|= _CUPS_WITH_LITERAL
;
2320 print_fatal_error("%s without a preceding EXPECT on line %d.", token
,
2326 else if (!_cups_strcasecmp(token
, "DISPLAY"))
2329 * Display attributes...
2332 if (num_displayed
>= (int)(sizeof(displayed
) / sizeof(displayed
[0])))
2334 print_fatal_error("Too many DISPLAY's on line %d", linenum
);
2339 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2341 print_fatal_error("Missing DISPLAY name on line %d.", linenum
);
2346 displayed
[num_displayed
] = strdup(token
);
2351 print_fatal_error("Unexpected token %s seen on line %d.", token
,
2359 * Submit the IPP request...
2364 ippSetVersion(request
, version
/ 10, version
% 10);
2365 ippSetOperation(request
, op
);
2366 ippSetRequestId(request
, request_id
);
2368 if (Output
== _CUPS_OUTPUT_PLIST
)
2371 puts("<key>Name</key>");
2372 print_xml_string("string", name
);
2375 puts("<key>FileId</key>");
2376 print_xml_string("string", file_id
);
2380 puts("<key>TestId</key>");
2381 print_xml_string("string", test_id
);
2383 puts("<key>Version</key>");
2384 printf("<string>%d.%d</string>\n", version
/ 10, version
% 10);
2385 puts("<key>Operation</key>");
2386 print_xml_string("string", ippOpString(op
));
2387 puts("<key>RequestId</key>");
2388 printf("<integer>%d</integer>\n", request_id
);
2389 puts("<key>RequestAttributes</key>");
2394 for (attrptr
= request
->attrs
,
2395 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2397 attrptr
= attrptr
->next
)
2398 print_attr(attrptr
, &group
);
2403 else if (Output
== _CUPS_OUTPUT_TEST
)
2407 printf(" %s:\n", ippOpString(op
));
2409 for (attrptr
= request
->attrs
; attrptr
; attrptr
= attrptr
->next
)
2410 print_attr(attrptr
, NULL
);
2413 printf(" %-68.68s [", name
);
2417 if ((skip_previous
&& !prev_pass
) || skip_test
)
2424 if (Output
== _CUPS_OUTPUT_PLIST
)
2426 puts("<key>Successful</key>");
2428 puts("<key>StatusCode</key>");
2429 print_xml_string("string", "skip");
2430 puts("<key>ResponseAttributes</key>");
2433 else if (Output
== _CUPS_OUTPUT_TEST
)
2441 repeat_interval
= 1;
2448 status
= HTTP_STATUS_OK
;
2450 if (transfer
== _CUPS_TRANSFER_CHUNKED
||
2451 (transfer
== _CUPS_TRANSFER_AUTO
&& filename
[0]))
2454 * Send request using chunking - a 0 length means "chunk".
2462 * Send request using content length...
2465 length
= ippLength(request
);
2467 if (filename
[0] && (reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2470 * Read the file to get the uncompressed file size...
2473 while ((bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
2474 length
+= (size_t)bytes
;
2476 cupsFileClose(reqfile
);
2481 * Send the request...
2488 if (status
!= HTTP_STATUS_ERROR
)
2490 while (!response
&& !Cancel
&& prev_pass
)
2492 status
= cupsSendRequest(http
, request
, resource
, length
);
2496 httpSetField(http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
2497 #endif /* HAVE_LIBZ */
2499 if (!Cancel
&& status
== HTTP_STATUS_CONTINUE
&&
2500 request
->state
== IPP_DATA
&& filename
[0])
2502 if ((reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2505 (bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
2506 if ((status
= cupsWriteRequestData(http
, buffer
, (size_t)bytes
)) != HTTP_STATUS_CONTINUE
)
2509 cupsFileClose(reqfile
);
2513 snprintf(buffer
, sizeof(buffer
), "%s: %s", filename
,
2515 _cupsSetError(IPP_INTERNAL_ERROR
, buffer
, 0);
2517 status
= HTTP_STATUS_ERROR
;
2522 * Get the server's response...
2525 if (!Cancel
&& status
!= HTTP_STATUS_ERROR
)
2527 response
= cupsGetResponse(http
, resource
);
2528 status
= httpGetStatus(http
);
2531 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& http
->error
!= EINVAL
&&
2533 http
->error
!= WSAETIMEDOUT
)
2535 http
->error
!= ETIMEDOUT
)
2538 if (httpReconnect(http
))
2541 else if (status
== HTTP_STATUS_ERROR
|| status
== HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED
)
2546 else if (status
!= HTTP_STATUS_OK
)
2550 if (status
== HTTP_STATUS_UNAUTHORIZED
)
2558 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& http
->error
!= EINVAL
&&
2560 http
->error
!= WSAETIMEDOUT
)
2562 http
->error
!= ETIMEDOUT
)
2565 if (httpReconnect(http
))
2568 else if (status
== HTTP_STATUS_ERROR
)
2571 httpReconnect(http
);
2575 else if (status
!= HTTP_STATUS_OK
)
2582 * Check results of request...
2585 cupsArrayClear(errors
);
2587 if (http
->version
!= HTTP_1_1
)
2588 add_stringf(errors
, "Bad HTTP version (%d.%d)", http
->version
/ 100,
2589 http
->version
% 100);
2594 * No response, log error...
2597 add_stringf(errors
, "IPP request failed with status %s (%s)",
2598 ippErrorString(cupsLastError()),
2599 cupsLastErrorString());
2604 * Collect common attribute values...
2607 if ((attrptr
= ippFindAttribute(response
, "job-id",
2608 IPP_TAG_INTEGER
)) != NULL
)
2610 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2611 set_variable(vars
, "job-id", temp
);
2614 if ((attrptr
= ippFindAttribute(response
, "job-uri",
2615 IPP_TAG_URI
)) != NULL
)
2616 set_variable(vars
, "job-uri", attrptr
->values
[0].string
.text
);
2618 if ((attrptr
= ippFindAttribute(response
, "notify-subscription-id",
2619 IPP_TAG_INTEGER
)) != NULL
)
2621 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2622 set_variable(vars
, "notify-subscription-id", temp
);
2626 * Check response, validating groups and attributes and logging errors
2630 if (response
->state
!= IPP_DATA
)
2632 "Missing end-of-attributes-tag in response "
2633 "(RFC 2910 section 3.5.1)");
2636 (response
->request
.status
.version
[0] != (version
/ 10) ||
2637 response
->request
.status
.version
[1] != (version
% 10)))
2639 "Bad version %d.%d in response - expected %d.%d "
2640 "(RFC 2911 section 3.1.8).",
2641 response
->request
.status
.version
[0],
2642 response
->request
.status
.version
[1],
2643 version
/ 10, version
% 10);
2645 if (response
->request
.status
.request_id
!= request_id
)
2647 "Bad request ID %d in response - expected %d "
2648 "(RFC 2911 section 3.1.1)",
2649 response
->request
.status
.request_id
, request_id
);
2651 attrptr
= response
->attrs
;
2654 "Missing first attribute \"attributes-charset "
2655 "(charset)\" in group operation-attributes-tag "
2656 "(RFC 2911 section 3.1.4).");
2659 if (!attrptr
->name
||
2660 attrptr
->value_tag
!= IPP_TAG_CHARSET
||
2661 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2662 attrptr
->num_values
!= 1 ||
2663 strcmp(attrptr
->name
, "attributes-charset"))
2665 "Bad first attribute \"%s (%s%s)\" in group %s, "
2666 "expected \"attributes-charset (charset)\" in "
2667 "group operation-attributes-tag (RFC 2911 section "
2669 attrptr
->name
? attrptr
->name
: "(null)",
2670 attrptr
->num_values
> 1 ? "1setOf " : "",
2671 ippTagString(attrptr
->value_tag
),
2672 ippTagString(attrptr
->group_tag
));
2674 attrptr
= attrptr
->next
;
2677 "Missing second attribute \"attributes-natural-"
2678 "language (naturalLanguage)\" in group "
2679 "operation-attributes-tag (RFC 2911 section "
2681 else if (!attrptr
->name
||
2682 attrptr
->value_tag
!= IPP_TAG_LANGUAGE
||
2683 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2684 attrptr
->num_values
!= 1 ||
2685 strcmp(attrptr
->name
, "attributes-natural-language"))
2687 "Bad first attribute \"%s (%s%s)\" in group %s, "
2688 "expected \"attributes-natural-language "
2689 "(naturalLanguage)\" in group "
2690 "operation-attributes-tag (RFC 2911 section "
2692 attrptr
->name
? attrptr
->name
: "(null)",
2693 attrptr
->num_values
> 1 ? "1setOf " : "",
2694 ippTagString(attrptr
->value_tag
),
2695 ippTagString(attrptr
->group_tag
));
2698 if ((attrptr
= ippFindAttribute(response
, "status-message",
2699 IPP_TAG_ZERO
)) != NULL
)
2701 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2703 "status-message (text(255)) has wrong value tag "
2704 "%s (RFC 2911 section 3.1.6.2).",
2705 ippTagString(attrptr
->value_tag
));
2706 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2708 "status-message (text(255)) has wrong group tag "
2709 "%s (RFC 2911 section 3.1.6.2).",
2710 ippTagString(attrptr
->group_tag
));
2711 if (attrptr
->num_values
!= 1)
2713 "status-message (text(255)) has %d values "
2714 "(RFC 2911 section 3.1.6.2).",
2715 attrptr
->num_values
);
2716 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2717 strlen(attrptr
->values
[0].string
.text
) > 255)
2719 "status-message (text(255)) has bad length %d"
2720 " (RFC 2911 section 3.1.6.2).",
2721 (int)strlen(attrptr
->values
[0].string
.text
));
2724 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
2725 IPP_TAG_ZERO
)) != NULL
)
2727 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2729 "detailed-status-message (text(MAX)) has wrong "
2730 "value tag %s (RFC 2911 section 3.1.6.3).",
2731 ippTagString(attrptr
->value_tag
));
2732 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2734 "detailed-status-message (text(MAX)) has wrong "
2735 "group tag %s (RFC 2911 section 3.1.6.3).",
2736 ippTagString(attrptr
->group_tag
));
2737 if (attrptr
->num_values
!= 1)
2739 "detailed-status-message (text(MAX)) has %d values"
2740 " (RFC 2911 section 3.1.6.3).",
2741 attrptr
->num_values
);
2742 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2743 strlen(attrptr
->values
[0].string
.text
) > 1023)
2745 "detailed-status-message (text(MAX)) has bad "
2746 "length %d (RFC 2911 section 3.1.6.3).",
2747 (int)strlen(attrptr
->values
[0].string
.text
));
2750 a
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2752 for (attrptr
= response
->attrs
,
2753 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2755 attrptr
= attrptr
->next
)
2757 if (attrptr
->group_tag
!= group
)
2759 int out_of_order
= 0; /* Are attribute groups out-of-order? */
2762 switch (attrptr
->group_tag
)
2767 case IPP_TAG_OPERATION
:
2771 case IPP_TAG_UNSUPPORTED_GROUP
:
2772 if (group
!= IPP_TAG_OPERATION
)
2777 case IPP_TAG_PRINTER
:
2778 if (group
!= IPP_TAG_OPERATION
&&
2779 group
!= IPP_TAG_UNSUPPORTED_GROUP
)
2783 case IPP_TAG_SUBSCRIPTION
:
2784 if (group
> attrptr
->group_tag
&&
2785 group
!= IPP_TAG_DOCUMENT
)
2790 if (group
> attrptr
->group_tag
)
2796 add_stringf(errors
, "Attribute groups out of order (%s < %s)",
2797 ippTagString(attrptr
->group_tag
),
2798 ippTagString(group
));
2800 if (attrptr
->group_tag
!= IPP_TAG_ZERO
)
2801 group
= attrptr
->group_tag
;
2804 validate_attr(errors
, attrptr
);
2808 if (cupsArrayFind(a
, attrptr
->name
))
2809 add_stringf(errors
, "Duplicate \"%s\" attribute in %s group",
2810 attrptr
->name
, ippTagString(group
));
2812 cupsArrayAdd(a
, attrptr
->name
);
2819 * Now check the test-defined expected status-code and attribute
2823 for (i
= 0; i
< num_statuses
; i
++)
2825 if (statuses
[i
].if_defined
&&
2826 !get_variable(vars
, statuses
[i
].if_defined
))
2829 if (statuses
[i
].if_not_defined
&&
2830 get_variable(vars
, statuses
[i
].if_not_defined
))
2833 if (response
->request
.status
.status_code
== statuses
[i
].status
)
2835 if (statuses
[i
].repeat_match
&&
2836 repeat_count
< statuses
[i
].repeat_limit
)
2839 if (statuses
[i
].define_match
)
2840 set_variable(vars
, statuses
[i
].define_match
, "1");
2846 if (statuses
[i
].repeat_no_match
&&
2847 repeat_count
< statuses
[i
].repeat_limit
)
2850 if (statuses
[i
].define_no_match
)
2852 set_variable(vars
, statuses
[i
].define_no_match
, "1");
2858 if (i
== num_statuses
&& num_statuses
> 0)
2860 for (i
= 0; i
< num_statuses
; i
++)
2862 if (statuses
[i
].if_defined
&&
2863 !get_variable(vars
, statuses
[i
].if_defined
))
2866 if (statuses
[i
].if_not_defined
&&
2867 get_variable(vars
, statuses
[i
].if_not_defined
))
2870 if (!statuses
[i
].repeat_match
)
2871 add_stringf(errors
, "EXPECTED: STATUS %s (got %s)",
2872 ippErrorString(statuses
[i
].status
),
2873 ippErrorString(cupsLastError()));
2876 if ((attrptr
= ippFindAttribute(response
, "status-message",
2877 IPP_TAG_TEXT
)) != NULL
)
2878 add_stringf(errors
, "status-message=\"%s\"",
2879 attrptr
->values
[0].string
.text
);
2882 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
2884 if (expect
->if_defined
&& !get_variable(vars
, expect
->if_defined
))
2887 if (expect
->if_not_defined
&&
2888 get_variable(vars
, expect
->if_not_defined
))
2891 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
2893 if ((found
&& expect
->not_expect
) ||
2894 (!found
&& !(expect
->not_expect
|| expect
->optional
)) ||
2895 (found
&& !expect_matches(expect
, found
->value_tag
)) ||
2896 (found
&& expect
->in_group
&&
2897 found
->group_tag
!= expect
->in_group
))
2899 if (expect
->define_no_match
)
2900 set_variable(vars
, expect
->define_no_match
, "1");
2901 else if (!expect
->define_match
&& !expect
->define_value
)
2903 if (found
&& expect
->not_expect
)
2904 add_stringf(errors
, "NOT EXPECTED: %s", expect
->name
);
2905 else if (!found
&& !(expect
->not_expect
|| expect
->optional
))
2906 add_stringf(errors
, "EXPECTED: %s", expect
->name
);
2909 if (!expect_matches(expect
, found
->value_tag
))
2910 add_stringf(errors
, "EXPECTED: %s OF-TYPE %s (got %s)",
2911 expect
->name
, expect
->of_type
,
2912 ippTagString(found
->value_tag
));
2914 if (expect
->in_group
&& found
->group_tag
!= expect
->in_group
)
2915 add_stringf(errors
, "EXPECTED: %s IN-GROUP %s (got %s).",
2916 expect
->name
, ippTagString(expect
->in_group
),
2917 ippTagString(found
->group_tag
));
2921 if (expect
->repeat_no_match
&&
2922 repeat_count
< expect
->repeat_limit
)
2929 ippAttributeString(found
, buffer
, sizeof(buffer
));
2932 !with_value(NULL
, expect
->with_value
, expect
->with_flags
, found
,
2933 buffer
, sizeof(buffer
)))
2935 if (expect
->define_no_match
)
2936 set_variable(vars
, expect
->define_no_match
, "1");
2937 else if (!expect
->define_match
&& !expect
->define_value
&&
2938 !expect
->repeat_match
&& !expect
->repeat_no_match
)
2940 if (expect
->with_flags
& _CUPS_WITH_REGEX
)
2941 add_stringf(errors
, "EXPECTED: %s %s /%s/",
2943 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
2944 "WITH-ALL-VALUES" : "WITH-VALUE",
2945 expect
->with_value
);
2947 add_stringf(errors
, "EXPECTED: %s %s \"%s\"",
2949 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
2950 "WITH-ALL-VALUES" : "WITH-VALUE",
2951 expect
->with_value
);
2953 with_value(errors
, expect
->with_value
, expect
->with_flags
, found
,
2954 buffer
, sizeof(buffer
));
2957 if (expect
->repeat_no_match
&&
2958 repeat_count
< expect
->repeat_limit
)
2964 if (found
&& expect
->count
> 0 &&
2965 found
->num_values
!= expect
->count
)
2967 if (expect
->define_no_match
)
2968 set_variable(vars
, expect
->define_no_match
, "1");
2969 else if (!expect
->define_match
&& !expect
->define_value
)
2971 add_stringf(errors
, "EXPECTED: %s COUNT %d (got %d)", expect
->name
,
2972 expect
->count
, found
->num_values
);
2975 if (expect
->repeat_no_match
&&
2976 repeat_count
< expect
->repeat_limit
)
2982 if (found
&& expect
->same_count_as
)
2984 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
2987 if (!attrptr
|| attrptr
->num_values
!= found
->num_values
)
2989 if (expect
->define_no_match
)
2990 set_variable(vars
, expect
->define_no_match
, "1");
2991 else if (!expect
->define_match
&& !expect
->define_value
)
2995 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2996 "(not returned)", expect
->name
,
2997 found
->num_values
, expect
->same_count_as
);
2998 else if (attrptr
->num_values
!= found
->num_values
)
3000 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
3001 "(%d values)", expect
->name
, found
->num_values
,
3002 expect
->same_count_as
, attrptr
->num_values
);
3005 if (expect
->repeat_no_match
&&
3006 repeat_count
< expect
->repeat_limit
)
3013 if (found
&& expect
->define_match
)
3014 set_variable(vars
, expect
->define_match
, "1");
3016 if (found
&& expect
->define_value
)
3017 set_variable(vars
, expect
->define_value
, buffer
);
3019 if (found
&& expect
->repeat_match
&&
3020 repeat_count
< expect
->repeat_limit
)
3026 * If we are going to repeat this test, sleep 1 second so we don't flood
3027 * the printer with requests...
3032 if (Output
== _CUPS_OUTPUT_TEST
)
3034 printf("%04d]\n", repeat_count
);
3038 sleep((unsigned)repeat_interval
);
3039 repeat_interval
= _cupsNextDelay(repeat_interval
, &repeat_prev
);
3041 if (Output
== _CUPS_OUTPUT_TEST
)
3043 printf(" %-68.68s [", name
);
3048 while (repeat_test
);
3054 if (cupsArrayCount(errors
) > 0)
3055 prev_pass
= pass
= 0;
3062 if (Output
== _CUPS_OUTPUT_PLIST
)
3064 puts("<key>Successful</key>");
3065 puts(prev_pass
? "<true />" : "<false />");
3066 puts("<key>StatusCode</key>");
3067 print_xml_string("string", ippErrorString(cupsLastError()));
3068 puts("<key>ResponseAttributes</key>");
3071 for (attrptr
= response
? response
->attrs
: NULL
,
3072 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
3074 attrptr
= attrptr
->next
)
3075 print_attr(attrptr
, &group
);
3079 else if (Output
== _CUPS_OUTPUT_TEST
)
3081 puts(prev_pass
? "PASS]" : "FAIL]");
3083 if (!prev_pass
|| (Verbosity
&& response
))
3085 printf(" RECEIVED: %lu bytes in response\n",
3086 (unsigned long)ippLength(response
));
3087 printf(" status-code = %s (%s)\n", ippErrorString(cupsLastError()),
3088 cupsLastErrorString());
3092 for (attrptr
= response
->attrs
;
3094 attrptr
= attrptr
->next
)
3095 print_attr(attrptr
, NULL
);
3099 else if (!prev_pass
)
3100 fprintf(stderr
, "%s\n", cupsLastErrorString());
3102 if (prev_pass
&& Output
>= _CUPS_OUTPUT_LIST
&& !Verbosity
&&
3105 size_t width
; /* Length of value */
3107 for (i
= 0; i
< num_displayed
; i
++)
3109 widths
[i
] = strlen(displayed
[i
]);
3111 for (attrptr
= ippFindAttribute(response
, displayed
[i
], IPP_TAG_ZERO
);
3113 attrptr
= ippFindNextAttribute(response
, displayed
[i
],
3116 width
= ippAttributeString(attrptr
, NULL
, 0);
3117 if (width
> widths
[i
])
3122 if (Output
== _CUPS_OUTPUT_CSV
)
3123 print_csv(NULL
, num_displayed
, displayed
, widths
);
3125 print_line(NULL
, num_displayed
, displayed
, widths
);
3127 attrptr
= response
->attrs
;
3131 while (attrptr
&& attrptr
->group_tag
<= IPP_TAG_OPERATION
)
3132 attrptr
= attrptr
->next
;
3136 if (Output
== _CUPS_OUTPUT_CSV
)
3137 print_csv(attrptr
, num_displayed
, displayed
, widths
);
3139 print_line(attrptr
, num_displayed
, displayed
, widths
);
3141 while (attrptr
&& attrptr
->group_tag
> IPP_TAG_OPERATION
)
3142 attrptr
= attrptr
->next
;
3146 else if (!prev_pass
)
3148 if (Output
== _CUPS_OUTPUT_PLIST
)
3150 puts("<key>Errors</key>");
3153 for (error
= (char *)cupsArrayFirst(errors
);
3155 error
= (char *)cupsArrayNext(errors
))
3156 print_xml_string("string", error
);
3162 for (error
= (char *)cupsArrayFirst(errors
);
3164 error
= (char *)cupsArrayNext(errors
))
3165 printf(" %s\n", error
);
3169 if (num_displayed
> 0 && !Verbosity
&& response
&&
3170 Output
== _CUPS_OUTPUT_TEST
)
3172 for (attrptr
= response
->attrs
;
3174 attrptr
= attrptr
->next
)
3178 for (i
= 0; i
< num_displayed
; i
++)
3180 if (!strcmp(displayed
[i
], attrptr
->name
))
3182 print_attr(attrptr
, NULL
);
3192 if (Output
== _CUPS_OUTPUT_PLIST
)
3197 ippDelete(response
);
3200 for (i
= 0; i
< num_statuses
; i
++)
3202 if (statuses
[i
].if_defined
)
3203 free(statuses
[i
].if_defined
);
3204 if (statuses
[i
].if_not_defined
)
3205 free(statuses
[i
].if_not_defined
);
3206 if (statuses
[i
].define_match
)
3207 free(statuses
[i
].define_match
);
3208 if (statuses
[i
].define_no_match
)
3209 free(statuses
[i
].define_no_match
);
3213 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3216 if (expect
->of_type
)
3217 free(expect
->of_type
);
3218 if (expect
->same_count_as
)
3219 free(expect
->same_count_as
);
3220 if (expect
->if_defined
)
3221 free(expect
->if_defined
);
3222 if (expect
->if_not_defined
)
3223 free(expect
->if_not_defined
);
3224 if (expect
->with_value
)
3225 free(expect
->with_value
);
3226 if (expect
->define_match
)
3227 free(expect
->define_match
);
3228 if (expect
->define_no_match
)
3229 free(expect
->define_no_match
);
3230 if (expect
->define_value
)
3231 free(expect
->define_value
);
3235 for (i
= 0; i
< num_displayed
; i
++)
3239 if (!ignore_errors
&& !prev_pass
)
3245 cupsArrayDelete(errors
);
3252 ippDelete(response
);
3254 for (i
= 0; i
< num_statuses
; i
++)
3256 if (statuses
[i
].if_defined
)
3257 free(statuses
[i
].if_defined
);
3258 if (statuses
[i
].if_not_defined
)
3259 free(statuses
[i
].if_not_defined
);
3260 if (statuses
[i
].define_match
)
3261 free(statuses
[i
].define_match
);
3262 if (statuses
[i
].define_no_match
)
3263 free(statuses
[i
].define_no_match
);
3266 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3269 if (expect
->of_type
)
3270 free(expect
->of_type
);
3271 if (expect
->same_count_as
)
3272 free(expect
->same_count_as
);
3273 if (expect
->if_defined
)
3274 free(expect
->if_defined
);
3275 if (expect
->if_not_defined
)
3276 free(expect
->if_not_defined
);
3277 if (expect
->with_value
)
3278 free(expect
->with_value
);
3279 if (expect
->define_match
)
3280 free(expect
->define_match
);
3281 if (expect
->define_no_match
)
3282 free(expect
->define_no_match
);
3283 if (expect
->define_value
)
3284 free(expect
->define_value
);
3287 for (i
= 0; i
< num_displayed
; i
++)
3295 * 'expand_variables()' - Expand variables in a string.
3299 expand_variables(_cups_vars_t
*vars
, /* I - Variables */
3300 char *dst
, /* I - Destination string buffer */
3301 const char *src
, /* I - Source string */
3302 size_t dstsize
) /* I - Size of destination buffer */
3304 char *dstptr
, /* Pointer into destination */
3305 *dstend
, /* End of destination */
3306 temp
[256], /* Temporary string */
3307 *tempptr
; /* Pointer into temporary string */
3308 const char *value
; /* Value to substitute */
3312 dstend
= dst
+ dstsize
- 1;
3314 while (*src
&& dstptr
< dstend
)
3319 * Substitute a string/number...
3322 if (!strncmp(src
, "$$", 2))
3327 else if (!strncmp(src
, "$ENV[", 5))
3329 strlcpy(temp
, src
+ 5, sizeof(temp
));
3331 for (tempptr
= temp
; *tempptr
; tempptr
++)
3332 if (*tempptr
== ']')
3338 value
= getenv(temp
);
3339 src
+= tempptr
- temp
+ 5;
3343 strlcpy(temp
, src
+ 1, sizeof(temp
));
3345 for (tempptr
= temp
; *tempptr
; tempptr
++)
3346 if (!isalnum(*tempptr
& 255) && *tempptr
!= '-' && *tempptr
!= '_')
3352 if (!strcmp(temp
, "uri"))
3354 else if (!strcmp(temp
, "filename"))
3355 value
= vars
->filename
;
3356 else if (!strcmp(temp
, "scheme") || !strcmp(temp
, "method"))
3357 value
= vars
->scheme
;
3358 else if (!strcmp(temp
, "username"))
3359 value
= vars
->userpass
;
3360 else if (!strcmp(temp
, "hostname"))
3361 value
= vars
->hostname
;
3362 else if (!strcmp(temp
, "port"))
3364 snprintf(temp
, sizeof(temp
), "%d", vars
->port
);
3367 else if (!strcmp(temp
, "resource"))
3368 value
= vars
->resource
;
3369 else if (!strcmp(temp
, "user"))
3372 value
= get_variable(vars
, temp
);
3374 src
+= tempptr
- temp
+ 1;
3384 strlcpy(dstptr
, value
, (size_t)(dstend
- dstptr
+ 1));
3385 dstptr
+= strlen(dstptr
);
3397 * 'expect_matches()' - Return true if the tag matches the specification.
3400 static int /* O - 1 if matches, 0 otherwise */
3402 _cups_expect_t
*expect
, /* I - Expected attribute */
3403 ipp_tag_t value_tag
) /* I - Value tag for attribute */
3405 int match
; /* Match? */
3406 char *of_type
, /* Type name to match */
3407 *next
, /* Next name to match */
3408 sep
; /* Separator character */
3412 * If we don't expect a particular type, return immediately...
3415 if (!expect
->of_type
)
3419 * Parse the "of_type" value since the string can contain multiple attribute
3420 * types separated by "," or "|"...
3423 for (of_type
= expect
->of_type
, match
= 0; !match
&& *of_type
; of_type
= next
)
3426 * Find the next separator, and set it (temporarily) to nul if present.
3429 for (next
= of_type
; *next
&& *next
!= '|' && *next
!= ','; next
++);
3431 if ((sep
= *next
) != '\0')
3435 * Support some meta-types to make it easier to write the test file.
3438 if (!strcmp(of_type
, "text"))
3439 match
= value_tag
== IPP_TAG_TEXTLANG
|| value_tag
== IPP_TAG_TEXT
;
3440 else if (!strcmp(of_type
, "name"))
3441 match
= value_tag
== IPP_TAG_NAMELANG
|| value_tag
== IPP_TAG_NAME
;
3442 else if (!strcmp(of_type
, "collection"))
3443 match
= value_tag
== IPP_TAG_BEGIN_COLLECTION
;
3445 match
= value_tag
== ippTagValue(of_type
);
3448 * Restore the separator if we have one...
3460 * 'get_collection()' - Get a collection value from the current test file.
3463 static ipp_t
* /* O - Collection value */
3464 get_collection(_cups_vars_t
*vars
, /* I - Variables */
3465 FILE *fp
, /* I - File to read from */
3466 int *linenum
) /* IO - Line number */
3468 char token
[1024], /* Token from file */
3469 temp
[1024], /* Temporary string */
3470 attr
[128]; /* Attribute name */
3471 ipp_tag_t value
; /* Current value type */
3472 ipp_t
*col
= ippNew(); /* Collection value */
3473 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
3476 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
3478 if (!strcmp(token
, "}"))
3480 else if (!strcmp(token
, "{") && lastcol
)
3483 * Another collection value
3486 ipp_t
*subcol
= get_collection(vars
, fp
, linenum
);
3487 /* Collection value */
3490 ippSetCollection(col
, &lastcol
, ippGetCount(lastcol
), subcol
);
3494 else if (!_cups_strcasecmp(token
, "MEMBER"))
3502 if (!get_token(fp
, token
, sizeof(token
), linenum
))
3504 print_fatal_error("Missing MEMBER value tag on line %d.", *linenum
);
3508 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
3510 print_fatal_error("Bad MEMBER value tag \"%s\" on line %d.", token
,
3515 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
3517 print_fatal_error("Missing MEMBER name on line %d.", *linenum
);
3521 if (!get_token(fp
, temp
, sizeof(temp
), linenum
))
3523 print_fatal_error("Missing MEMBER value on line %d.", *linenum
);
3527 expand_variables(vars
, token
, temp
, sizeof(token
));
3531 case IPP_TAG_BOOLEAN
:
3532 if (!_cups_strcasecmp(token
, "true"))
3533 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
3535 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, (char)atoi(token
));
3538 case IPP_TAG_INTEGER
:
3540 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
3543 case IPP_TAG_RESOLUTION
:
3545 int xres
, /* X resolution */
3546 yres
; /* Y resolution */
3547 char units
[6]; /* Units */
3549 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
3550 (_cups_strcasecmp(units
, "dpi") &&
3551 _cups_strcasecmp(units
, "dpc") &&
3552 _cups_strcasecmp(units
, "dpcm") &&
3553 _cups_strcasecmp(units
, "other")))
3555 print_fatal_error("Bad resolution value \"%s\" on line %d.",
3560 if (!_cups_strcasecmp(units
, "dpi"))
3561 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
3562 else if (!_cups_strcasecmp(units
, "dpc") ||
3563 !_cups_strcasecmp(units
, "dpcm"))
3564 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_CM
, xres
, yres
);
3566 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, (ipp_res_t
)0, xres
, yres
);
3570 case IPP_TAG_RANGE
:
3572 int lowers
[4], /* Lower value */
3573 uppers
[4], /* Upper values */
3574 num_vals
; /* Number of values */
3577 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
3578 lowers
+ 0, uppers
+ 0,
3579 lowers
+ 1, uppers
+ 1,
3580 lowers
+ 2, uppers
+ 2,
3581 lowers
+ 3, uppers
+ 3);
3583 if ((num_vals
& 1) || num_vals
== 0)
3585 print_fatal_error("Bad rangeOfInteger value \"%s\" on line %d.",
3590 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
3595 case IPP_TAG_BEGIN_COLLECTION
:
3596 if (!strcmp(token
, "{"))
3598 ipp_t
*subcol
= get_collection(vars
, fp
, linenum
);
3599 /* Collection value */
3603 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
3611 print_fatal_error("Bad collection value on line %d.", *linenum
);
3615 case IPP_TAG_STRING
:
3616 ippAddOctetString(col
, IPP_TAG_ZERO
, attr
, token
, (int)strlen(token
));
3620 if (!strchr(token
, ','))
3621 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
3625 * Multiple string values...
3628 int num_values
; /* Number of values */
3629 char *values
[100], /* Values */
3630 *ptr
; /* Pointer to next value */
3636 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
3639 values
[num_values
] = ptr
;
3643 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
3644 NULL
, (const char **)values
);
3654 * If we get here there was a parse error; free memory and return.
3666 * 'get_filename()' - Get a filename based on the current test file.
3669 static char * /* O - Filename */
3670 get_filename(const char *testfile
, /* I - Current test file */
3671 char *dst
, /* I - Destination filename */
3672 const char *src
, /* I - Source filename */
3673 size_t dstsize
) /* I - Size of destination buffer */
3675 char *dstptr
; /* Pointer into destination */
3676 _cups_globals_t
*cg
= _cupsGlobals();
3680 if (*src
== '<' && src
[strlen(src
) - 1] == '>')
3683 * Map <filename> to CUPS_DATADIR/ipptool/filename...
3686 snprintf(dst
, dstsize
, "%s/ipptool/%s", cg
->cups_datadir
, src
+ 1);
3687 dstptr
= dst
+ strlen(dst
) - 1;
3691 else if (*src
== '/' || !strchr(testfile
, '/')
3693 || (isalpha(*src
& 255) && src
[1] == ':')
3698 * Use the path as-is...
3701 strlcpy(dst
, src
, dstsize
);
3706 * Make path relative to testfile...
3709 strlcpy(dst
, testfile
, dstsize
);
3710 if ((dstptr
= strrchr(dst
, '/')) != NULL
)
3713 dstptr
= dst
; /* Should never happen */
3715 strlcpy(dstptr
, src
, dstsize
- (size_t)(dstptr
- dst
));
3723 * 'get_string()' - Get a pointer to a string value or the portion of interest.
3726 static char * /* O - Pointer to string */
3727 get_string(ipp_attribute_t
*attr
, /* I - IPP attribute */
3728 int element
, /* I - Element to fetch */
3729 int flags
, /* I - Value ("with") flags */
3730 char *buffer
, /* I - Temporary buffer */
3731 size_t bufsize
) /* I - Size of temporary buffer */
3733 char *ptr
, /* Value */
3734 scheme
[256], /* URI scheme */
3735 userpass
[256], /* Username/password */
3736 hostname
[256], /* Hostname */
3737 resource
[1024]; /* Resource */
3738 int port
; /* Port number */
3741 ptr
= attr
->values
[element
].string
.text
;
3743 if (flags
& _CUPS_WITH_HOSTNAME
)
3745 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ptr
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), buffer
, (int)bufsize
, &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
3750 else if (flags
& _CUPS_WITH_RESOURCE
)
3752 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ptr
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, buffer
, (int)bufsize
) < HTTP_URI_STATUS_OK
)
3757 else if (flags
& _CUPS_WITH_SCHEME
)
3759 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ptr
, buffer
, (int)bufsize
, userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
3770 * 'get_token()' - Get a token from a file.
3773 static char * /* O - Token from file or NULL on EOF */
3774 get_token(FILE *fp
, /* I - File to read from */
3775 char *buf
, /* I - Buffer to read into */
3776 int buflen
, /* I - Length of buffer */
3777 int *linenum
) /* IO - Current line number */
3779 int ch
, /* Character from file */
3780 quote
; /* Quoting character */
3781 char *bufptr
, /* Pointer into buffer */
3782 *bufend
; /* End of buffer */
3788 * Skip whitespace...
3791 while (isspace(ch
= getc(fp
)))
3803 else if (ch
== '\'' || ch
== '\"')
3806 * Quoted text or regular expression...
3811 bufend
= buf
+ buflen
- 1;
3813 while ((ch
= getc(fp
)) != EOF
)
3818 * Escape next character...
3821 if (bufptr
< bufend
)
3822 *bufptr
++ = (char)ch
;
3824 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
3825 *bufptr
++ = (char)ch
;
3827 else if (ch
== quote
)
3829 else if (bufptr
< bufend
)
3830 *bufptr
++ = (char)ch
;
3843 while ((ch
= getc(fp
)) != EOF
)
3849 else if (ch
== '{' || ch
== '}' || ch
== ',')
3859 * Whitespace delimited text...
3865 bufend
= buf
+ buflen
- 1;
3867 while ((ch
= getc(fp
)) != EOF
)
3868 if (isspace(ch
) || ch
== '#')
3870 else if (bufptr
< bufend
)
3871 *bufptr
++ = (char)ch
;
3875 else if (ch
== '\n')
3887 * 'get_variable()' - Get the value of a variable.
3890 static char * /* O - Value or NULL */
3891 get_variable(_cups_vars_t
*vars
, /* I - Variables */
3892 const char *name
) /* I - Variable name */
3894 _cups_var_t key
, /* Search key */
3895 *match
; /* Matching variable, if any */
3898 key
.name
= (char *)name
;
3899 match
= cupsArrayFind(vars
->vars
, &key
);
3901 return (match
? match
->value
: NULL
);
3906 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
3910 static char * /* O - ISO 8601 date/time string */
3911 iso_date(ipp_uchar_t
*date
) /* I - IPP (RFC 1903) date/time value */
3913 time_t utctime
; /* UTC time since 1970 */
3914 struct tm
*utcdate
; /* UTC date/time */
3915 static char buffer
[255]; /* String buffer */
3918 utctime
= ippDateToTime(date
);
3919 utcdate
= gmtime(&utctime
);
3921 snprintf(buffer
, sizeof(buffer
), "%04d-%02d-%02dT%02d:%02d:%02dZ",
3922 utcdate
->tm_year
+ 1900, utcdate
->tm_mon
+ 1, utcdate
->tm_mday
,
3923 utcdate
->tm_hour
, utcdate
->tm_min
, utcdate
->tm_sec
);
3930 * 'password_cb()' - Password callback for authenticated tests.
3933 static const char * /* O - Password */
3934 password_cb(const char *prompt
) /* I - Prompt (unused) */
3938 if (PasswordTries
< 3)
3942 cupsSetUser(Username
);
3952 * 'print_attr()' - Print an attribute on the screen.
3956 print_attr(ipp_attribute_t
*attr
, /* I - Attribute to print */
3957 ipp_tag_t
*group
) /* IO - Current group */
3959 int i
; /* Looping var */
3960 ipp_attribute_t
*colattr
; /* Collection attribute */
3963 if (Output
== _CUPS_OUTPUT_PLIST
)
3965 if (!attr
->name
|| (group
&& *group
!= attr
->group_tag
))
3967 if (attr
->group_tag
!= IPP_TAG_ZERO
)
3974 *group
= attr
->group_tag
;
3980 print_xml_string("key", attr
->name
);
3981 if (attr
->num_values
> 1)
3984 switch (attr
->value_tag
)
3986 case IPP_TAG_INTEGER
:
3988 for (i
= 0; i
< attr
->num_values
; i
++)
3989 if (Output
== _CUPS_OUTPUT_PLIST
)
3990 printf("<integer>%d</integer>\n", attr
->values
[i
].integer
);
3992 printf("%d ", attr
->values
[i
].integer
);
3995 case IPP_TAG_BOOLEAN
:
3996 for (i
= 0; i
< attr
->num_values
; i
++)
3997 if (Output
== _CUPS_OUTPUT_PLIST
)
3998 puts(attr
->values
[i
].boolean
? "<true />" : "<false />");
3999 else if (attr
->values
[i
].boolean
)
4000 fputs("true ", stdout
);
4002 fputs("false ", stdout
);
4005 case IPP_TAG_RANGE
:
4006 for (i
= 0; i
< attr
->num_values
; i
++)
4007 if (Output
== _CUPS_OUTPUT_PLIST
)
4008 printf("<dict><key>lower</key><integer>%d</integer>"
4009 "<key>upper</key><integer>%d</integer></dict>\n",
4010 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
4012 printf("%d-%d ", attr
->values
[i
].range
.lower
,
4013 attr
->values
[i
].range
.upper
);
4016 case IPP_TAG_RESOLUTION
:
4017 for (i
= 0; i
< attr
->num_values
; i
++)
4018 if (Output
== _CUPS_OUTPUT_PLIST
)
4019 printf("<dict><key>xres</key><integer>%d</integer>"
4020 "<key>yres</key><integer>%d</integer>"
4021 "<key>units</key><string>%s</string></dict>\n",
4022 attr
->values
[i
].resolution
.xres
,
4023 attr
->values
[i
].resolution
.yres
,
4024 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
4027 printf("%dx%d%s ", attr
->values
[i
].resolution
.xres
,
4028 attr
->values
[i
].resolution
.yres
,
4029 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
4034 for (i
= 0; i
< attr
->num_values
; i
++)
4035 if (Output
== _CUPS_OUTPUT_PLIST
)
4036 printf("<date>%s</date>\n", iso_date(attr
->values
[i
].date
));
4038 printf("%s ", iso_date(attr
->values
[i
].date
));
4041 case IPP_TAG_STRING
:
4042 for (i
= 0; i
< attr
->num_values
; i
++)
4044 if (Output
== _CUPS_OUTPUT_PLIST
)
4046 char buffer
[IPP_MAX_LENGTH
* 5 / 4 + 1];
4049 printf("<data>%s</data>\n",
4050 httpEncode64_2(buffer
, sizeof(buffer
),
4051 attr
->values
[i
].unknown
.data
,
4052 attr
->values
[i
].unknown
.length
));
4056 char *ptr
, /* Pointer into data */
4057 *end
; /* End of data */
4060 for (ptr
= attr
->values
[i
].unknown
.data
,
4061 end
= ptr
+ attr
->values
[i
].unknown
.length
;
4065 if (*ptr
== '\\' || *ptr
== '\"')
4066 printf("\\%c", *ptr
);
4067 else if (!isprint(*ptr
& 255))
4068 printf("\\%03o", *ptr
& 255);
4079 case IPP_TAG_KEYWORD
:
4080 case IPP_TAG_CHARSET
:
4082 case IPP_TAG_MIMETYPE
:
4083 case IPP_TAG_LANGUAGE
:
4084 for (i
= 0; i
< attr
->num_values
; i
++)
4085 if (Output
== _CUPS_OUTPUT_PLIST
)
4086 print_xml_string("string", attr
->values
[i
].string
.text
);
4088 printf("\"%s\" ", attr
->values
[i
].string
.text
);
4091 case IPP_TAG_TEXTLANG
:
4092 case IPP_TAG_NAMELANG
:
4093 for (i
= 0; i
< attr
->num_values
; i
++)
4094 if (Output
== _CUPS_OUTPUT_PLIST
)
4096 fputs("<dict><key>language</key><string>", stdout
);
4097 print_xml_string(NULL
, attr
->values
[i
].string
.language
);
4098 fputs("</string><key>string</key><string>", stdout
);
4099 print_xml_string(NULL
, attr
->values
[i
].string
.text
);
4100 puts("</string></dict>");
4103 printf("\"%s\"[%s] ", attr
->values
[i
].string
.text
,
4104 attr
->values
[i
].string
.language
);
4107 case IPP_TAG_BEGIN_COLLECTION
:
4108 for (i
= 0; i
< attr
->num_values
; i
++)
4110 if (Output
== _CUPS_OUTPUT_PLIST
)
4113 for (colattr
= attr
->values
[i
].collection
->attrs
;
4115 colattr
= colattr
->next
)
4116 print_attr(colattr
, NULL
);
4124 print_col(attr
->values
[i
].collection
);
4130 if (Output
== _CUPS_OUTPUT_PLIST
)
4131 printf("<string><<%s>></string>\n",
4132 ippTagString(attr
->value_tag
));
4134 fputs(ippTagString(attr
->value_tag
), stdout
);
4138 if (attr
->num_values
> 1)
4143 char buffer
[8192]; /* Value buffer */
4145 if (Output
== _CUPS_OUTPUT_TEST
)
4149 puts(" -- separator --");
4153 printf(" %s (%s%s) = ", attr
->name
,
4154 attr
->num_values
> 1 ? "1setOf " : "",
4155 ippTagString(attr
->value_tag
));
4158 ippAttributeString(attr
, buffer
, sizeof(buffer
));
4165 * 'print_col()' - Print a collection attribute on the screen.
4169 print_col(ipp_t
*col
) /* I - Collection attribute to print */
4171 int i
; /* Looping var */
4172 ipp_attribute_t
*attr
; /* Current attribute in collection */
4175 fputs("{ ", stdout
);
4176 for (attr
= col
->attrs
; attr
; attr
= attr
->next
)
4178 printf("%s (%s%s) = ", attr
->name
, attr
->num_values
> 1 ? "1setOf " : "",
4179 ippTagString(attr
->value_tag
));
4181 switch (attr
->value_tag
)
4183 case IPP_TAG_INTEGER
:
4185 for (i
= 0; i
< attr
->num_values
; i
++)
4186 printf("%d ", attr
->values
[i
].integer
);
4189 case IPP_TAG_BOOLEAN
:
4190 for (i
= 0; i
< attr
->num_values
; i
++)
4191 if (attr
->values
[i
].boolean
)
4197 case IPP_TAG_NOVALUE
:
4201 case IPP_TAG_RANGE
:
4202 for (i
= 0; i
< attr
->num_values
; i
++)
4203 printf("%d-%d ", attr
->values
[i
].range
.lower
,
4204 attr
->values
[i
].range
.upper
);
4207 case IPP_TAG_RESOLUTION
:
4208 for (i
= 0; i
< attr
->num_values
; i
++)
4209 printf("%dx%d%s ", attr
->values
[i
].resolution
.xres
,
4210 attr
->values
[i
].resolution
.yres
,
4211 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
4215 case IPP_TAG_STRING
:
4218 case IPP_TAG_KEYWORD
:
4219 case IPP_TAG_CHARSET
:
4221 case IPP_TAG_MIMETYPE
:
4222 case IPP_TAG_LANGUAGE
:
4223 for (i
= 0; i
< attr
->num_values
; i
++)
4224 printf("\"%s\" ", attr
->values
[i
].string
.text
);
4227 case IPP_TAG_TEXTLANG
:
4228 case IPP_TAG_NAMELANG
:
4229 for (i
= 0; i
< attr
->num_values
; i
++)
4230 printf("\"%s\"[%s] ", attr
->values
[i
].string
.text
,
4231 attr
->values
[i
].string
.language
);
4234 case IPP_TAG_BEGIN_COLLECTION
:
4235 for (i
= 0; i
< attr
->num_values
; i
++)
4237 print_col(attr
->values
[i
].collection
);
4243 break; /* anti-compiler-warning-code */
4252 * 'print_csv()' - Print a line of CSV text.
4257 ipp_attribute_t
*attr
, /* I - First attribute for line */
4258 int num_displayed
, /* I - Number of attributes to display */
4259 char **displayed
, /* I - Attributes to display */
4260 size_t *widths
) /* I - Column widths */
4262 int i
; /* Looping var */
4263 size_t maxlength
; /* Max length of all columns */
4264 char *buffer
, /* String buffer */
4265 *bufptr
; /* Pointer into buffer */
4266 ipp_attribute_t
*current
; /* Current attribute */
4270 * Get the maximum string length we have to show and allocate...
4273 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4274 if (widths
[i
] > maxlength
)
4275 maxlength
= widths
[i
];
4279 if ((buffer
= malloc(maxlength
)) == NULL
)
4283 * Loop through the attributes to display...
4288 for (i
= 0; i
< num_displayed
; i
++)
4295 for (current
= attr
; current
; current
= current
->next
)
4299 else if (!strcmp(current
->name
, displayed
[i
]))
4301 ippAttributeString(current
, buffer
, maxlength
);
4306 if (strchr(buffer
, ',') != NULL
|| strchr(buffer
, '\"') != NULL
||
4307 strchr(buffer
, '\\') != NULL
)
4310 for (bufptr
= buffer
; *bufptr
; bufptr
++)
4312 if (*bufptr
== '\\' || *bufptr
== '\"')
4319 fputs(buffer
, stdout
);
4325 for (i
= 0; i
< num_displayed
; i
++)
4330 fputs(displayed
[i
], stdout
);
4340 * 'print_fatal_error()' - Print a fatal error message.
4344 print_fatal_error(const char *s
, /* I - Printf-style format string */
4345 ...) /* I - Additional arguments as needed */
4347 char buffer
[10240]; /* Format buffer */
4348 va_list ap
; /* Pointer to arguments */
4352 * Format the error message...
4356 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
4363 if (Output
== _CUPS_OUTPUT_PLIST
)
4366 print_xml_trailer(0, buffer
);
4369 _cupsLangPrintf(stderr
, "ipptool: %s", buffer
);
4374 * 'print_line()' - Print a line of formatted or CSV text.
4379 ipp_attribute_t
*attr
, /* I - First attribute for line */
4380 int num_displayed
, /* I - Number of attributes to display */
4381 char **displayed
, /* I - Attributes to display */
4382 size_t *widths
) /* I - Column widths */
4384 int i
; /* Looping var */
4385 size_t maxlength
; /* Max length of all columns */
4386 char *buffer
; /* String buffer */
4387 ipp_attribute_t
*current
; /* Current attribute */
4391 * Get the maximum string length we have to show and allocate...
4394 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4395 if (widths
[i
] > maxlength
)
4396 maxlength
= widths
[i
];
4400 if ((buffer
= malloc(maxlength
)) == NULL
)
4404 * Loop through the attributes to display...
4409 for (i
= 0; i
< num_displayed
; i
++)
4416 for (current
= attr
; current
; current
= current
->next
)
4420 else if (!strcmp(current
->name
, displayed
[i
]))
4422 ippAttributeString(current
, buffer
, maxlength
);
4427 printf("%*s", (int)-widths
[i
], buffer
);
4433 for (i
= 0; i
< num_displayed
; i
++)
4438 printf("%*s", (int)-widths
[i
], displayed
[i
]);
4442 for (i
= 0; i
< num_displayed
; i
++)
4447 memset(buffer
, '-', widths
[i
]);
4448 buffer
[widths
[i
]] = '\0';
4449 fputs(buffer
, stdout
);
4459 * 'print_xml_header()' - Print a standard XML plist header.
4463 print_xml_header(void)
4467 puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
4468 puts("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
4469 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
4470 puts("<plist version=\"1.0\">");
4472 puts("<key>ipptoolVersion</key>");
4473 puts("<string>" CUPS_SVERSION
"</string>");
4474 puts("<key>Transfer</key>");
4475 printf("<string>%s</string>\n",
4476 Transfer
== _CUPS_TRANSFER_AUTO
? "auto" :
4477 Transfer
== _CUPS_TRANSFER_CHUNKED
? "chunked" : "length");
4478 puts("<key>Tests</key>");
4487 * 'print_xml_string()' - Print an XML string with escaping.
4491 print_xml_string(const char *element
, /* I - Element name or NULL */
4492 const char *s
) /* I - String to print */
4495 printf("<%s>", element
);
4500 fputs("&", stdout
);
4502 fputs("<", stdout
);
4504 fputs(">", stdout
);
4505 else if ((*s
& 0xe0) == 0xc0)
4508 * Validate UTF-8 two-byte sequence...
4511 if ((s
[1] & 0xc0) != 0x80)
4522 else if ((*s
& 0xf0) == 0xe0)
4525 * Validate UTF-8 three-byte sequence...
4528 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80)
4540 else if ((*s
& 0xf8) == 0xf0)
4543 * Validate UTF-8 four-byte sequence...
4546 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80 ||
4547 (s
[3] & 0xc0) != 0x80)
4560 else if ((*s
& 0x80) || (*s
< ' ' && !isspace(*s
& 255)))
4563 * Invalid control character...
4575 printf("</%s>\n", element
);
4580 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
4584 print_xml_trailer(int success
, /* I - 1 on success, 0 on failure */
4585 const char *message
) /* I - Error message or NULL */
4590 puts("<key>Successful</key>");
4591 puts(success
? "<true />" : "<false />");
4594 puts("<key>ErrorMessage</key>");
4595 print_xml_string("string", message
);
4606 * 'set_variable()' - Set a variable value.
4610 set_variable(_cups_vars_t
*vars
, /* I - Variables */
4611 const char *name
, /* I - Variable name */
4612 const char *value
) /* I - Value string */
4614 _cups_var_t key
, /* Search key */
4615 *var
; /* New variable */
4618 if (!_cups_strcasecmp(name
, "filename"))
4621 free(vars
->filename
);
4623 vars
->filename
= strdup(value
);
4627 key
.name
= (char *)name
;
4628 if ((var
= cupsArrayFind(vars
->vars
, &key
)) != NULL
)
4631 var
->value
= strdup(value
);
4633 else if ((var
= malloc(sizeof(_cups_var_t
))) == NULL
)
4635 print_fatal_error("Unable to allocate memory for variable \"%s\".", name
);
4640 var
->name
= strdup(name
);
4641 var
->value
= strdup(value
);
4643 cupsArrayAdd(vars
->vars
, var
);
4650 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
4654 sigterm_handler(int sig
) /* I - Signal number (unused) */
4660 signal(SIGINT
, SIG_DFL
);
4661 signal(SIGTERM
, SIG_DFL
);
4667 * 'timeout_cb()' - Handle HTTP timeouts.
4670 static int /* O - 1 to continue, 0 to cancel */
4671 timeout_cb(http_t
*http
, /* I - Connection to server */
4672 void *user_data
) /* I - User data (unused) */
4674 int buffered
= 0; /* Bytes buffered but not yet sent */
4680 * If the socket still have data waiting to be sent to the printer (as can
4681 * happen if the printer runs out of paper), continue to wait until the output
4682 * buffer is empty...
4685 #ifdef SO_NWRITE /* OS X and some versions of Linux */
4686 socklen_t len
= sizeof(buffered
); /* Size of return value */
4688 if (getsockopt(httpGetFd(http
), SOL_SOCKET
, SO_NWRITE
, &buffered
, &len
))
4691 #elif defined(SIOCOUTQ) /* Others except Windows */
4692 if (ioctl(httpGetFd(http
), SIOCOUTQ
, &buffered
))
4695 #else /* Windows (not possible) */
4697 #endif /* SO_NWRITE */
4699 return (buffered
> 0);
4704 * 'usage()' - Show program usage.
4710 _cupsLangPuts(stderr
, _("Usage: ipptool [options] URI filename [ ... "
4712 _cupsLangPuts(stderr
, _("Options:"));
4713 _cupsLangPuts(stderr
, _(" -4 Connect using IPv4."));
4714 _cupsLangPuts(stderr
, _(" -6 Connect using IPv6."));
4715 _cupsLangPuts(stderr
, _(" -C Send requests using "
4716 "chunking (default)."));
4717 _cupsLangPuts(stdout
, _(" -E Test with HTTP Upgrade to "
4719 _cupsLangPuts(stderr
, _(" -I Ignore errors."));
4720 _cupsLangPuts(stderr
, _(" -L Send requests using "
4721 "content-length."));
4722 _cupsLangPuts(stderr
, _(" -S Test with SSL "
4724 _cupsLangPuts(stderr
, _(" -T seconds Set the receive/send "
4725 "timeout in seconds."));
4726 _cupsLangPuts(stderr
, _(" -V version Set default IPP "
4728 _cupsLangPuts(stderr
, _(" -X Produce XML plist instead "
4730 _cupsLangPuts(stderr
, _(" -d name=value Set named variable to "
4732 _cupsLangPuts(stderr
, _(" -f filename Set default request "
4734 _cupsLangPuts(stderr
, _(" -i seconds Repeat the last file with "
4735 "the given time interval."));
4736 _cupsLangPuts(stderr
, _(" -n count Repeat the last file the "
4737 "given number of times."));
4738 _cupsLangPuts(stderr
, _(" -q Run silently."));
4739 _cupsLangPuts(stderr
, _(" -t Produce a test report."));
4740 _cupsLangPuts(stderr
, _(" -v Be verbose."));
4747 * 'validate_attr()' - Determine whether an attribute is valid.
4750 static int /* O - 1 if valid, 0 otherwise */
4751 validate_attr(cups_array_t
*errors
, /* I - Errors array */
4752 ipp_attribute_t
*attr
) /* I - Attribute to validate */
4754 int i
; /* Looping var */
4755 char scheme
[64], /* Scheme from URI */
4756 userpass
[256], /* Username/password from URI */
4757 hostname
[256], /* Hostname from URI */
4758 resource
[1024]; /* Resource from URI */
4759 int port
, /* Port number from URI */
4760 uri_status
, /* URI separation status */
4761 valid
= 1; /* Is the attribute valid? */
4762 const char *ptr
; /* Pointer into string */
4763 ipp_attribute_t
*colattr
; /* Collection attribute */
4764 regex_t re
; /* Regular expression */
4765 ipp_uchar_t
*date
; /* Current date value */
4776 * Validate the attribute name.
4779 for (ptr
= attr
->name
; *ptr
; ptr
++)
4780 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' && *ptr
!= '_')
4783 if (*ptr
|| ptr
== attr
->name
)
4788 "\"%s\": Bad attribute name - invalid character "
4789 "(RFC 2911 section 4.1.3).", attr
->name
);
4792 if ((ptr
- attr
->name
) > 255)
4797 "\"%s\": Bad attribute name - bad length "
4798 "(RFC 2911 section 4.1.3).", attr
->name
);
4801 switch (attr
->value_tag
)
4803 case IPP_TAG_INTEGER
:
4806 case IPP_TAG_BOOLEAN
:
4807 for (i
= 0; i
< attr
->num_values
; i
++)
4809 if (attr
->values
[i
].boolean
!= 0 &&
4810 attr
->values
[i
].boolean
!= 1)
4815 "\"%s\": Bad boolen value %d "
4816 "(RFC 2911 section 4.1.11).", attr
->name
,
4817 attr
->values
[i
].boolean
);
4823 for (i
= 0; i
< attr
->num_values
; i
++)
4825 if (attr
->values
[i
].integer
< 1)
4830 "\"%s\": Bad enum value %d - out of range "
4831 "(RFC 2911 section 4.1.4).", attr
->name
,
4832 attr
->values
[i
].integer
);
4837 case IPP_TAG_STRING
:
4838 for (i
= 0; i
< attr
->num_values
; i
++)
4840 if (attr
->values
[i
].unknown
.length
> IPP_MAX_OCTETSTRING
)
4845 "\"%s\": Bad octetString value - bad length %d "
4846 "(RFC 2911 section 4.1.10).", attr
->name
,
4847 attr
->values
[i
].unknown
.length
);
4853 for (i
= 0; i
< attr
->num_values
; i
++)
4855 date
= attr
->values
[i
].date
;
4857 if (date
[2] < 1 || date
[2] > 12)
4862 "\"%s\": Bad dateTime month %u "
4863 "(RFC 2911 section 4.1.14).", attr
->name
, date
[2]);
4866 if (date
[3] < 1 || date
[3] > 31)
4871 "\"%s\": Bad dateTime day %u "
4872 "(RFC 2911 section 4.1.14).", attr
->name
, date
[3]);
4880 "\"%s\": Bad dateTime hours %u "
4881 "(RFC 2911 section 4.1.14).", attr
->name
, date
[4]);
4889 "\"%s\": Bad dateTime minutes %u "
4890 "(RFC 2911 section 4.1.14).", attr
->name
, date
[5]);
4898 "\"%s\": Bad dateTime seconds %u "
4899 "(RFC 2911 section 4.1.14).", attr
->name
, date
[6]);
4907 "\"%s\": Bad dateTime deciseconds %u "
4908 "(RFC 2911 section 4.1.14).", attr
->name
, date
[7]);
4911 if (date
[8] != '-' && date
[8] != '+')
4916 "\"%s\": Bad dateTime UTC sign '%c' "
4917 "(RFC 2911 section 4.1.14).", attr
->name
, date
[8]);
4925 "\"%s\": Bad dateTime UTC hours %u "
4926 "(RFC 2911 section 4.1.14).", attr
->name
, date
[9]);
4934 "\"%s\": Bad dateTime UTC minutes %u "
4935 "(RFC 2911 section 4.1.14).", attr
->name
, date
[10]);
4940 case IPP_TAG_RESOLUTION
:
4941 for (i
= 0; i
< attr
->num_values
; i
++)
4943 if (attr
->values
[i
].resolution
.xres
<= 0)
4948 "\"%s\": Bad resolution value %dx%d%s - cross "
4949 "feed resolution must be positive "
4950 "(RFC 2911 section 4.1.15).", attr
->name
,
4951 attr
->values
[i
].resolution
.xres
,
4952 attr
->values
[i
].resolution
.yres
,
4953 attr
->values
[i
].resolution
.units
==
4954 IPP_RES_PER_INCH
? "dpi" :
4955 attr
->values
[i
].resolution
.units
==
4956 IPP_RES_PER_CM
? "dpcm" : "unknown");
4959 if (attr
->values
[i
].resolution
.yres
<= 0)
4964 "\"%s\": Bad resolution value %dx%d%s - feed "
4965 "resolution must be positive "
4966 "(RFC 2911 section 4.1.15).", attr
->name
,
4967 attr
->values
[i
].resolution
.xres
,
4968 attr
->values
[i
].resolution
.yres
,
4969 attr
->values
[i
].resolution
.units
==
4970 IPP_RES_PER_INCH
? "dpi" :
4971 attr
->values
[i
].resolution
.units
==
4972 IPP_RES_PER_CM
? "dpcm" : "unknown");
4975 if (attr
->values
[i
].resolution
.units
!= IPP_RES_PER_INCH
&&
4976 attr
->values
[i
].resolution
.units
!= IPP_RES_PER_CM
)
4981 "\"%s\": Bad resolution value %dx%d%s - bad "
4982 "units value (RFC 2911 section 4.1.15).",
4983 attr
->name
, attr
->values
[i
].resolution
.xres
,
4984 attr
->values
[i
].resolution
.yres
,
4985 attr
->values
[i
].resolution
.units
==
4986 IPP_RES_PER_INCH
? "dpi" :
4987 attr
->values
[i
].resolution
.units
==
4988 IPP_RES_PER_CM
? "dpcm" : "unknown");
4993 case IPP_TAG_RANGE
:
4994 for (i
= 0; i
< attr
->num_values
; i
++)
4996 if (attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
5001 "\"%s\": Bad rangeOfInteger value %d-%d - lower "
5002 "greater than upper (RFC 2911 section 4.1.13).",
5003 attr
->name
, attr
->values
[i
].range
.lower
,
5004 attr
->values
[i
].range
.upper
);
5009 case IPP_TAG_BEGIN_COLLECTION
:
5010 for (i
= 0; i
< attr
->num_values
; i
++)
5012 for (colattr
= attr
->values
[i
].collection
->attrs
;
5014 colattr
= colattr
->next
)
5016 if (!validate_attr(NULL
, colattr
))
5023 if (colattr
&& errors
)
5025 add_stringf(errors
, "\"%s\": Bad collection value.", attr
->name
);
5029 validate_attr(errors
, colattr
);
5030 colattr
= colattr
->next
;
5037 case IPP_TAG_TEXTLANG
:
5038 for (i
= 0; i
< attr
->num_values
; i
++)
5040 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5042 if ((*ptr
& 0xe0) == 0xc0)
5045 if ((*ptr
& 0xc0) != 0x80)
5048 else if ((*ptr
& 0xf0) == 0xe0)
5051 if ((*ptr
& 0xc0) != 0x80)
5054 if ((*ptr
& 0xc0) != 0x80)
5057 else if ((*ptr
& 0xf8) == 0xf0)
5060 if ((*ptr
& 0xc0) != 0x80)
5063 if ((*ptr
& 0xc0) != 0x80)
5066 if ((*ptr
& 0xc0) != 0x80)
5069 else if (*ptr
& 0x80)
5078 "\"%s\": Bad text value \"%s\" - bad UTF-8 "
5079 "sequence (RFC 2911 section 4.1.1).", attr
->name
,
5080 attr
->values
[i
].string
.text
);
5083 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_TEXT
- 1))
5088 "\"%s\": Bad text value \"%s\" - bad length %d "
5089 "(RFC 2911 section 4.1.1).", attr
->name
,
5090 attr
->values
[i
].string
.text
,
5091 (int)strlen(attr
->values
[i
].string
.text
));
5097 case IPP_TAG_NAMELANG
:
5098 for (i
= 0; i
< attr
->num_values
; i
++)
5100 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5102 if ((*ptr
& 0xe0) == 0xc0)
5105 if ((*ptr
& 0xc0) != 0x80)
5108 else if ((*ptr
& 0xf0) == 0xe0)
5111 if ((*ptr
& 0xc0) != 0x80)
5114 if ((*ptr
& 0xc0) != 0x80)
5117 else if ((*ptr
& 0xf8) == 0xf0)
5120 if ((*ptr
& 0xc0) != 0x80)
5123 if ((*ptr
& 0xc0) != 0x80)
5126 if ((*ptr
& 0xc0) != 0x80)
5129 else if (*ptr
& 0x80)
5138 "\"%s\": Bad name value \"%s\" - bad UTF-8 "
5139 "sequence (RFC 2911 section 4.1.2).", attr
->name
,
5140 attr
->values
[i
].string
.text
);
5143 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_NAME
- 1))
5148 "\"%s\": Bad name value \"%s\" - bad length %d "
5149 "(RFC 2911 section 4.1.2).", attr
->name
,
5150 attr
->values
[i
].string
.text
,
5151 (int)strlen(attr
->values
[i
].string
.text
));
5156 case IPP_TAG_KEYWORD
:
5157 for (i
= 0; i
< attr
->num_values
; i
++)
5159 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5160 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' &&
5164 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5169 "\"%s\": Bad keyword value \"%s\" - invalid "
5170 "character (RFC 2911 section 4.1.3).",
5171 attr
->name
, attr
->values
[i
].string
.text
);
5174 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_KEYWORD
- 1))
5179 "\"%s\": Bad keyword value \"%s\" - bad "
5180 "length %d (RFC 2911 section 4.1.3).",
5181 attr
->name
, attr
->values
[i
].string
.text
,
5182 (int)strlen(attr
->values
[i
].string
.text
));
5188 for (i
= 0; i
< attr
->num_values
; i
++)
5190 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
,
5191 attr
->values
[i
].string
.text
,
5192 scheme
, sizeof(scheme
),
5193 userpass
, sizeof(userpass
),
5194 hostname
, sizeof(hostname
),
5195 &port
, resource
, sizeof(resource
));
5197 if (uri_status
< HTTP_URI_OK
)
5202 "\"%s\": Bad URI value \"%s\" - %s "
5203 "(RFC 2911 section 4.1.5).", attr
->name
,
5204 attr
->values
[i
].string
.text
,
5205 httpURIStatusString(uri_status
));
5208 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_URI
- 1))
5213 "\"%s\": Bad URI value \"%s\" - bad length %d "
5214 "(RFC 2911 section 4.1.5).", attr
->name
,
5215 attr
->values
[i
].string
.text
,
5216 (int)strlen(attr
->values
[i
].string
.text
));
5221 case IPP_TAG_URISCHEME
:
5222 for (i
= 0; i
< attr
->num_values
; i
++)
5224 ptr
= attr
->values
[i
].string
.text
;
5225 if (islower(*ptr
& 255))
5227 for (ptr
++; *ptr
; ptr
++)
5228 if (!islower(*ptr
& 255) && !isdigit(*ptr
& 255) &&
5229 *ptr
!= '+' && *ptr
!= '-' && *ptr
!= '.')
5233 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5238 "\"%s\": Bad uriScheme value \"%s\" - bad "
5239 "characters (RFC 2911 section 4.1.6).",
5240 attr
->name
, attr
->values
[i
].string
.text
);
5243 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_URISCHEME
- 1))
5248 "\"%s\": Bad uriScheme value \"%s\" - bad "
5249 "length %d (RFC 2911 section 4.1.6).",
5250 attr
->name
, attr
->values
[i
].string
.text
,
5251 (int)strlen(attr
->values
[i
].string
.text
));
5256 case IPP_TAG_CHARSET
:
5257 for (i
= 0; i
< attr
->num_values
; i
++)
5259 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5260 if (!isprint(*ptr
& 255) || isupper(*ptr
& 255) ||
5261 isspace(*ptr
& 255))
5264 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5269 "\"%s\": Bad charset value \"%s\" - bad "
5270 "characters (RFC 2911 section 4.1.7).",
5271 attr
->name
, attr
->values
[i
].string
.text
);
5274 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_CHARSET
- 1))
5279 "\"%s\": Bad charset value \"%s\" - bad "
5280 "length %d (RFC 2911 section 4.1.7).",
5281 attr
->name
, attr
->values
[i
].string
.text
,
5282 (int)strlen(attr
->values
[i
].string
.text
));
5287 case IPP_TAG_LANGUAGE
:
5289 * The following regular expression is derived from the ABNF for
5290 * language tags in RFC 4646. All I can say is that this is the
5291 * easiest way to check the values...
5294 if ((i
= regcomp(&re
,
5296 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
5298 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
5299 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
5300 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
5301 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
5302 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
5304 "x(-[a-z0-9]{1,8})+" /* privateuse */
5306 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
5308 REG_NOSUB
| REG_EXTENDED
)) != 0)
5310 char temp
[256]; /* Temporary error string */
5312 regerror(i
, &re
, temp
, sizeof(temp
));
5313 print_fatal_error("Unable to compile naturalLanguage regular "
5314 "expression: %s.", temp
);
5318 for (i
= 0; i
< attr
->num_values
; i
++)
5320 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5325 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5326 "characters (RFC 2911 section 4.1.8).",
5327 attr
->name
, attr
->values
[i
].string
.text
);
5330 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_LANGUAGE
- 1))
5335 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5336 "length %d (RFC 2911 section 4.1.8).",
5337 attr
->name
, attr
->values
[i
].string
.text
,
5338 (int)strlen(attr
->values
[i
].string
.text
));
5345 case IPP_TAG_MIMETYPE
:
5347 * The following regular expression is derived from the ABNF for
5348 * language tags in RFC 2045 and 4288. All I can say is that this is
5349 * the easiest way to check the values...
5352 if ((i
= regcomp(&re
,
5354 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
5356 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
5357 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
5358 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
5361 REG_NOSUB
| REG_EXTENDED
)) != 0)
5363 char temp
[256]; /* Temporary error string */
5365 regerror(i
, &re
, temp
, sizeof(temp
));
5366 print_fatal_error("Unable to compile mimeMediaType regular "
5367 "expression: %s.", temp
);
5371 for (i
= 0; i
< attr
->num_values
; i
++)
5373 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5378 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5379 "characters (RFC 2911 section 4.1.9).",
5380 attr
->name
, attr
->values
[i
].string
.text
);
5383 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_MIMETYPE
- 1))
5388 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5389 "length %d (RFC 2911 section 4.1.9).",
5390 attr
->name
, attr
->values
[i
].string
.text
,
5391 (int)strlen(attr
->values
[i
].string
.text
));
5407 * 'with_value()' - Test a WITH-VALUE predicate.
5410 static int /* O - 1 on match, 0 on non-match */
5411 with_value(cups_array_t
*errors
, /* I - Errors array */
5412 char *value
, /* I - Value string */
5413 int flags
, /* I - Flags for match */
5414 ipp_attribute_t
*attr
, /* I - Attribute to compare */
5415 char *matchbuf
, /* I - Buffer to hold matching value */
5416 size_t matchlen
) /* I - Length of match buffer */
5418 int i
, /* Looping var */
5420 char temp
[1024], /* Temporary value string */
5421 *valptr
; /* Pointer into value */
5425 match
= (flags
& _CUPS_WITH_ALL
) ? 1 : 0;
5428 * NULL matches everything.
5431 if (!value
|| !*value
)
5435 * Compare the value string to the attribute value.
5438 switch (attr
->value_tag
)
5440 case IPP_TAG_INTEGER
:
5442 for (i
= 0; i
< attr
->num_values
; i
++)
5444 char op
, /* Comparison operator */
5445 *nextptr
; /* Next pointer */
5446 int intvalue
, /* Integer value */
5447 valmatch
= 0; /* Does the current value match? */
5451 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5452 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5453 *valptr
== '=' || *valptr
== '>')
5456 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5458 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5466 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
5467 if (nextptr
== valptr
)
5471 if ((op
== '=' && attr
->values
[i
].integer
== intvalue
) ||
5472 (op
== '<' && attr
->values
[i
].integer
< intvalue
) ||
5473 (op
== '>' && attr
->values
[i
].integer
> intvalue
))
5476 snprintf(matchbuf
, matchlen
, "%d", attr
->values
[i
].integer
);
5483 if (flags
& _CUPS_WITH_ALL
)
5498 if (!match
&& errors
)
5500 for (i
= 0; i
< attr
->num_values
; i
++)
5501 add_stringf(errors
, "GOT: %s=%d", attr
->name
,
5502 attr
->values
[i
].integer
);
5506 case IPP_TAG_RANGE
:
5507 for (i
= 0; i
< attr
->num_values
; i
++)
5509 char op
, /* Comparison operator */
5510 *nextptr
; /* Next pointer */
5511 int intvalue
, /* Integer value */
5512 valmatch
= 0; /* Does the current value match? */
5516 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5517 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5518 *valptr
== '=' || *valptr
== '>')
5521 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5523 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5531 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
5532 if (nextptr
== valptr
)
5536 if ((op
== '=' && (attr
->values
[i
].range
.lower
== intvalue
||
5537 attr
->values
[i
].range
.upper
== intvalue
)) ||
5538 (op
== '<' && attr
->values
[i
].range
.upper
< intvalue
) ||
5539 (op
== '>' && attr
->values
[i
].range
.upper
> intvalue
))
5542 snprintf(matchbuf
, matchlen
, "%d-%d",
5543 attr
->values
[0].range
.lower
,
5544 attr
->values
[0].range
.upper
);
5551 if (flags
& _CUPS_WITH_ALL
)
5566 if (!match
&& errors
)
5568 for (i
= 0; i
< attr
->num_values
; i
++)
5569 add_stringf(errors
, "GOT: %s=%d-%d", attr
->name
,
5570 attr
->values
[i
].range
.lower
,
5571 attr
->values
[i
].range
.upper
);
5575 case IPP_TAG_BOOLEAN
:
5576 for (i
= 0; i
< attr
->num_values
; i
++)
5578 if (!strcmp(value
, "true") == attr
->values
[i
].boolean
)
5581 strlcpy(matchbuf
, value
, matchlen
);
5583 if (!(flags
& _CUPS_WITH_ALL
))
5589 else if (flags
& _CUPS_WITH_ALL
)
5596 if (!match
&& errors
)
5598 for (i
= 0; i
< attr
->num_values
; i
++)
5599 add_stringf(errors
, "GOT: %s=%s", attr
->name
,
5600 attr
->values
[i
].boolean
? "true" : "false");
5604 case IPP_TAG_RESOLUTION
:
5605 for (i
= 0; i
< attr
->num_values
; i
++)
5607 if (attr
->values
[i
].resolution
.xres
==
5608 attr
->values
[i
].resolution
.yres
)
5609 snprintf(temp
, sizeof(temp
), "%d%s",
5610 attr
->values
[i
].resolution
.xres
,
5611 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5614 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5615 attr
->values
[i
].resolution
.xres
,
5616 attr
->values
[i
].resolution
.yres
,
5617 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5620 if (!strcmp(value
, temp
))
5623 strlcpy(matchbuf
, value
, matchlen
);
5625 if (!(flags
& _CUPS_WITH_ALL
))
5631 else if (flags
& _CUPS_WITH_ALL
)
5638 if (!match
&& errors
)
5640 for (i
= 0; i
< attr
->num_values
; i
++)
5642 if (attr
->values
[i
].resolution
.xres
==
5643 attr
->values
[i
].resolution
.yres
)
5644 snprintf(temp
, sizeof(temp
), "%d%s",
5645 attr
->values
[i
].resolution
.xres
,
5646 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5649 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5650 attr
->values
[i
].resolution
.xres
,
5651 attr
->values
[i
].resolution
.yres
,
5652 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5655 if (strcmp(value
, temp
))
5656 add_stringf(errors
, "GOT: %s=%s", attr
->name
, temp
);
5661 case IPP_TAG_NOVALUE
:
5662 case IPP_TAG_UNKNOWN
:
5665 case IPP_TAG_CHARSET
:
5666 case IPP_TAG_KEYWORD
:
5667 case IPP_TAG_LANGUAGE
:
5668 case IPP_TAG_MIMETYPE
:
5670 case IPP_TAG_NAMELANG
:
5672 case IPP_TAG_TEXTLANG
:
5674 case IPP_TAG_URISCHEME
:
5675 if (flags
& _CUPS_WITH_REGEX
)
5678 * Value is an extended, case-sensitive POSIX regular expression...
5681 regex_t re
; /* Regular expression */
5683 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
5685 regerror(i
, &re
, temp
, sizeof(temp
));
5687 print_fatal_error("Unable to compile WITH-VALUE regular expression "
5688 "\"%s\" - %s", value
, temp
);
5693 * See if ALL of the values match the given regular expression.
5696 for (i
= 0; i
< attr
->num_values
; i
++)
5698 if (!regexec(&re
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5703 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5706 if (!(flags
& _CUPS_WITH_ALL
))
5712 else if (flags
& _CUPS_WITH_ALL
)
5724 * Value is a literal string, see if the value(s) match...
5727 for (i
= 0; i
< attr
->num_values
; i
++)
5729 if (!strcmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
))))
5733 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5736 if (!(flags
& _CUPS_WITH_ALL
))
5742 else if (flags
& _CUPS_WITH_ALL
)
5750 if (!match
&& errors
)
5752 for (i
= 0; i
< attr
->num_values
; i
++)
5753 add_stringf(errors
, "GOT: %s=\"%s\"", attr
->name
,
5754 attr
->values
[i
].string
.text
);