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 *Password
= NULL
; /* Password from URI */
148 static void add_stringf(cups_array_t
*a
, const char *s
, ...)
149 __attribute__ ((__format__ (__printf__
, 2, 3)));
150 static int compare_vars(_cups_var_t
*a
, _cups_var_t
*b
);
151 static int do_tests(_cups_vars_t
*vars
, const char *testfile
);
152 static void expand_variables(_cups_vars_t
*vars
, char *dst
, const char *src
,
153 size_t dstsize
) __attribute__((nonnull(1,2,3)));
154 static int expect_matches(_cups_expect_t
*expect
, ipp_tag_t value_tag
);
155 static ipp_t
*get_collection(_cups_vars_t
*vars
, FILE *fp
, int *linenum
);
156 static char *get_filename(const char *testfile
, char *dst
, const char *src
,
158 static char *get_string(ipp_attribute_t
*attr
, int element
, int flags
,
159 char *buffer
, size_t bufsize
);
160 static char *get_token(FILE *fp
, char *buf
, int buflen
,
162 static char *get_variable(_cups_vars_t
*vars
, const char *name
);
163 static char *iso_date(ipp_uchar_t
*date
);
164 static const char *password_cb(const char *prompt
);
165 static void print_attr(ipp_attribute_t
*attr
, ipp_tag_t
*group
);
166 static void print_col(ipp_t
*col
);
167 static void print_csv(ipp_attribute_t
*attr
, int num_displayed
,
168 char **displayed
, size_t *widths
);
169 static void print_fatal_error(const char *s
, ...)
170 __attribute__ ((__format__ (__printf__
, 1, 2)));
171 static void print_line(ipp_attribute_t
*attr
, int num_displayed
,
172 char **displayed
, size_t *widths
);
173 static void print_xml_header(void);
174 static void print_xml_string(const char *element
, const char *s
);
175 static void print_xml_trailer(int success
, const char *message
);
176 static void set_variable(_cups_vars_t
*vars
, const char *name
,
179 static void sigterm_handler(int sig
);
181 static int timeout_cb(http_t
*http
, void *user_data
);
182 static void usage(void) __attribute__((noreturn
));
183 static int validate_attr(cups_array_t
*errors
, ipp_attribute_t
*attr
);
184 static int with_value(cups_array_t
*errors
, char *value
, int flags
,
185 ipp_attribute_t
*attr
, char *matchbuf
,
190 * 'main()' - Parse options and do tests.
193 int /* O - Exit status */
194 main(int argc
, /* I - Number of command-line args */
195 char *argv
[]) /* I - Command-line arguments */
197 int i
; /* Looping var */
198 int status
; /* Status of tests... */
199 char *opt
, /* Current option */
200 name
[1024], /* Name/value buffer */
201 *value
, /* Pointer to value */
202 filename
[1024], /* Real filename */
203 testname
[1024], /* Real test filename */
204 uri
[1024]; /* Copy of printer URI */
205 const char *ext
, /* Extension on filename */
206 *testfile
; /* Test file to use */
207 int interval
, /* Test interval in microseconds */
208 repeat
; /* Repeat count */
209 _cups_vars_t vars
; /* Variables */
210 http_uri_status_t uri_status
; /* URI separation status */
211 _cups_globals_t
*cg
= _cupsGlobals();
217 * Catch SIGINT and SIGTERM...
220 signal(SIGINT
, sigterm_handler
);
221 signal(SIGTERM
, sigterm_handler
);
225 * Initialize the locale and variables...
228 _cupsSetLocale(argv
);
230 memset(&vars
, 0, sizeof(vars
));
231 vars
.family
= AF_UNSPEC
;
232 vars
.vars
= cupsArrayNew((cups_array_func_t
)compare_vars
, NULL
);
237 * ipptool URI testfile
245 for (i
= 1; i
< argc
; i
++)
247 if (!strcmp(argv
[i
], "--help"))
251 else if (!strcmp(argv
[i
], "--stop-after-include-error"))
253 StopAfterIncludeError
= 1;
255 else if (!strcmp(argv
[i
], "--version"))
260 else if (argv
[i
][0] == '-')
262 for (opt
= argv
[i
] + 1; *opt
; opt
++)
266 case '4' : /* Connect using IPv4 only */
267 vars
.family
= AF_INET
;
271 case '6' : /* Connect using IPv6 only */
272 vars
.family
= AF_INET6
;
274 #endif /* AF_INET6 */
276 case 'C' : /* Enable HTTP chunking */
277 Transfer
= _CUPS_TRANSFER_CHUNKED
;
280 case 'E' : /* Encrypt with TLS */
282 vars
.encryption
= HTTP_ENCRYPT_REQUIRED
;
284 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
286 #endif /* HAVE_SSL */
289 case 'I' : /* Ignore errors */
293 case 'L' : /* Disable HTTP chunking */
294 Transfer
= _CUPS_TRANSFER_LENGTH
;
297 case 'S' : /* Encrypt with SSL */
299 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
301 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
303 #endif /* HAVE_SSL */
306 case 'T' : /* Set timeout */
311 _cupsLangPrintf(stderr
,
312 _("%s: Missing timeout for \"-T\"."),
317 vars
.timeout
= _cupsStrScand(argv
[i
], NULL
, localeconv());
320 case 'V' : /* Set IPP version */
325 _cupsLangPrintf(stderr
,
326 _("%s: Missing version for \"-V\"."),
331 if (!strcmp(argv
[i
], "1.0"))
333 else if (!strcmp(argv
[i
], "1.1"))
335 else if (!strcmp(argv
[i
], "2.0"))
337 else if (!strcmp(argv
[i
], "2.1"))
339 else if (!strcmp(argv
[i
], "2.2"))
343 _cupsLangPrintf(stderr
,
344 _("%s: Bad version %s for \"-V\"."),
350 case 'X' : /* Produce XML output */
351 Output
= _CUPS_OUTPUT_PLIST
;
353 if (interval
|| repeat
)
355 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are "
356 "incompatible with -X\"."));
361 case 'c' : /* CSV output */
362 Output
= _CUPS_OUTPUT_CSV
;
365 case 'd' : /* Define a variable */
370 _cupsLangPuts(stderr
,
371 _("ipptool: Missing name=value for \"-d\"."));
375 strlcpy(name
, argv
[i
], sizeof(name
));
376 if ((value
= strchr(name
, '=')) != NULL
)
379 value
= name
+ strlen(name
);
381 set_variable(&vars
, name
, value
);
384 case 'f' : /* Set the default test filename */
389 _cupsLangPuts(stderr
,
390 _("ipptool: Missing filename for \"-f\"."));
397 vars
.filename
= NULL
;
400 if (access(argv
[i
], 0))
406 snprintf(filename
, sizeof(filename
), "%s.gz", argv
[i
]);
407 if (access(filename
, 0) && filename
[0] != '/'
409 && (!isalpha(filename
[0] & 255) || filename
[1] != ':')
413 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s",
414 cg
->cups_datadir
, argv
[i
]);
415 if (access(filename
, 0))
417 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s.gz",
418 cg
->cups_datadir
, argv
[i
]);
419 if (access(filename
, 0))
420 vars
.filename
= strdup(argv
[i
]);
422 vars
.filename
= strdup(filename
);
425 vars
.filename
= strdup(filename
);
428 vars
.filename
= strdup(filename
);
431 vars
.filename
= strdup(argv
[i
]);
433 if ((ext
= strrchr(vars
.filename
, '.')) != NULL
)
436 * Guess the MIME media type based on the extension...
439 if (!_cups_strcasecmp(ext
, ".gif"))
440 set_variable(&vars
, "filetype", "image/gif");
441 else if (!_cups_strcasecmp(ext
, ".htm") ||
442 !_cups_strcasecmp(ext
, ".htm.gz") ||
443 !_cups_strcasecmp(ext
, ".html") ||
444 !_cups_strcasecmp(ext
, ".html.gz"))
445 set_variable(&vars
, "filetype", "text/html");
446 else if (!_cups_strcasecmp(ext
, ".jpg"))
447 set_variable(&vars
, "filetype", "image/jpeg");
448 else if (!_cups_strcasecmp(ext
, ".pcl") ||
449 !_cups_strcasecmp(ext
, ".pcl.gz"))
450 set_variable(&vars
, "filetype", "application/vnd.hp-PCL");
451 else if (!_cups_strcasecmp(ext
, ".pdf"))
452 set_variable(&vars
, "filetype", "application/pdf");
453 else if (!_cups_strcasecmp(ext
, ".png"))
454 set_variable(&vars
, "filetype", "image/png");
455 else if (!_cups_strcasecmp(ext
, ".ps") ||
456 !_cups_strcasecmp(ext
, ".ps.gz"))
457 set_variable(&vars
, "filetype", "application/postscript");
458 else if (!_cups_strcasecmp(ext
, ".pwg") ||
459 !_cups_strcasecmp(ext
, ".pwg.gz") ||
460 !_cups_strcasecmp(ext
, ".ras") ||
461 !_cups_strcasecmp(ext
, ".ras.gz"))
462 set_variable(&vars
, "filetype", "image/pwg-raster");
463 else if (!_cups_strcasecmp(ext
, ".txt") ||
464 !_cups_strcasecmp(ext
, ".txt.gz"))
465 set_variable(&vars
, "filetype", "text/plain");
466 else if (!_cups_strcasecmp(ext
, ".xps"))
467 set_variable(&vars
, "filetype", "application/openxps");
469 set_variable(&vars
, "filetype", "application/octet-stream");
474 * Use the "auto-type" MIME media type...
477 set_variable(&vars
, "filetype", "application/octet-stream");
481 case 'i' : /* Test every N seconds */
486 _cupsLangPuts(stderr
,
487 _("ipptool: Missing seconds for \"-i\"."));
492 interval
= (int)(_cupsStrScand(argv
[i
], NULL
, localeconv()) *
496 _cupsLangPuts(stderr
,
497 _("ipptool: Invalid seconds for \"-i\"."));
502 if (Output
== _CUPS_OUTPUT_PLIST
&& interval
)
504 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are "
505 "incompatible with -X\"."));
510 case 'l' : /* List as a table */
511 Output
= _CUPS_OUTPUT_LIST
;
514 case 'n' : /* Repeat count */
519 _cupsLangPuts(stderr
,
520 _("ipptool: Missing count for \"-n\"."));
524 repeat
= atoi(argv
[i
]);
526 if (Output
== _CUPS_OUTPUT_PLIST
&& repeat
)
528 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are "
529 "incompatible with -X\"."));
534 case 'q' : /* Be quiet */
535 Output
= _CUPS_OUTPUT_QUIET
;
538 case 't' : /* CUPS test output */
539 Output
= _CUPS_OUTPUT_TEST
;
542 case 'v' : /* Be verbose */
547 _cupsLangPrintf(stderr
, _("ipptool: Unknown option \"-%c\"."),
553 else if (!strncmp(argv
[i
], "ipp://", 6) || !strncmp(argv
[i
], "http://", 7)
555 || !strncmp(argv
[i
], "ipps://", 7)
556 || !strncmp(argv
[i
], "https://", 8)
557 #endif /* HAVE_SSL */
566 _cupsLangPuts(stderr
, _("ipptool: May only specify a single URI."));
571 if (!strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8))
572 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
573 #endif /* HAVE_SSL */
576 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, vars
.uri
,
577 vars
.scheme
, sizeof(vars
.scheme
),
578 vars
.userpass
, sizeof(vars
.userpass
),
579 vars
.hostname
, sizeof(vars
.hostname
),
581 vars
.resource
, sizeof(vars
.resource
));
583 if (uri_status
!= HTTP_URI_OK
)
585 _cupsLangPrintf(stderr
, _("ipptool: Bad URI - %s."), httpURIStatusString(uri_status
));
589 if (vars
.userpass
[0])
591 if ((Password
= strchr(vars
.userpass
, ':')) != NULL
)
594 cupsSetUser(vars
.userpass
);
595 cupsSetPasswordCB(password_cb
);
596 set_variable(&vars
, "uriuser", vars
.userpass
);
599 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), vars
.scheme
, NULL
,
600 vars
.hostname
, vars
.port
, vars
.resource
);
611 _cupsLangPuts(stderr
, _("ipptool: URI required before test file."));
615 if (access(argv
[i
], 0) && argv
[i
][0] != '/'
617 && (!isalpha(argv
[i
][0] & 255) || argv
[i
][1] != ':')
621 snprintf(testname
, sizeof(testname
), "%s/ipptool/%s", cg
->cups_datadir
,
623 if (access(testname
, 0))
631 if (!do_tests(&vars
, testfile
))
636 if (!vars
.uri
|| !testfile
)
640 * Loop if the interval is set...
643 if (Output
== _CUPS_OUTPUT_PLIST
)
644 print_xml_trailer(!status
, NULL
);
645 else if (interval
> 0 && repeat
> 0)
649 usleep((useconds_t
)interval
);
650 do_tests(&vars
, testfile
);
654 else if (interval
> 0)
658 usleep((useconds_t
)interval
);
659 do_tests(&vars
, testfile
);
662 else if (Output
== _CUPS_OUTPUT_TEST
&& TestCount
> 1)
665 * Show a summary report if there were multiple tests...
668 printf("\nSummary: %d tests, %d passed, %d failed, %d skipped\n"
669 "Score: %d%%\n", TestCount
, PassCount
, FailCount
, SkipCount
,
670 100 * (PassCount
+ SkipCount
) / TestCount
);
682 * 'add_stringf()' - Add a formatted string to an array.
686 add_stringf(cups_array_t
*a
, /* I - Array */
687 const char *s
, /* I - Printf-style format string */
688 ...) /* I - Additional args as needed */
690 char buffer
[10240]; /* Format buffer */
691 va_list ap
; /* Argument pointer */
695 * Don't bother is the array is NULL...
702 * Format the message...
706 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
710 * Add it to the array...
713 cupsArrayAdd(a
, buffer
);
718 * 'compare_vars()' - Compare two variables.
721 static int /* O - Result of comparison */
722 compare_vars(_cups_var_t
*a
, /* I - First variable */
723 _cups_var_t
*b
) /* I - Second variable */
725 return (_cups_strcasecmp(a
->name
, b
->name
));
730 * 'do_tests()' - Do tests as specified in the test file.
733 static int /* 1 = success, 0 = failure */
734 do_tests(_cups_vars_t
*vars
, /* I - Variables */
735 const char *testfile
) /* I - Test file to use */
737 int i
, /* Looping var */
738 linenum
, /* Current line number */
739 pass
, /* Did we pass the test? */
740 prev_pass
= 1, /* Did we pass the previous test? */
741 request_id
, /* Current request ID */
742 show_header
= 1, /* Show the test header? */
743 ignore_errors
, /* Ignore test failures? */
744 skip_previous
= 0, /* Skip on previous test failure? */
745 repeat_count
, /* Repeat count */
746 repeat_interval
, /* Repeat interval */
747 repeat_prev
, /* Previous repeat interval */
748 repeat_test
; /* Repeat a test? */
749 http_t
*http
= NULL
; /* HTTP connection to server */
750 FILE *fp
= NULL
; /* Test file */
751 char resource
[512], /* Resource for request */
752 token
[1024], /* Token from file */
753 *tokenptr
, /* Pointer into token */
754 temp
[1024], /* Temporary string */
755 buffer
[8192], /* Copy buffer */
756 compression
[16]; /* COMPRESSION value */
757 ipp_t
*request
= NULL
, /* IPP request */
758 *response
= NULL
; /* IPP response */
759 size_t length
; /* Length of IPP request */
760 http_status_t status
; /* HTTP status */
761 cups_file_t
*reqfile
; /* File to send */
762 ssize_t bytes
; /* Bytes read/written */
763 char attr
[128]; /* Attribute name */
764 ipp_op_t op
; /* Operation */
765 ipp_tag_t group
; /* Current group */
766 ipp_tag_t value
; /* Current value type */
767 ipp_attribute_t
*attrptr
, /* Attribute pointer */
768 *found
, /* Found attribute */
769 *lastcol
= NULL
; /* Last collection attribute */
770 char name
[1024], /* Name of test */
771 file_id
[1024], /* File identifier */
772 test_id
[1024]; /* Test identifier */
773 char filename
[1024]; /* Filename */
774 _cups_transfer_t transfer
; /* To chunk or not to chunk */
775 int version
, /* IPP version number to use */
776 skip_test
; /* Skip this test? */
777 int num_statuses
= 0; /* Number of valid status codes */
778 _cups_status_t statuses
[100], /* Valid status codes */
779 *last_status
; /* Last STATUS (for predicates) */
780 int num_expects
= 0; /* Number of expected attributes */
781 _cups_expect_t expects
[200], /* Expected attributes */
782 *expect
, /* Current expected attribute */
783 *last_expect
; /* Last EXPECT (for predicates) */
784 int num_displayed
= 0; /* Number of displayed attributes */
785 char *displayed
[200]; /* Displayed attributes */
786 size_t widths
[200]; /* Width of columns */
787 cups_array_t
*a
, /* Duplicate attribute array */
788 *errors
= NULL
; /* Errors array */
789 const char *error
; /* Current error */
793 * Open the test file...
796 if ((fp
= fopen(testfile
, "r")) == NULL
)
798 print_fatal_error("Unable to open test file %s - %s", testfile
,
805 * Connect to the server...
808 if ((http
= httpConnect2(vars
->hostname
, vars
->port
, NULL
, vars
->family
,
809 vars
->encryption
, 1, 30000, NULL
)) == NULL
)
811 print_fatal_error("Unable to connect to %s on port %d - %s", vars
->hostname
,
812 vars
->port
, cupsLastErrorString());
818 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
,
819 "deflate, gzip, identity");
821 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
, "identity");
822 #endif /* HAVE_LIBZ */
824 if (vars
->timeout
> 0.0)
825 httpSetTimeout(http
, vars
->timeout
, timeout_cb
, NULL
);
831 CUPS_SRAND(time(NULL
));
833 errors
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
,
834 (cups_afree_func_t
)free
);
838 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
840 while (!Cancel
&& get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
843 * Expect an open brace...
846 if (!strcmp(token
, "DEFINE"))
852 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
853 get_token(fp
, temp
, sizeof(temp
), &linenum
))
855 expand_variables(vars
, token
, temp
, sizeof(token
));
856 set_variable(vars
, attr
, token
);
860 print_fatal_error("Missing DEFINE name and/or value on line %d.",
868 else if (!strcmp(token
, "DEFINE-DEFAULT"))
871 * DEFINE-DEFAULT name value
874 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
875 get_token(fp
, temp
, sizeof(temp
), &linenum
))
877 expand_variables(vars
, token
, temp
, sizeof(token
));
878 if (!get_variable(vars
, attr
))
879 set_variable(vars
, attr
, token
);
883 print_fatal_error("Missing DEFINE-DEFAULT name and/or value on line "
891 else if (!strcmp(token
, "FILE-ID"))
897 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
899 expand_variables(vars
, file_id
, temp
, sizeof(file_id
));
903 print_fatal_error("Missing FILE-ID value on line %d.", linenum
);
910 else if (!strcmp(token
, "IGNORE-ERRORS"))
917 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
918 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
920 IgnoreErrors
= !_cups_strcasecmp(temp
, "yes");
924 print_fatal_error("Missing IGNORE-ERRORS value on line %d.", linenum
);
931 else if (!strcmp(token
, "INCLUDE"))
938 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
941 * Map the filename to and then run the tests...
944 if (!do_tests(vars
, get_filename(testfile
, filename
, temp
,
949 if (StopAfterIncludeError
)
955 print_fatal_error("Missing INCLUDE filename on line %d.", linenum
);
963 else if (!strcmp(token
, "INCLUDE-IF-DEFINED"))
966 * INCLUDE-IF-DEFINED name "filename"
967 * INCLUDE-IF-DEFINED name <filename>
970 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
971 get_token(fp
, temp
, sizeof(temp
), &linenum
))
974 * Map the filename to and then run the tests...
977 if (get_variable(vars
, attr
) &&
978 !do_tests(vars
, get_filename(testfile
, filename
, temp
,
983 if (StopAfterIncludeError
)
989 print_fatal_error("Missing INCLUDE-IF-DEFINED name or filename on line "
998 else if (!strcmp(token
, "INCLUDE-IF-NOT-DEFINED"))
1001 * INCLUDE-IF-NOT-DEFINED name "filename"
1002 * INCLUDE-IF-NOT-DEFINED name <filename>
1005 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1006 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1009 * Map the filename to and then run the tests...
1012 if (!get_variable(vars
, attr
) &&
1013 !do_tests(vars
, get_filename(testfile
, filename
, temp
,
1018 if (StopAfterIncludeError
)
1024 print_fatal_error("Missing INCLUDE-IF-NOT-DEFINED name or filename on "
1025 "line %d.", linenum
);
1033 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1036 * SKIP-IF-DEFINED variable
1039 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1041 if (get_variable(vars
, temp
))
1046 print_fatal_error("Missing SKIP-IF-DEFINED variable on line %d.",
1052 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1055 * SKIP-IF-NOT-DEFINED variable
1058 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1060 if (!get_variable(vars
, temp
))
1065 print_fatal_error("Missing SKIP-IF-NOT-DEFINED variable on line %d.",
1071 else if (!strcmp(token
, "STOP-AFTER-INCLUDE-ERROR"))
1074 * STOP-AFTER-INCLUDE-ERROR yes
1075 * STOP-AFTER-INCLUDE-ERROR no
1078 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1079 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1081 StopAfterIncludeError
= !_cups_strcasecmp(temp
, "yes");
1085 print_fatal_error("Missing STOP-AFTER-INCLUDE-ERROR value on line %d.",
1093 else if (!strcmp(token
, "TRANSFER"))
1101 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1103 if (!strcmp(temp
, "auto"))
1104 Transfer
= _CUPS_TRANSFER_AUTO
;
1105 else if (!strcmp(temp
, "chunked"))
1106 Transfer
= _CUPS_TRANSFER_CHUNKED
;
1107 else if (!strcmp(temp
, "length"))
1108 Transfer
= _CUPS_TRANSFER_LENGTH
;
1111 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp
,
1119 print_fatal_error("Missing TRANSFER value on line %d.", linenum
);
1126 else if (!strcmp(token
, "VERSION"))
1128 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1130 if (!strcmp(temp
, "1.0"))
1132 else if (!strcmp(temp
, "1.1"))
1134 else if (!strcmp(temp
, "2.0"))
1136 else if (!strcmp(temp
, "2.1"))
1138 else if (!strcmp(temp
, "2.2"))
1142 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1149 print_fatal_error("Missing VERSION number on line %d.", linenum
);
1156 else if (strcmp(token
, "{"))
1158 print_fatal_error("Unexpected token %s seen on line %d.", token
, linenum
);
1164 * Initialize things...
1169 if (Output
== _CUPS_OUTPUT_PLIST
)
1171 else if (Output
== _CUPS_OUTPUT_TEST
)
1172 printf("\"%s\":\n", testfile
);
1177 strlcpy(resource
, vars
->resource
, sizeof(resource
));
1182 group
= IPP_TAG_ZERO
;
1183 ignore_errors
= IgnoreErrors
;
1191 transfer
= Transfer
;
1192 compression
[0] = '\0';
1194 strlcpy(name
, testfile
, sizeof(name
));
1195 if (strrchr(name
, '.') != NULL
)
1196 *strrchr(name
, '.') = '\0';
1199 * Parse until we see a close brace...
1202 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
1204 if (_cups_strcasecmp(token
, "COUNT") &&
1205 _cups_strcasecmp(token
, "DEFINE-MATCH") &&
1206 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1207 _cups_strcasecmp(token
, "DEFINE-VALUE") &&
1208 _cups_strcasecmp(token
, "IF-DEFINED") &&
1209 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1210 _cups_strcasecmp(token
, "IN-GROUP") &&
1211 _cups_strcasecmp(token
, "OF-TYPE") &&
1212 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1213 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1214 _cups_strcasecmp(token
, "REPEAT-NO-MATCH") &&
1215 _cups_strcasecmp(token
, "SAME-COUNT-AS") &&
1216 _cups_strcasecmp(token
, "WITH-ALL-VALUES") &&
1217 _cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") &&
1218 _cups_strcasecmp(token
, "WITH-ALL-RESOURCES") &&
1219 _cups_strcasecmp(token
, "WITH-ALL-SCHEMES") &&
1220 _cups_strcasecmp(token
, "WITH-HOSTNAME") &&
1221 _cups_strcasecmp(token
, "WITH-RESOURCE") &&
1222 _cups_strcasecmp(token
, "WITH-SCHEME") &&
1223 _cups_strcasecmp(token
, "WITH-VALUE"))
1226 if (_cups_strcasecmp(token
, "DEFINE-MATCH") &&
1227 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1228 _cups_strcasecmp(token
, "IF-DEFINED") &&
1229 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1230 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1231 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1232 _cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
1235 if (!strcmp(token
, "}"))
1237 else if (!strcmp(token
, "{") && lastcol
)
1240 * Another collection value
1243 ipp_t
*col
= get_collection(vars
, fp
, &linenum
);
1244 /* Collection value */
1248 ippSetCollection(request
, &lastcol
, ippGetCount(lastcol
), col
);
1256 else if (!strcmp(token
, "COMPRESSION"))
1260 * COMPRESSION deflate
1264 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1266 expand_variables(vars
, compression
, temp
, sizeof(compression
));
1268 if (strcmp(compression
, "none") && strcmp(compression
, "deflate") &&
1269 strcmp(compression
, "gzip"))
1271 if (strcmp(compression
, "none"))
1272 #endif /* HAVE_LIBZ */
1274 print_fatal_error("Unsupported COMPRESSION value '%s' on line %d.",
1275 compression
, linenum
);
1280 if (!strcmp(compression
, "none"))
1281 compression
[0] = '\0';
1285 print_fatal_error("Missing COMPRESSION value on line %d.", linenum
);
1290 else if (!strcmp(token
, "DEFINE"))
1296 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1297 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1299 expand_variables(vars
, token
, temp
, sizeof(token
));
1300 set_variable(vars
, attr
, token
);
1304 print_fatal_error("Missing DEFINE name and/or value on line %d.",
1310 else if (!strcmp(token
, "IGNORE-ERRORS"))
1317 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1318 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1320 ignore_errors
= !_cups_strcasecmp(temp
, "yes");
1324 print_fatal_error("Missing IGNORE-ERRORS value on line %d.", linenum
);
1331 else if (!_cups_strcasecmp(token
, "NAME"))
1337 get_token(fp
, name
, sizeof(name
), &linenum
);
1339 else if (!strcmp(token
, "REQUEST-ID"))
1346 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1348 if (isdigit(temp
[0] & 255))
1349 request_id
= atoi(temp
);
1350 else if (!_cups_strcasecmp(temp
, "random"))
1351 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
1354 print_fatal_error("Bad REQUEST-ID value \"%s\" on line %d.", temp
,
1362 print_fatal_error("Missing REQUEST-ID value on line %d.", linenum
);
1367 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1370 * SKIP-IF-DEFINED variable
1373 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1375 if (get_variable(vars
, temp
))
1380 print_fatal_error("Missing SKIP-IF-DEFINED value on line %d.",
1386 else if (!strcmp(token
, "SKIP-IF-MISSING"))
1389 * SKIP-IF-MISSING filename
1392 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1394 expand_variables(vars
, token
, temp
, sizeof(token
));
1395 get_filename(testfile
, filename
, token
, sizeof(filename
));
1397 if (access(filename
, R_OK
))
1402 print_fatal_error("Missing SKIP-IF-MISSING filename on line %d.",
1408 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1411 * SKIP-IF-NOT-DEFINED variable
1414 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1416 if (!get_variable(vars
, temp
))
1421 print_fatal_error("Missing SKIP-IF-NOT-DEFINED value on line %d.",
1427 else if (!strcmp(token
, "SKIP-PREVIOUS-ERROR"))
1430 * SKIP-PREVIOUS-ERROR yes
1431 * SKIP-PREVIOUS-ERROR no
1434 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1435 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1437 skip_previous
= !_cups_strcasecmp(temp
, "yes");
1441 print_fatal_error("Missing SKIP-PREVIOUS-ERROR value on line %d.", linenum
);
1448 else if (!strcmp(token
, "TEST-ID"))
1454 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1456 expand_variables(vars
, test_id
, temp
, sizeof(test_id
));
1460 print_fatal_error("Missing TEST-ID value on line %d.", linenum
);
1467 else if (!strcmp(token
, "TRANSFER"))
1475 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1477 if (!strcmp(temp
, "auto"))
1478 transfer
= _CUPS_TRANSFER_AUTO
;
1479 else if (!strcmp(temp
, "chunked"))
1480 transfer
= _CUPS_TRANSFER_CHUNKED
;
1481 else if (!strcmp(temp
, "length"))
1482 transfer
= _CUPS_TRANSFER_LENGTH
;
1485 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp
,
1493 print_fatal_error("Missing TRANSFER value on line %d.", linenum
);
1498 else if (!_cups_strcasecmp(token
, "VERSION"))
1500 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1502 if (!strcmp(temp
, "0.0"))
1504 else if (!strcmp(temp
, "1.0"))
1506 else if (!strcmp(temp
, "1.1"))
1508 else if (!strcmp(temp
, "2.0"))
1510 else if (!strcmp(temp
, "2.1"))
1512 else if (!strcmp(temp
, "2.2"))
1516 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1523 print_fatal_error("Missing VERSION number on line %d.", linenum
);
1528 else if (!_cups_strcasecmp(token
, "RESOURCE"))
1534 if (!get_token(fp
, resource
, sizeof(resource
), &linenum
))
1536 print_fatal_error("Missing RESOURCE path on line %d.", linenum
);
1541 else if (!_cups_strcasecmp(token
, "OPERATION"))
1547 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1549 print_fatal_error("Missing OPERATION code on line %d.", linenum
);
1554 expand_variables(vars
, token
, temp
, sizeof(token
));
1556 if ((op
= ippOpValue(token
)) == (ipp_op_t
)-1 &&
1557 (op
= (ipp_op_t
)strtol(token
, NULL
, 0)) == 0)
1559 print_fatal_error("Bad OPERATION code \"%s\" on line %d.", token
,
1565 else if (!_cups_strcasecmp(token
, "GROUP"))
1568 * Attribute group...
1571 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1573 print_fatal_error("Missing GROUP tag on line %d.", linenum
);
1578 if ((value
= ippTagValue(token
)) < 0)
1580 print_fatal_error("Bad GROUP tag \"%s\" on line %d.", token
, linenum
);
1586 ippAddSeparator(request
);
1590 else if (!_cups_strcasecmp(token
, "DELAY"))
1593 * Delay before operation...
1598 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1600 print_fatal_error("Missing DELAY value on line %d.", linenum
);
1605 expand_variables(vars
, token
, temp
, sizeof(token
));
1607 if ((delay
= _cupsStrScand(token
, NULL
, localeconv())) <= 0.0)
1609 print_fatal_error("Bad DELAY value \"%s\" on line %d.", token
,
1616 if (Output
== _CUPS_OUTPUT_TEST
)
1617 printf(" [%g second delay]\n", delay
);
1619 usleep((useconds_t
)(1000000.0 * delay
));
1622 else if (!_cups_strcasecmp(token
, "ATTR"))
1628 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1630 print_fatal_error("Missing ATTR value tag on line %d.", linenum
);
1635 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
1637 print_fatal_error("Bad ATTR value tag \"%s\" on line %d.", token
,
1643 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
1645 print_fatal_error("Missing ATTR name on line %d.", linenum
);
1650 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1652 print_fatal_error("Missing ATTR value on line %d.", linenum
);
1657 expand_variables(vars
, token
, temp
, sizeof(token
));
1662 case IPP_TAG_BOOLEAN
:
1663 if (!_cups_strcasecmp(token
, "true"))
1664 attrptr
= ippAddBoolean(request
, group
, attr
, 1);
1666 attrptr
= ippAddBoolean(request
, group
, attr
, (char)atoi(token
));
1669 case IPP_TAG_INTEGER
:
1671 if (!strchr(token
, ','))
1672 attrptr
= ippAddInteger(request
, group
, value
, attr
, (int)strtol(token
, &tokenptr
, 0));
1675 int values
[100], /* Values */
1676 num_values
= 1; /* Number of values */
1678 values
[0] = (int)strtol(token
, &tokenptr
, 10);
1679 while (tokenptr
&& *tokenptr
&&
1680 num_values
< (int)(sizeof(values
) / sizeof(values
[0])))
1682 if (*tokenptr
== ',')
1684 else if (!isdigit(*tokenptr
& 255) && *tokenptr
!= '-')
1687 values
[num_values
] = (int)strtol(tokenptr
, &tokenptr
, 0);
1691 attrptr
= ippAddIntegers(request
, group
, value
, attr
, num_values
, values
);
1694 if (!tokenptr
|| *tokenptr
)
1696 print_fatal_error("Bad %s value \"%s\" on line %d.",
1697 ippTagString(value
), token
, linenum
);
1703 case IPP_TAG_RESOLUTION
:
1705 int xres
, /* X resolution */
1706 yres
; /* Y resolution */
1707 char *ptr
; /* Pointer into value */
1709 xres
= yres
= (int)strtol(token
, (char **)&ptr
, 10);
1710 if (ptr
> token
&& xres
> 0)
1713 yres
= (int)strtol(ptr
+ 1, (char **)&ptr
, 10);
1716 if (ptr
<= token
|| xres
<= 0 || yres
<= 0 || !ptr
||
1717 (_cups_strcasecmp(ptr
, "dpi") &&
1718 _cups_strcasecmp(ptr
, "dpc") &&
1719 _cups_strcasecmp(ptr
, "dpcm") &&
1720 _cups_strcasecmp(ptr
, "other")))
1722 print_fatal_error("Bad resolution value \"%s\" on line %d.",
1728 if (!_cups_strcasecmp(ptr
, "dpi"))
1729 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
1730 else if (!_cups_strcasecmp(ptr
, "dpc") ||
1731 !_cups_strcasecmp(ptr
, "dpcm"))
1732 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_CM
, xres
, yres
);
1734 attrptr
= ippAddResolution(request
, group
, attr
, (ipp_res_t
)0, xres
, yres
);
1738 case IPP_TAG_RANGE
:
1740 int lowers
[4], /* Lower value */
1741 uppers
[4], /* Upper values */
1742 num_vals
; /* Number of values */
1745 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
1746 lowers
+ 0, uppers
+ 0,
1747 lowers
+ 1, uppers
+ 1,
1748 lowers
+ 2, uppers
+ 2,
1749 lowers
+ 3, uppers
+ 3);
1751 if ((num_vals
& 1) || num_vals
== 0)
1753 print_fatal_error("Bad rangeOfInteger value \"%s\" on line "
1754 "%d.", token
, linenum
);
1759 attrptr
= ippAddRanges(request
, group
, attr
, num_vals
/ 2, lowers
,
1764 case IPP_TAG_BEGIN_COLLECTION
:
1765 if (!strcmp(token
, "{"))
1767 ipp_t
*col
= get_collection(vars
, fp
, &linenum
);
1768 /* Collection value */
1772 attrptr
= lastcol
= ippAddCollection(request
, group
, attr
, col
);
1783 print_fatal_error("Bad ATTR collection value on line %d.",
1790 case IPP_TAG_STRING
:
1791 attrptr
= ippAddOctetString(request
, group
, attr
, token
, (int)strlen(token
));
1795 print_fatal_error("Unsupported ATTR value tag %s on line %d.",
1796 ippTagString(value
), linenum
);
1800 case IPP_TAG_TEXTLANG
:
1801 case IPP_TAG_NAMELANG
:
1804 case IPP_TAG_KEYWORD
:
1806 case IPP_TAG_URISCHEME
:
1807 case IPP_TAG_CHARSET
:
1808 case IPP_TAG_LANGUAGE
:
1809 case IPP_TAG_MIMETYPE
:
1810 if (!strchr(token
, ','))
1811 attrptr
= ippAddString(request
, group
, value
, attr
, NULL
, token
);
1815 * Multiple string values...
1818 int num_values
; /* Number of values */
1819 char *values
[100], /* Values */
1820 *ptr
; /* Pointer to next value */
1826 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
1828 if (ptr
> token
&& ptr
[-1] == '\\')
1829 _cups_strcpy(ptr
- 1, ptr
);
1833 values
[num_values
] = ptr
;
1838 attrptr
= ippAddStrings(request
, group
, value
, attr
, num_values
,
1839 NULL
, (const char **)values
);
1846 print_fatal_error("Unable to add attribute on line %d: %s", linenum
,
1847 cupsLastErrorString());
1852 else if (!_cups_strcasecmp(token
, "FILE"))
1858 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1860 print_fatal_error("Missing FILE filename on line %d.", linenum
);
1865 expand_variables(vars
, token
, temp
, sizeof(token
));
1866 get_filename(testfile
, filename
, token
, sizeof(filename
));
1868 if (access(filename
, R_OK
))
1870 print_fatal_error("Filename \"%s\" on line %d cannot be read.",
1872 print_fatal_error("Filename mapped to \"%s\".", filename
);
1877 else if (!_cups_strcasecmp(token
, "STATUS"))
1883 if (num_statuses
>= (int)(sizeof(statuses
) / sizeof(statuses
[0])))
1885 print_fatal_error("Too many STATUS's on line %d.", linenum
);
1890 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1892 print_fatal_error("Missing STATUS code on line %d.", linenum
);
1897 if ((statuses
[num_statuses
].status
= ippErrorValue(token
))
1898 == (ipp_status_t
)-1 &&
1899 (statuses
[num_statuses
].status
= (ipp_status_t
)strtol(token
, NULL
, 0)) == 0)
1901 print_fatal_error("Bad STATUS code \"%s\" on line %d.", token
,
1907 last_status
= statuses
+ num_statuses
;
1910 last_status
->define_match
= NULL
;
1911 last_status
->define_no_match
= NULL
;
1912 last_status
->if_defined
= NULL
;
1913 last_status
->if_not_defined
= NULL
;
1914 last_status
->repeat_limit
= 1000;
1915 last_status
->repeat_match
= 0;
1916 last_status
->repeat_no_match
= 0;
1918 else if (!_cups_strcasecmp(token
, "EXPECT"))
1921 * Expected attributes...
1924 if (num_expects
>= (int)(sizeof(expects
) / sizeof(expects
[0])))
1926 print_fatal_error("Too many EXPECT's on line %d.", linenum
);
1931 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1933 print_fatal_error("Missing EXPECT name on line %d.", linenum
);
1938 last_expect
= expects
+ num_expects
;
1941 memset(last_expect
, 0, sizeof(_cups_expect_t
));
1942 last_expect
->repeat_limit
= 1000;
1944 if (token
[0] == '!')
1946 last_expect
->not_expect
= 1;
1947 last_expect
->name
= strdup(token
+ 1);
1949 else if (token
[0] == '?')
1951 last_expect
->optional
= 1;
1952 last_expect
->name
= strdup(token
+ 1);
1955 last_expect
->name
= strdup(token
);
1957 else if (!_cups_strcasecmp(token
, "COUNT"))
1959 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1961 print_fatal_error("Missing COUNT number on line %d.", linenum
);
1966 if ((i
= atoi(token
)) <= 0)
1968 print_fatal_error("Bad COUNT \"%s\" on line %d.", token
, linenum
);
1974 last_expect
->count
= i
;
1977 print_fatal_error("COUNT without a preceding EXPECT on line %d.",
1983 else if (!_cups_strcasecmp(token
, "DEFINE-MATCH"))
1985 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1987 print_fatal_error("Missing DEFINE-MATCH variable on line %d.",
1994 last_expect
->define_match
= strdup(token
);
1995 else if (last_status
)
1996 last_status
->define_match
= strdup(token
);
1999 print_fatal_error("DEFINE-MATCH without a preceding EXPECT or STATUS "
2000 "on line %d.", linenum
);
2005 else if (!_cups_strcasecmp(token
, "DEFINE-NO-MATCH"))
2007 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2009 print_fatal_error("Missing DEFINE-NO-MATCH variable on line %d.",
2016 last_expect
->define_no_match
= strdup(token
);
2017 else if (last_status
)
2018 last_status
->define_no_match
= strdup(token
);
2021 print_fatal_error("DEFINE-NO-MATCH without a preceding EXPECT or "
2022 "STATUS on line %d.", linenum
);
2027 else if (!_cups_strcasecmp(token
, "DEFINE-VALUE"))
2029 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2031 print_fatal_error("Missing DEFINE-VALUE variable on line %d.",
2038 last_expect
->define_value
= strdup(token
);
2041 print_fatal_error("DEFINE-VALUE without a preceding EXPECT on "
2042 "line %d.", linenum
);
2047 else if (!_cups_strcasecmp(token
, "OF-TYPE"))
2049 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2051 print_fatal_error("Missing OF-TYPE value tag(s) on line %d.",
2058 last_expect
->of_type
= strdup(token
);
2061 print_fatal_error("OF-TYPE without a preceding EXPECT on line %d.",
2067 else if (!_cups_strcasecmp(token
, "IN-GROUP"))
2069 ipp_tag_t in_group
; /* IN-GROUP value */
2072 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2074 print_fatal_error("Missing IN-GROUP group tag on line %d.", linenum
);
2079 if ((in_group
= ippTagValue(token
)) == (ipp_tag_t
)-1)
2082 else if (last_expect
)
2083 last_expect
->in_group
= in_group
;
2086 print_fatal_error("IN-GROUP without a preceding EXPECT on line %d.",
2092 else if (!_cups_strcasecmp(token
, "REPEAT-LIMIT"))
2094 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2096 print_fatal_error("Missing REPEAT-LIMIT value on line %d.", linenum
);
2100 else if (atoi(token
) <= 0)
2102 print_fatal_error("Bad REPEAT-LIMIT value on line %d.", linenum
);
2108 last_status
->repeat_limit
= atoi(token
);
2109 else if (last_expect
)
2110 last_expect
->repeat_limit
= atoi(token
);
2113 print_fatal_error("REPEAT-LIMIT without a preceding EXPECT or STATUS "
2114 "on line %d.", linenum
);
2119 else if (!_cups_strcasecmp(token
, "REPEAT-MATCH"))
2122 last_status
->repeat_match
= 1;
2123 else if (last_expect
)
2124 last_expect
->repeat_match
= 1;
2127 print_fatal_error("REPEAT-MATCH without a preceding EXPECT or STATUS "
2128 "on line %d.", linenum
);
2133 else if (!_cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
2136 last_status
->repeat_no_match
= 1;
2137 else if (last_expect
)
2138 last_expect
->repeat_no_match
= 1;
2141 print_fatal_error("REPEAT-NO-MATCH without a preceding EXPECT or "
2142 "STATUS on ine %d.", linenum
);
2147 else if (!_cups_strcasecmp(token
, "SAME-COUNT-AS"))
2149 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2151 print_fatal_error("Missing SAME-COUNT-AS name on line %d.", linenum
);
2157 last_expect
->same_count_as
= strdup(token
);
2160 print_fatal_error("SAME-COUNT-AS without a preceding EXPECT on line "
2166 else if (!_cups_strcasecmp(token
, "IF-DEFINED"))
2168 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2170 print_fatal_error("Missing IF-DEFINED name on line %d.", linenum
);
2176 last_expect
->if_defined
= strdup(token
);
2177 else if (last_status
)
2178 last_status
->if_defined
= strdup(token
);
2181 print_fatal_error("IF-DEFINED without a preceding EXPECT or STATUS "
2182 "on line %d.", linenum
);
2187 else if (!_cups_strcasecmp(token
, "IF-NOT-DEFINED"))
2189 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2191 print_fatal_error("Missing IF-NOT-DEFINED name on line %d.", linenum
);
2197 last_expect
->if_not_defined
= strdup(token
);
2198 else if (last_status
)
2199 last_status
->if_not_defined
= strdup(token
);
2202 print_fatal_error("IF-NOT-DEFINED without a preceding EXPECT or STATUS "
2203 "on line %d.", linenum
);
2208 else if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") ||
2209 !_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
2210 !_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
2211 !_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
2212 !_cups_strcasecmp(token
, "WITH-HOSTNAME") ||
2213 !_cups_strcasecmp(token
, "WITH-RESOURCE") ||
2214 !_cups_strcasecmp(token
, "WITH-SCHEME") ||
2215 !_cups_strcasecmp(token
, "WITH-VALUE"))
2219 if (!_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
2220 !_cups_strcasecmp(token
, "WITH-HOSTNAME"))
2221 last_expect
->with_flags
= _CUPS_WITH_HOSTNAME
;
2222 else if (!_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
2223 !_cups_strcasecmp(token
, "WITH-RESOURCE"))
2224 last_expect
->with_flags
= _CUPS_WITH_RESOURCE
;
2225 else if (!_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
2226 !_cups_strcasecmp(token
, "WITH-SCHEME"))
2227 last_expect
->with_flags
= _CUPS_WITH_SCHEME
;
2229 if (!_cups_strncasecmp(token
, "WITH-ALL-", 9))
2230 last_expect
->with_flags
|= _CUPS_WITH_ALL
;
2233 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
2235 print_fatal_error("Missing %s value on line %d.", token
, linenum
);
2243 * Expand any variables in the value and then save it.
2246 expand_variables(vars
, token
, temp
, sizeof(token
));
2248 tokenptr
= token
+ strlen(token
) - 1;
2250 if (token
[0] == '/' && tokenptr
> token
&& *tokenptr
== '/')
2253 * WITH-VALUE is a POSIX extended regular expression.
2256 last_expect
->with_value
= calloc(1, (size_t)(tokenptr
- token
));
2257 last_expect
->with_flags
|= _CUPS_WITH_REGEX
;
2259 if (last_expect
->with_value
)
2260 memcpy(last_expect
->with_value
, token
+ 1, (size_t)(tokenptr
- token
- 1));
2265 * WITH-VALUE is a literal value...
2268 char *ptr
; /* Pointer into value */
2270 for (ptr
= token
; *ptr
; ptr
++)
2272 if (*ptr
== '\\' && ptr
[1])
2275 * Remove \ from \foo...
2278 _cups_strcpy(ptr
, ptr
+ 1);
2282 last_expect
->with_value
= strdup(token
);
2283 last_expect
->with_flags
|= _CUPS_WITH_LITERAL
;
2288 print_fatal_error("%s without a preceding EXPECT on line %d.", token
,
2294 else if (!_cups_strcasecmp(token
, "DISPLAY"))
2297 * Display attributes...
2300 if (num_displayed
>= (int)(sizeof(displayed
) / sizeof(displayed
[0])))
2302 print_fatal_error("Too many DISPLAY's on line %d", linenum
);
2307 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2309 print_fatal_error("Missing DISPLAY name on line %d.", linenum
);
2314 displayed
[num_displayed
] = strdup(token
);
2319 print_fatal_error("Unexpected token %s seen on line %d.", token
,
2327 * Submit the IPP request...
2332 ippSetVersion(request
, version
/ 10, version
% 10);
2333 ippSetOperation(request
, op
);
2334 ippSetRequestId(request
, request_id
);
2336 if (Output
== _CUPS_OUTPUT_PLIST
)
2339 puts("<key>Name</key>");
2340 print_xml_string("string", name
);
2343 puts("<key>FileId</key>");
2344 print_xml_string("string", file_id
);
2348 puts("<key>TestId</key>");
2349 print_xml_string("string", test_id
);
2351 puts("<key>Version</key>");
2352 printf("<string>%d.%d</string>\n", version
/ 10, version
% 10);
2353 puts("<key>Operation</key>");
2354 print_xml_string("string", ippOpString(op
));
2355 puts("<key>RequestId</key>");
2356 printf("<integer>%d</integer>\n", request_id
);
2357 puts("<key>RequestAttributes</key>");
2362 for (attrptr
= request
->attrs
,
2363 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2365 attrptr
= attrptr
->next
)
2366 print_attr(attrptr
, &group
);
2371 else if (Output
== _CUPS_OUTPUT_TEST
)
2375 printf(" %s:\n", ippOpString(op
));
2377 for (attrptr
= request
->attrs
; attrptr
; attrptr
= attrptr
->next
)
2378 print_attr(attrptr
, NULL
);
2381 printf(" %-68.68s [", name
);
2385 if ((skip_previous
&& !prev_pass
) || skip_test
)
2392 if (Output
== _CUPS_OUTPUT_PLIST
)
2394 puts("<key>Successful</key>");
2396 puts("<key>StatusCode</key>");
2397 print_xml_string("string", "skip");
2398 puts("<key>ResponseAttributes</key>");
2401 else if (Output
== _CUPS_OUTPUT_TEST
)
2408 repeat_interval
= 1;
2417 if (transfer
== _CUPS_TRANSFER_CHUNKED
||
2418 (transfer
== _CUPS_TRANSFER_AUTO
&& filename
[0]))
2421 * Send request using chunking - a 0 length means "chunk".
2429 * Send request using content length...
2432 length
= ippLength(request
);
2434 if (filename
[0] && (reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2437 * Read the file to get the uncompressed file size...
2440 while ((bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
2441 length
+= (size_t)bytes
;
2443 cupsFileClose(reqfile
);
2448 * Send the request...
2455 if (status
!= HTTP_ERROR
)
2457 while (!response
&& !Cancel
&& prev_pass
)
2459 status
= cupsSendRequest(http
, request
, resource
, length
);
2463 httpSetField(http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
2464 #endif /* HAVE_LIBZ */
2466 if (!Cancel
&& status
== HTTP_CONTINUE
&&
2467 request
->state
== IPP_DATA
&& filename
[0])
2469 if ((reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2472 (bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
2473 if ((status
= cupsWriteRequestData(http
, buffer
, (size_t)bytes
)) != HTTP_CONTINUE
)
2476 cupsFileClose(reqfile
);
2480 snprintf(buffer
, sizeof(buffer
), "%s: %s", filename
,
2482 _cupsSetError(IPP_INTERNAL_ERROR
, buffer
, 0);
2484 status
= HTTP_ERROR
;
2489 * Get the server's response...
2492 if (!Cancel
&& status
!= HTTP_ERROR
)
2494 response
= cupsGetResponse(http
, resource
);
2495 status
= httpGetStatus(http
);
2498 if (!Cancel
&& status
== HTTP_ERROR
&& http
->error
!= EINVAL
&&
2500 http
->error
!= WSAETIMEDOUT
)
2502 http
->error
!= ETIMEDOUT
)
2505 if (httpReconnect(http
))
2508 else if (status
== HTTP_ERROR
)
2513 else if (status
!= HTTP_OK
)
2517 if (status
== HTTP_STATUS_UNAUTHORIZED
)
2525 if (!Cancel
&& status
== HTTP_ERROR
&& http
->error
!= EINVAL
&&
2527 http
->error
!= WSAETIMEDOUT
)
2529 http
->error
!= ETIMEDOUT
)
2532 if (httpReconnect(http
))
2535 else if (status
== HTTP_ERROR
)
2538 httpReconnect(http
);
2542 else if (status
!= HTTP_OK
)
2549 * Check results of request...
2552 cupsArrayClear(errors
);
2554 if (http
->version
!= HTTP_1_1
)
2555 add_stringf(errors
, "Bad HTTP version (%d.%d)", http
->version
/ 100,
2556 http
->version
% 100);
2561 * No response, log error...
2564 add_stringf(errors
, "IPP request failed with status %s (%s)",
2565 ippErrorString(cupsLastError()),
2566 cupsLastErrorString());
2571 * Collect common attribute values...
2574 if ((attrptr
= ippFindAttribute(response
, "job-id",
2575 IPP_TAG_INTEGER
)) != NULL
)
2577 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2578 set_variable(vars
, "job-id", temp
);
2581 if ((attrptr
= ippFindAttribute(response
, "job-uri",
2582 IPP_TAG_URI
)) != NULL
)
2583 set_variable(vars
, "job-uri", attrptr
->values
[0].string
.text
);
2585 if ((attrptr
= ippFindAttribute(response
, "notify-subscription-id",
2586 IPP_TAG_INTEGER
)) != NULL
)
2588 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2589 set_variable(vars
, "notify-subscription-id", temp
);
2593 * Check response, validating groups and attributes and logging errors
2597 if (response
->state
!= IPP_DATA
)
2599 "Missing end-of-attributes-tag in response "
2600 "(RFC 2910 section 3.5.1)");
2603 (response
->request
.status
.version
[0] != (version
/ 10) ||
2604 response
->request
.status
.version
[1] != (version
% 10)))
2606 "Bad version %d.%d in response - expected %d.%d "
2607 "(RFC 2911 section 3.1.8).",
2608 response
->request
.status
.version
[0],
2609 response
->request
.status
.version
[1],
2610 version
/ 10, version
% 10);
2612 if (response
->request
.status
.request_id
!= request_id
)
2614 "Bad request ID %d in response - expected %d "
2615 "(RFC 2911 section 3.1.1)",
2616 response
->request
.status
.request_id
, request_id
);
2618 attrptr
= response
->attrs
;
2621 "Missing first attribute \"attributes-charset "
2622 "(charset)\" in group operation-attributes-tag "
2623 "(RFC 2911 section 3.1.4).");
2626 if (!attrptr
->name
||
2627 attrptr
->value_tag
!= IPP_TAG_CHARSET
||
2628 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2629 attrptr
->num_values
!= 1 ||
2630 strcmp(attrptr
->name
, "attributes-charset"))
2632 "Bad first attribute \"%s (%s%s)\" in group %s, "
2633 "expected \"attributes-charset (charset)\" in "
2634 "group operation-attributes-tag (RFC 2911 section "
2636 attrptr
->name
? attrptr
->name
: "(null)",
2637 attrptr
->num_values
> 1 ? "1setOf " : "",
2638 ippTagString(attrptr
->value_tag
),
2639 ippTagString(attrptr
->group_tag
));
2641 attrptr
= attrptr
->next
;
2644 "Missing second attribute \"attributes-natural-"
2645 "language (naturalLanguage)\" in group "
2646 "operation-attributes-tag (RFC 2911 section "
2648 else if (!attrptr
->name
||
2649 attrptr
->value_tag
!= IPP_TAG_LANGUAGE
||
2650 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2651 attrptr
->num_values
!= 1 ||
2652 strcmp(attrptr
->name
, "attributes-natural-language"))
2654 "Bad first attribute \"%s (%s%s)\" in group %s, "
2655 "expected \"attributes-natural-language "
2656 "(naturalLanguage)\" in group "
2657 "operation-attributes-tag (RFC 2911 section "
2659 attrptr
->name
? attrptr
->name
: "(null)",
2660 attrptr
->num_values
> 1 ? "1setOf " : "",
2661 ippTagString(attrptr
->value_tag
),
2662 ippTagString(attrptr
->group_tag
));
2665 if ((attrptr
= ippFindAttribute(response
, "status-message",
2666 IPP_TAG_ZERO
)) != NULL
)
2668 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2670 "status-message (text(255)) has wrong value tag "
2671 "%s (RFC 2911 section 3.1.6.2).",
2672 ippTagString(attrptr
->value_tag
));
2673 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2675 "status-message (text(255)) has wrong group tag "
2676 "%s (RFC 2911 section 3.1.6.2).",
2677 ippTagString(attrptr
->group_tag
));
2678 if (attrptr
->num_values
!= 1)
2680 "status-message (text(255)) has %d values "
2681 "(RFC 2911 section 3.1.6.2).",
2682 attrptr
->num_values
);
2683 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2684 strlen(attrptr
->values
[0].string
.text
) > 255)
2686 "status-message (text(255)) has bad length %d"
2687 " (RFC 2911 section 3.1.6.2).",
2688 (int)strlen(attrptr
->values
[0].string
.text
));
2691 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
2692 IPP_TAG_ZERO
)) != NULL
)
2694 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2696 "detailed-status-message (text(MAX)) has wrong "
2697 "value tag %s (RFC 2911 section 3.1.6.3).",
2698 ippTagString(attrptr
->value_tag
));
2699 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2701 "detailed-status-message (text(MAX)) has wrong "
2702 "group tag %s (RFC 2911 section 3.1.6.3).",
2703 ippTagString(attrptr
->group_tag
));
2704 if (attrptr
->num_values
!= 1)
2706 "detailed-status-message (text(MAX)) has %d values"
2707 " (RFC 2911 section 3.1.6.3).",
2708 attrptr
->num_values
);
2709 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2710 strlen(attrptr
->values
[0].string
.text
) > 1023)
2712 "detailed-status-message (text(MAX)) has bad "
2713 "length %d (RFC 2911 section 3.1.6.3).",
2714 (int)strlen(attrptr
->values
[0].string
.text
));
2717 a
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2719 for (attrptr
= response
->attrs
,
2720 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2722 attrptr
= attrptr
->next
)
2724 if (attrptr
->group_tag
!= group
)
2726 int out_of_order
= 0; /* Are attribute groups out-of-order? */
2729 switch (attrptr
->group_tag
)
2734 case IPP_TAG_OPERATION
:
2738 case IPP_TAG_UNSUPPORTED_GROUP
:
2739 if (group
!= IPP_TAG_OPERATION
)
2744 case IPP_TAG_PRINTER
:
2745 if (group
!= IPP_TAG_OPERATION
&&
2746 group
!= IPP_TAG_UNSUPPORTED_GROUP
)
2750 case IPP_TAG_SUBSCRIPTION
:
2751 if (group
> attrptr
->group_tag
&&
2752 group
!= IPP_TAG_DOCUMENT
)
2757 if (group
> attrptr
->group_tag
)
2763 add_stringf(errors
, "Attribute groups out of order (%s < %s)",
2764 ippTagString(attrptr
->group_tag
),
2765 ippTagString(group
));
2767 if (attrptr
->group_tag
!= IPP_TAG_ZERO
)
2768 group
= attrptr
->group_tag
;
2771 validate_attr(errors
, attrptr
);
2775 if (cupsArrayFind(a
, attrptr
->name
))
2776 add_stringf(errors
, "Duplicate \"%s\" attribute in %s group",
2777 attrptr
->name
, ippTagString(group
));
2779 cupsArrayAdd(a
, attrptr
->name
);
2786 * Now check the test-defined expected status-code and attribute
2790 for (i
= 0; i
< num_statuses
; i
++)
2792 if (statuses
[i
].if_defined
&&
2793 !get_variable(vars
, statuses
[i
].if_defined
))
2796 if (statuses
[i
].if_not_defined
&&
2797 get_variable(vars
, statuses
[i
].if_not_defined
))
2800 if (response
->request
.status
.status_code
== statuses
[i
].status
)
2802 if (statuses
[i
].repeat_match
&&
2803 repeat_count
< statuses
[i
].repeat_limit
)
2806 if (statuses
[i
].define_match
)
2807 set_variable(vars
, statuses
[i
].define_match
, "1");
2813 if (statuses
[i
].repeat_no_match
&&
2814 repeat_count
< statuses
[i
].repeat_limit
)
2817 if (statuses
[i
].define_no_match
)
2819 set_variable(vars
, statuses
[i
].define_no_match
, "1");
2825 if (i
== num_statuses
&& num_statuses
> 0)
2827 for (i
= 0; i
< num_statuses
; i
++)
2829 if (statuses
[i
].if_defined
&&
2830 !get_variable(vars
, statuses
[i
].if_defined
))
2833 if (statuses
[i
].if_not_defined
&&
2834 get_variable(vars
, statuses
[i
].if_not_defined
))
2837 if (!statuses
[i
].repeat_match
)
2838 add_stringf(errors
, "EXPECTED: STATUS %s (got %s)",
2839 ippErrorString(statuses
[i
].status
),
2840 ippErrorString(cupsLastError()));
2843 if ((attrptr
= ippFindAttribute(response
, "status-message",
2844 IPP_TAG_TEXT
)) != NULL
)
2845 add_stringf(errors
, "status-message=\"%s\"",
2846 attrptr
->values
[0].string
.text
);
2849 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
2851 if (expect
->if_defined
&& !get_variable(vars
, expect
->if_defined
))
2854 if (expect
->if_not_defined
&&
2855 get_variable(vars
, expect
->if_not_defined
))
2858 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
2860 if ((found
&& expect
->not_expect
) ||
2861 (!found
&& !(expect
->not_expect
|| expect
->optional
)) ||
2862 (found
&& !expect_matches(expect
, found
->value_tag
)) ||
2863 (found
&& expect
->in_group
&&
2864 found
->group_tag
!= expect
->in_group
))
2866 if (expect
->define_no_match
)
2867 set_variable(vars
, expect
->define_no_match
, "1");
2868 else if (!expect
->define_match
&& !expect
->define_value
)
2870 if (found
&& expect
->not_expect
)
2871 add_stringf(errors
, "NOT EXPECTED: %s", expect
->name
);
2872 else if (!found
&& !(expect
->not_expect
|| expect
->optional
))
2873 add_stringf(errors
, "EXPECTED: %s", expect
->name
);
2876 if (!expect_matches(expect
, found
->value_tag
))
2877 add_stringf(errors
, "EXPECTED: %s OF-TYPE %s (got %s)",
2878 expect
->name
, expect
->of_type
,
2879 ippTagString(found
->value_tag
));
2881 if (expect
->in_group
&& found
->group_tag
!= expect
->in_group
)
2882 add_stringf(errors
, "EXPECTED: %s IN-GROUP %s (got %s).",
2883 expect
->name
, ippTagString(expect
->in_group
),
2884 ippTagString(found
->group_tag
));
2888 if (expect
->repeat_no_match
&&
2889 repeat_count
< expect
->repeat_limit
)
2896 ippAttributeString(found
, buffer
, sizeof(buffer
));
2899 !with_value(NULL
, expect
->with_value
, expect
->with_flags
, found
,
2900 buffer
, sizeof(buffer
)))
2902 if (expect
->define_no_match
)
2903 set_variable(vars
, expect
->define_no_match
, "1");
2904 else if (!expect
->define_match
&& !expect
->define_value
&&
2905 !expect
->repeat_match
&& !expect
->repeat_no_match
)
2907 if (expect
->with_flags
& _CUPS_WITH_REGEX
)
2908 add_stringf(errors
, "EXPECTED: %s %s /%s/",
2910 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
2911 "WITH-ALL-VALUES" : "WITH-VALUE",
2912 expect
->with_value
);
2914 add_stringf(errors
, "EXPECTED: %s %s \"%s\"",
2916 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
2917 "WITH-ALL-VALUES" : "WITH-VALUE",
2918 expect
->with_value
);
2920 with_value(errors
, expect
->with_value
, expect
->with_flags
, found
,
2921 buffer
, sizeof(buffer
));
2924 if (expect
->repeat_no_match
&&
2925 repeat_count
< expect
->repeat_limit
)
2931 if (found
&& expect
->count
> 0 &&
2932 found
->num_values
!= expect
->count
)
2934 if (expect
->define_no_match
)
2935 set_variable(vars
, expect
->define_no_match
, "1");
2936 else if (!expect
->define_match
&& !expect
->define_value
)
2938 add_stringf(errors
, "EXPECTED: %s COUNT %d (got %d)", expect
->name
,
2939 expect
->count
, found
->num_values
);
2942 if (expect
->repeat_no_match
&&
2943 repeat_count
< expect
->repeat_limit
)
2949 if (found
&& expect
->same_count_as
)
2951 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
2954 if (!attrptr
|| attrptr
->num_values
!= found
->num_values
)
2956 if (expect
->define_no_match
)
2957 set_variable(vars
, expect
->define_no_match
, "1");
2958 else if (!expect
->define_match
&& !expect
->define_value
)
2962 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2963 "(not returned)", expect
->name
,
2964 found
->num_values
, expect
->same_count_as
);
2965 else if (attrptr
->num_values
!= found
->num_values
)
2967 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2968 "(%d values)", expect
->name
, found
->num_values
,
2969 expect
->same_count_as
, attrptr
->num_values
);
2972 if (expect
->repeat_no_match
&&
2973 repeat_count
< expect
->repeat_limit
)
2980 if (found
&& expect
->define_match
)
2981 set_variable(vars
, expect
->define_match
, "1");
2983 if (found
&& expect
->define_value
)
2984 set_variable(vars
, expect
->define_value
, buffer
);
2986 if (found
&& expect
->repeat_match
&&
2987 repeat_count
< expect
->repeat_limit
)
2993 * If we are going to repeat this test, sleep 1 second so we don't flood
2994 * the printer with requests...
2999 if (Output
== _CUPS_OUTPUT_TEST
)
3001 printf("%04d]\n", repeat_count
);
3005 sleep((unsigned)repeat_interval
);
3006 repeat_interval
= _cupsNextDelay(repeat_interval
, &repeat_prev
);
3008 if (Output
== _CUPS_OUTPUT_TEST
)
3010 printf(" %-68.68s [", name
);
3015 while (repeat_test
);
3021 if (cupsArrayCount(errors
) > 0)
3022 prev_pass
= pass
= 0;
3029 if (Output
== _CUPS_OUTPUT_PLIST
)
3031 puts("<key>Successful</key>");
3032 puts(prev_pass
? "<true />" : "<false />");
3033 puts("<key>StatusCode</key>");
3034 print_xml_string("string", ippErrorString(cupsLastError()));
3035 puts("<key>ResponseAttributes</key>");
3038 for (attrptr
= response
? response
->attrs
: NULL
,
3039 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
3041 attrptr
= attrptr
->next
)
3042 print_attr(attrptr
, &group
);
3046 else if (Output
== _CUPS_OUTPUT_TEST
)
3048 puts(prev_pass
? "PASS]" : "FAIL]");
3050 if (!prev_pass
|| (Verbosity
&& response
))
3052 printf(" RECEIVED: %lu bytes in response\n",
3053 (unsigned long)ippLength(response
));
3054 printf(" status-code = %s (%s)\n", ippErrorString(cupsLastError()),
3055 cupsLastErrorString());
3059 for (attrptr
= response
->attrs
;
3061 attrptr
= attrptr
->next
)
3062 print_attr(attrptr
, NULL
);
3066 else if (!prev_pass
)
3067 fprintf(stderr
, "%s\n", cupsLastErrorString());
3069 if (prev_pass
&& Output
>= _CUPS_OUTPUT_LIST
&& !Verbosity
&&
3072 size_t width
; /* Length of value */
3074 for (i
= 0; i
< num_displayed
; i
++)
3076 widths
[i
] = strlen(displayed
[i
]);
3078 for (attrptr
= ippFindAttribute(response
, displayed
[i
], IPP_TAG_ZERO
);
3080 attrptr
= ippFindNextAttribute(response
, displayed
[i
],
3083 width
= ippAttributeString(attrptr
, NULL
, 0);
3084 if (width
> widths
[i
])
3089 if (Output
== _CUPS_OUTPUT_CSV
)
3090 print_csv(NULL
, num_displayed
, displayed
, widths
);
3092 print_line(NULL
, num_displayed
, displayed
, widths
);
3094 attrptr
= response
->attrs
;
3098 while (attrptr
&& attrptr
->group_tag
<= IPP_TAG_OPERATION
)
3099 attrptr
= attrptr
->next
;
3103 if (Output
== _CUPS_OUTPUT_CSV
)
3104 print_csv(attrptr
, num_displayed
, displayed
, widths
);
3106 print_line(attrptr
, num_displayed
, displayed
, widths
);
3108 while (attrptr
&& attrptr
->group_tag
> IPP_TAG_OPERATION
)
3109 attrptr
= attrptr
->next
;
3113 else if (!prev_pass
)
3115 if (Output
== _CUPS_OUTPUT_PLIST
)
3117 puts("<key>Errors</key>");
3120 for (error
= (char *)cupsArrayFirst(errors
);
3122 error
= (char *)cupsArrayNext(errors
))
3123 print_xml_string("string", error
);
3129 for (error
= (char *)cupsArrayFirst(errors
);
3131 error
= (char *)cupsArrayNext(errors
))
3132 printf(" %s\n", error
);
3136 if (num_displayed
> 0 && !Verbosity
&& response
&&
3137 Output
== _CUPS_OUTPUT_TEST
)
3139 for (attrptr
= response
->attrs
;
3141 attrptr
= attrptr
->next
)
3145 for (i
= 0; i
< num_displayed
; i
++)
3147 if (!strcmp(displayed
[i
], attrptr
->name
))
3149 print_attr(attrptr
, NULL
);
3159 if (Output
== _CUPS_OUTPUT_PLIST
)
3164 ippDelete(response
);
3167 for (i
= 0; i
< num_statuses
; i
++)
3169 if (statuses
[i
].if_defined
)
3170 free(statuses
[i
].if_defined
);
3171 if (statuses
[i
].if_not_defined
)
3172 free(statuses
[i
].if_not_defined
);
3173 if (statuses
[i
].define_match
)
3174 free(statuses
[i
].define_match
);
3175 if (statuses
[i
].define_no_match
)
3176 free(statuses
[i
].define_no_match
);
3180 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3183 if (expect
->of_type
)
3184 free(expect
->of_type
);
3185 if (expect
->same_count_as
)
3186 free(expect
->same_count_as
);
3187 if (expect
->if_defined
)
3188 free(expect
->if_defined
);
3189 if (expect
->if_not_defined
)
3190 free(expect
->if_not_defined
);
3191 if (expect
->with_value
)
3192 free(expect
->with_value
);
3193 if (expect
->define_match
)
3194 free(expect
->define_match
);
3195 if (expect
->define_no_match
)
3196 free(expect
->define_no_match
);
3197 if (expect
->define_value
)
3198 free(expect
->define_value
);
3202 for (i
= 0; i
< num_displayed
; i
++)
3206 if (!ignore_errors
&& !prev_pass
)
3212 cupsArrayDelete(errors
);
3219 ippDelete(response
);
3221 for (i
= 0; i
< num_statuses
; i
++)
3223 if (statuses
[i
].if_defined
)
3224 free(statuses
[i
].if_defined
);
3225 if (statuses
[i
].if_not_defined
)
3226 free(statuses
[i
].if_not_defined
);
3227 if (statuses
[i
].define_match
)
3228 free(statuses
[i
].define_match
);
3229 if (statuses
[i
].define_no_match
)
3230 free(statuses
[i
].define_no_match
);
3233 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3236 if (expect
->of_type
)
3237 free(expect
->of_type
);
3238 if (expect
->same_count_as
)
3239 free(expect
->same_count_as
);
3240 if (expect
->if_defined
)
3241 free(expect
->if_defined
);
3242 if (expect
->if_not_defined
)
3243 free(expect
->if_not_defined
);
3244 if (expect
->with_value
)
3245 free(expect
->with_value
);
3246 if (expect
->define_match
)
3247 free(expect
->define_match
);
3248 if (expect
->define_no_match
)
3249 free(expect
->define_no_match
);
3250 if (expect
->define_value
)
3251 free(expect
->define_value
);
3254 for (i
= 0; i
< num_displayed
; i
++)
3262 * 'expand_variables()' - Expand variables in a string.
3266 expand_variables(_cups_vars_t
*vars
, /* I - Variables */
3267 char *dst
, /* I - Destination string buffer */
3268 const char *src
, /* I - Source string */
3269 size_t dstsize
) /* I - Size of destination buffer */
3271 char *dstptr
, /* Pointer into destination */
3272 *dstend
, /* End of destination */
3273 temp
[256], /* Temporary string */
3274 *tempptr
; /* Pointer into temporary string */
3275 const char *value
; /* Value to substitute */
3279 dstend
= dst
+ dstsize
- 1;
3281 while (*src
&& dstptr
< dstend
)
3286 * Substitute a string/number...
3289 if (!strncmp(src
, "$$", 2))
3294 else if (!strncmp(src
, "$ENV[", 5))
3296 strlcpy(temp
, src
+ 5, sizeof(temp
));
3298 for (tempptr
= temp
; *tempptr
; tempptr
++)
3299 if (*tempptr
== ']')
3305 value
= getenv(temp
);
3306 src
+= tempptr
- temp
+ 5;
3310 strlcpy(temp
, src
+ 1, sizeof(temp
));
3312 for (tempptr
= temp
; *tempptr
; tempptr
++)
3313 if (!isalnum(*tempptr
& 255) && *tempptr
!= '-' && *tempptr
!= '_')
3319 if (!strcmp(temp
, "uri"))
3321 else if (!strcmp(temp
, "filename"))
3322 value
= vars
->filename
;
3323 else if (!strcmp(temp
, "scheme") || !strcmp(temp
, "method"))
3324 value
= vars
->scheme
;
3325 else if (!strcmp(temp
, "username"))
3326 value
= vars
->userpass
;
3327 else if (!strcmp(temp
, "hostname"))
3328 value
= vars
->hostname
;
3329 else if (!strcmp(temp
, "port"))
3331 snprintf(temp
, sizeof(temp
), "%d", vars
->port
);
3334 else if (!strcmp(temp
, "resource"))
3335 value
= vars
->resource
;
3336 else if (!strcmp(temp
, "user"))
3339 value
= get_variable(vars
, temp
);
3341 src
+= tempptr
- temp
+ 1;
3351 strlcpy(dstptr
, value
, (size_t)(dstend
- dstptr
+ 1));
3352 dstptr
+= strlen(dstptr
);
3364 * 'expect_matches()' - Return true if the tag matches the specification.
3367 static int /* O - 1 if matches, 0 otherwise */
3369 _cups_expect_t
*expect
, /* I - Expected attribute */
3370 ipp_tag_t value_tag
) /* I - Value tag for attribute */
3372 int match
; /* Match? */
3373 char *of_type
, /* Type name to match */
3374 *next
, /* Next name to match */
3375 sep
; /* Separator character */
3379 * If we don't expect a particular type, return immediately...
3382 if (!expect
->of_type
)
3386 * Parse the "of_type" value since the string can contain multiple attribute
3387 * types separated by "," or "|"...
3390 for (of_type
= expect
->of_type
, match
= 0; !match
&& *of_type
; of_type
= next
)
3393 * Find the next separator, and set it (temporarily) to nul if present.
3396 for (next
= of_type
; *next
&& *next
!= '|' && *next
!= ','; next
++);
3398 if ((sep
= *next
) != '\0')
3402 * Support some meta-types to make it easier to write the test file.
3405 if (!strcmp(of_type
, "text"))
3406 match
= value_tag
== IPP_TAG_TEXTLANG
|| value_tag
== IPP_TAG_TEXT
;
3407 else if (!strcmp(of_type
, "name"))
3408 match
= value_tag
== IPP_TAG_NAMELANG
|| value_tag
== IPP_TAG_NAME
;
3409 else if (!strcmp(of_type
, "collection"))
3410 match
= value_tag
== IPP_TAG_BEGIN_COLLECTION
;
3412 match
= value_tag
== ippTagValue(of_type
);
3415 * Restore the separator if we have one...
3427 * 'get_collection()' - Get a collection value from the current test file.
3430 static ipp_t
* /* O - Collection value */
3431 get_collection(_cups_vars_t
*vars
, /* I - Variables */
3432 FILE *fp
, /* I - File to read from */
3433 int *linenum
) /* IO - Line number */
3435 char token
[1024], /* Token from file */
3436 temp
[1024], /* Temporary string */
3437 attr
[128]; /* Attribute name */
3438 ipp_tag_t value
; /* Current value type */
3439 ipp_t
*col
= ippNew(); /* Collection value */
3440 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
3443 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
3445 if (!strcmp(token
, "}"))
3447 else if (!strcmp(token
, "{") && lastcol
)
3450 * Another collection value
3453 ipp_t
*subcol
= get_collection(vars
, fp
, linenum
);
3454 /* Collection value */
3457 ippSetCollection(col
, &lastcol
, ippGetCount(lastcol
), subcol
);
3461 else if (!_cups_strcasecmp(token
, "MEMBER"))
3469 if (!get_token(fp
, token
, sizeof(token
), linenum
))
3471 print_fatal_error("Missing MEMBER value tag on line %d.", *linenum
);
3475 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
3477 print_fatal_error("Bad MEMBER value tag \"%s\" on line %d.", token
,
3482 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
3484 print_fatal_error("Missing MEMBER name on line %d.", *linenum
);
3488 if (!get_token(fp
, temp
, sizeof(temp
), linenum
))
3490 print_fatal_error("Missing MEMBER value on line %d.", *linenum
);
3494 expand_variables(vars
, token
, temp
, sizeof(token
));
3498 case IPP_TAG_BOOLEAN
:
3499 if (!_cups_strcasecmp(token
, "true"))
3500 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
3502 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, (char)atoi(token
));
3505 case IPP_TAG_INTEGER
:
3507 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
3510 case IPP_TAG_RESOLUTION
:
3512 int xres
, /* X resolution */
3513 yres
; /* Y resolution */
3514 char units
[6]; /* Units */
3516 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
3517 (_cups_strcasecmp(units
, "dpi") &&
3518 _cups_strcasecmp(units
, "dpc") &&
3519 _cups_strcasecmp(units
, "dpcm") &&
3520 _cups_strcasecmp(units
, "other")))
3522 print_fatal_error("Bad resolution value \"%s\" on line %d.",
3527 if (!_cups_strcasecmp(units
, "dpi"))
3528 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
3529 else if (!_cups_strcasecmp(units
, "dpc") ||
3530 !_cups_strcasecmp(units
, "dpcm"))
3531 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_CM
, xres
, yres
);
3533 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, (ipp_res_t
)0, xres
, yres
);
3537 case IPP_TAG_RANGE
:
3539 int lowers
[4], /* Lower value */
3540 uppers
[4], /* Upper values */
3541 num_vals
; /* Number of values */
3544 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
3545 lowers
+ 0, uppers
+ 0,
3546 lowers
+ 1, uppers
+ 1,
3547 lowers
+ 2, uppers
+ 2,
3548 lowers
+ 3, uppers
+ 3);
3550 if ((num_vals
& 1) || num_vals
== 0)
3552 print_fatal_error("Bad rangeOfInteger value \"%s\" on line %d.",
3557 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
3562 case IPP_TAG_BEGIN_COLLECTION
:
3563 if (!strcmp(token
, "{"))
3565 ipp_t
*subcol
= get_collection(vars
, fp
, linenum
);
3566 /* Collection value */
3570 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
3578 print_fatal_error("Bad collection value on line %d.", *linenum
);
3582 case IPP_TAG_STRING
:
3583 ippAddOctetString(col
, IPP_TAG_ZERO
, attr
, token
, (int)strlen(token
));
3587 if (!strchr(token
, ','))
3588 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
3592 * Multiple string values...
3595 int num_values
; /* Number of values */
3596 char *values
[100], /* Values */
3597 *ptr
; /* Pointer to next value */
3603 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
3606 values
[num_values
] = ptr
;
3610 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
3611 NULL
, (const char **)values
);
3621 * If we get here there was a parse error; free memory and return.
3633 * 'get_filename()' - Get a filename based on the current test file.
3636 static char * /* O - Filename */
3637 get_filename(const char *testfile
, /* I - Current test file */
3638 char *dst
, /* I - Destination filename */
3639 const char *src
, /* I - Source filename */
3640 size_t dstsize
) /* I - Size of destination buffer */
3642 char *dstptr
; /* Pointer into destination */
3643 _cups_globals_t
*cg
= _cupsGlobals();
3647 if (*src
== '<' && src
[strlen(src
) - 1] == '>')
3650 * Map <filename> to CUPS_DATADIR/ipptool/filename...
3653 snprintf(dst
, dstsize
, "%s/ipptool/%s", cg
->cups_datadir
, src
+ 1);
3654 dstptr
= dst
+ strlen(dst
) - 1;
3658 else if (*src
== '/' || !strchr(testfile
, '/')
3660 || (isalpha(*src
& 255) && src
[1] == ':')
3665 * Use the path as-is...
3668 strlcpy(dst
, src
, dstsize
);
3673 * Make path relative to testfile...
3676 strlcpy(dst
, testfile
, dstsize
);
3677 if ((dstptr
= strrchr(dst
, '/')) != NULL
)
3680 dstptr
= dst
; /* Should never happen */
3682 strlcpy(dstptr
, src
, dstsize
- (size_t)(dstptr
- dst
));
3690 * 'get_string()' - Get a pointer to a string value or the portion of interest.
3693 static char * /* O - Pointer to string */
3694 get_string(ipp_attribute_t
*attr
, /* I - IPP attribute */
3695 int element
, /* I - Element to fetch */
3696 int flags
, /* I - Value ("with") flags */
3697 char *buffer
, /* I - Temporary buffer */
3698 size_t bufsize
) /* I - Size of temporary buffer */
3700 char *ptr
, /* Value */
3701 scheme
[256], /* URI scheme */
3702 userpass
[256], /* Username/password */
3703 hostname
[256], /* Hostname */
3704 resource
[1024]; /* Resource */
3705 int port
; /* Port number */
3708 ptr
= attr
->values
[element
].string
.text
;
3710 if (flags
& _CUPS_WITH_HOSTNAME
)
3712 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ptr
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), buffer
, (int)bufsize
, &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
3717 else if (flags
& _CUPS_WITH_RESOURCE
)
3719 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ptr
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, buffer
, (int)bufsize
) < HTTP_URI_STATUS_OK
)
3724 else if (flags
& _CUPS_WITH_SCHEME
)
3726 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ptr
, buffer
, (int)bufsize
, userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
3737 * 'get_token()' - Get a token from a file.
3740 static char * /* O - Token from file or NULL on EOF */
3741 get_token(FILE *fp
, /* I - File to read from */
3742 char *buf
, /* I - Buffer to read into */
3743 int buflen
, /* I - Length of buffer */
3744 int *linenum
) /* IO - Current line number */
3746 int ch
, /* Character from file */
3747 quote
; /* Quoting character */
3748 char *bufptr
, /* Pointer into buffer */
3749 *bufend
; /* End of buffer */
3755 * Skip whitespace...
3758 while (isspace(ch
= getc(fp
)))
3770 else if (ch
== '\'' || ch
== '\"')
3773 * Quoted text or regular expression...
3778 bufend
= buf
+ buflen
- 1;
3780 while ((ch
= getc(fp
)) != EOF
)
3785 * Escape next character...
3788 if (bufptr
< bufend
)
3789 *bufptr
++ = (char)ch
;
3791 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
3792 *bufptr
++ = (char)ch
;
3794 else if (ch
== quote
)
3796 else if (bufptr
< bufend
)
3797 *bufptr
++ = (char)ch
;
3810 while ((ch
= getc(fp
)) != EOF
)
3819 * Whitespace delimited text...
3825 bufend
= buf
+ buflen
- 1;
3827 while ((ch
= getc(fp
)) != EOF
)
3828 if (isspace(ch
) || ch
== '#')
3830 else if (bufptr
< bufend
)
3831 *bufptr
++ = (char)ch
;
3835 else if (ch
== '\n')
3847 * 'get_variable()' - Get the value of a variable.
3850 static char * /* O - Value or NULL */
3851 get_variable(_cups_vars_t
*vars
, /* I - Variables */
3852 const char *name
) /* I - Variable name */
3854 _cups_var_t key
, /* Search key */
3855 *match
; /* Matching variable, if any */
3858 key
.name
= (char *)name
;
3859 match
= cupsArrayFind(vars
->vars
, &key
);
3861 return (match
? match
->value
: NULL
);
3866 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
3870 static char * /* O - ISO 8601 date/time string */
3871 iso_date(ipp_uchar_t
*date
) /* I - IPP (RFC 1903) date/time value */
3873 time_t utctime
; /* UTC time since 1970 */
3874 struct tm
*utcdate
; /* UTC date/time */
3875 static char buffer
[255]; /* String buffer */
3878 utctime
= ippDateToTime(date
);
3879 utcdate
= gmtime(&utctime
);
3881 snprintf(buffer
, sizeof(buffer
), "%04d-%02d-%02dT%02d:%02d:%02dZ",
3882 utcdate
->tm_year
+ 1900, utcdate
->tm_mon
+ 1, utcdate
->tm_mday
,
3883 utcdate
->tm_hour
, utcdate
->tm_min
, utcdate
->tm_sec
);
3890 * 'password_cb()' - Password callback for authenticated tests.
3893 static const char * /* O - Password */
3894 password_cb(const char *prompt
) /* I - Prompt (unused) */
3903 * 'print_attr()' - Print an attribute on the screen.
3907 print_attr(ipp_attribute_t
*attr
, /* I - Attribute to print */
3908 ipp_tag_t
*group
) /* IO - Current group */
3910 int i
; /* Looping var */
3911 ipp_attribute_t
*colattr
; /* Collection attribute */
3914 if (Output
== _CUPS_OUTPUT_PLIST
)
3916 if (!attr
->name
|| (group
&& *group
!= attr
->group_tag
))
3918 if (attr
->group_tag
!= IPP_TAG_ZERO
)
3925 *group
= attr
->group_tag
;
3931 print_xml_string("key", attr
->name
);
3932 if (attr
->num_values
> 1)
3935 switch (attr
->value_tag
)
3937 case IPP_TAG_INTEGER
:
3939 for (i
= 0; i
< attr
->num_values
; i
++)
3940 if (Output
== _CUPS_OUTPUT_PLIST
)
3941 printf("<integer>%d</integer>\n", attr
->values
[i
].integer
);
3943 printf("%d ", attr
->values
[i
].integer
);
3946 case IPP_TAG_BOOLEAN
:
3947 for (i
= 0; i
< attr
->num_values
; i
++)
3948 if (Output
== _CUPS_OUTPUT_PLIST
)
3949 puts(attr
->values
[i
].boolean
? "<true />" : "<false />");
3950 else if (attr
->values
[i
].boolean
)
3951 fputs("true ", stdout
);
3953 fputs("false ", stdout
);
3956 case IPP_TAG_RANGE
:
3957 for (i
= 0; i
< attr
->num_values
; i
++)
3958 if (Output
== _CUPS_OUTPUT_PLIST
)
3959 printf("<dict><key>lower</key><integer>%d</integer>"
3960 "<key>upper</key><integer>%d</integer></dict>\n",
3961 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
3963 printf("%d-%d ", attr
->values
[i
].range
.lower
,
3964 attr
->values
[i
].range
.upper
);
3967 case IPP_TAG_RESOLUTION
:
3968 for (i
= 0; i
< attr
->num_values
; i
++)
3969 if (Output
== _CUPS_OUTPUT_PLIST
)
3970 printf("<dict><key>xres</key><integer>%d</integer>"
3971 "<key>yres</key><integer>%d</integer>"
3972 "<key>units</key><string>%s</string></dict>\n",
3973 attr
->values
[i
].resolution
.xres
,
3974 attr
->values
[i
].resolution
.yres
,
3975 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
3978 printf("%dx%d%s ", attr
->values
[i
].resolution
.xres
,
3979 attr
->values
[i
].resolution
.yres
,
3980 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
3985 for (i
= 0; i
< attr
->num_values
; i
++)
3986 if (Output
== _CUPS_OUTPUT_PLIST
)
3987 printf("<date>%s</date>\n", iso_date(attr
->values
[i
].date
));
3989 printf("%s ", iso_date(attr
->values
[i
].date
));
3992 case IPP_TAG_STRING
:
3993 for (i
= 0; i
< attr
->num_values
; i
++)
3995 if (Output
== _CUPS_OUTPUT_PLIST
)
3997 char buffer
[IPP_MAX_LENGTH
* 5 / 4 + 1];
4000 printf("<data>%s</data>\n",
4001 httpEncode64_2(buffer
, sizeof(buffer
),
4002 attr
->values
[i
].unknown
.data
,
4003 attr
->values
[i
].unknown
.length
));
4007 char *ptr
, /* Pointer into data */
4008 *end
; /* End of data */
4011 for (ptr
= attr
->values
[i
].unknown
.data
,
4012 end
= ptr
+ attr
->values
[i
].unknown
.length
;
4016 if (*ptr
== '\\' || *ptr
== '\"')
4017 printf("\\%c", *ptr
);
4018 else if (!isprint(*ptr
& 255))
4019 printf("\\%03o", *ptr
& 255);
4030 case IPP_TAG_KEYWORD
:
4031 case IPP_TAG_CHARSET
:
4033 case IPP_TAG_MIMETYPE
:
4034 case IPP_TAG_LANGUAGE
:
4035 for (i
= 0; i
< attr
->num_values
; i
++)
4036 if (Output
== _CUPS_OUTPUT_PLIST
)
4037 print_xml_string("string", attr
->values
[i
].string
.text
);
4039 printf("\"%s\" ", attr
->values
[i
].string
.text
);
4042 case IPP_TAG_TEXTLANG
:
4043 case IPP_TAG_NAMELANG
:
4044 for (i
= 0; i
< attr
->num_values
; i
++)
4045 if (Output
== _CUPS_OUTPUT_PLIST
)
4047 fputs("<dict><key>language</key><string>", stdout
);
4048 print_xml_string(NULL
, attr
->values
[i
].string
.language
);
4049 fputs("</string><key>string</key><string>", stdout
);
4050 print_xml_string(NULL
, attr
->values
[i
].string
.text
);
4051 puts("</string></dict>");
4054 printf("\"%s\"[%s] ", attr
->values
[i
].string
.text
,
4055 attr
->values
[i
].string
.language
);
4058 case IPP_TAG_BEGIN_COLLECTION
:
4059 for (i
= 0; i
< attr
->num_values
; i
++)
4061 if (Output
== _CUPS_OUTPUT_PLIST
)
4064 for (colattr
= attr
->values
[i
].collection
->attrs
;
4066 colattr
= colattr
->next
)
4067 print_attr(colattr
, NULL
);
4075 print_col(attr
->values
[i
].collection
);
4081 if (Output
== _CUPS_OUTPUT_PLIST
)
4082 printf("<string><<%s>></string>\n",
4083 ippTagString(attr
->value_tag
));
4085 fputs(ippTagString(attr
->value_tag
), stdout
);
4089 if (attr
->num_values
> 1)
4094 char buffer
[8192]; /* Value buffer */
4096 if (Output
== _CUPS_OUTPUT_TEST
)
4100 puts(" -- separator --");
4104 printf(" %s (%s%s) = ", attr
->name
,
4105 attr
->num_values
> 1 ? "1setOf " : "",
4106 ippTagString(attr
->value_tag
));
4109 ippAttributeString(attr
, buffer
, sizeof(buffer
));
4116 * 'print_col()' - Print a collection attribute on the screen.
4120 print_col(ipp_t
*col
) /* I - Collection attribute to print */
4122 int i
; /* Looping var */
4123 ipp_attribute_t
*attr
; /* Current attribute in collection */
4126 fputs("{ ", stdout
);
4127 for (attr
= col
->attrs
; attr
; attr
= attr
->next
)
4129 printf("%s (%s%s) = ", attr
->name
, attr
->num_values
> 1 ? "1setOf " : "",
4130 ippTagString(attr
->value_tag
));
4132 switch (attr
->value_tag
)
4134 case IPP_TAG_INTEGER
:
4136 for (i
= 0; i
< attr
->num_values
; i
++)
4137 printf("%d ", attr
->values
[i
].integer
);
4140 case IPP_TAG_BOOLEAN
:
4141 for (i
= 0; i
< attr
->num_values
; i
++)
4142 if (attr
->values
[i
].boolean
)
4148 case IPP_TAG_NOVALUE
:
4152 case IPP_TAG_RANGE
:
4153 for (i
= 0; i
< attr
->num_values
; i
++)
4154 printf("%d-%d ", attr
->values
[i
].range
.lower
,
4155 attr
->values
[i
].range
.upper
);
4158 case IPP_TAG_RESOLUTION
:
4159 for (i
= 0; i
< attr
->num_values
; i
++)
4160 printf("%dx%d%s ", attr
->values
[i
].resolution
.xres
,
4161 attr
->values
[i
].resolution
.yres
,
4162 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
4166 case IPP_TAG_STRING
:
4169 case IPP_TAG_KEYWORD
:
4170 case IPP_TAG_CHARSET
:
4172 case IPP_TAG_MIMETYPE
:
4173 case IPP_TAG_LANGUAGE
:
4174 for (i
= 0; i
< attr
->num_values
; i
++)
4175 printf("\"%s\" ", attr
->values
[i
].string
.text
);
4178 case IPP_TAG_TEXTLANG
:
4179 case IPP_TAG_NAMELANG
:
4180 for (i
= 0; i
< attr
->num_values
; i
++)
4181 printf("\"%s\"[%s] ", attr
->values
[i
].string
.text
,
4182 attr
->values
[i
].string
.language
);
4185 case IPP_TAG_BEGIN_COLLECTION
:
4186 for (i
= 0; i
< attr
->num_values
; i
++)
4188 print_col(attr
->values
[i
].collection
);
4194 break; /* anti-compiler-warning-code */
4203 * 'print_csv()' - Print a line of CSV text.
4208 ipp_attribute_t
*attr
, /* I - First attribute for line */
4209 int num_displayed
, /* I - Number of attributes to display */
4210 char **displayed
, /* I - Attributes to display */
4211 size_t *widths
) /* I - Column widths */
4213 int i
; /* Looping var */
4214 size_t maxlength
; /* Max length of all columns */
4215 char *buffer
, /* String buffer */
4216 *bufptr
; /* Pointer into buffer */
4217 ipp_attribute_t
*current
; /* Current attribute */
4221 * Get the maximum string length we have to show and allocate...
4224 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4225 if (widths
[i
] > maxlength
)
4226 maxlength
= widths
[i
];
4230 if ((buffer
= malloc(maxlength
)) == NULL
)
4234 * Loop through the attributes to display...
4239 for (i
= 0; i
< num_displayed
; i
++)
4246 for (current
= attr
; current
; current
= current
->next
)
4250 else if (!strcmp(current
->name
, displayed
[i
]))
4252 ippAttributeString(current
, buffer
, maxlength
);
4257 if (strchr(buffer
, ',') != NULL
|| strchr(buffer
, '\"') != NULL
||
4258 strchr(buffer
, '\\') != NULL
)
4261 for (bufptr
= buffer
; *bufptr
; bufptr
++)
4263 if (*bufptr
== '\\' || *bufptr
== '\"')
4270 fputs(buffer
, stdout
);
4276 for (i
= 0; i
< num_displayed
; i
++)
4281 fputs(displayed
[i
], stdout
);
4291 * 'print_fatal_error()' - Print a fatal error message.
4295 print_fatal_error(const char *s
, /* I - Printf-style format string */
4296 ...) /* I - Additional arguments as needed */
4298 char buffer
[10240]; /* Format buffer */
4299 va_list ap
; /* Pointer to arguments */
4303 * Format the error message...
4307 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
4314 if (Output
== _CUPS_OUTPUT_PLIST
)
4317 print_xml_trailer(0, buffer
);
4320 _cupsLangPrintf(stderr
, "ipptool: %s", buffer
);
4325 * 'print_line()' - Print a line of formatted or CSV text.
4330 ipp_attribute_t
*attr
, /* I - First attribute for line */
4331 int num_displayed
, /* I - Number of attributes to display */
4332 char **displayed
, /* I - Attributes to display */
4333 size_t *widths
) /* I - Column widths */
4335 int i
; /* Looping var */
4336 size_t maxlength
; /* Max length of all columns */
4337 char *buffer
; /* String buffer */
4338 ipp_attribute_t
*current
; /* Current attribute */
4342 * Get the maximum string length we have to show and allocate...
4345 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4346 if (widths
[i
] > maxlength
)
4347 maxlength
= widths
[i
];
4351 if ((buffer
= malloc(maxlength
)) == NULL
)
4355 * Loop through the attributes to display...
4360 for (i
= 0; i
< num_displayed
; i
++)
4367 for (current
= attr
; current
; current
= current
->next
)
4371 else if (!strcmp(current
->name
, displayed
[i
]))
4373 ippAttributeString(current
, buffer
, maxlength
);
4378 printf("%*s", (int)-widths
[i
], buffer
);
4384 for (i
= 0; i
< num_displayed
; i
++)
4389 printf("%*s", (int)-widths
[i
], displayed
[i
]);
4393 for (i
= 0; i
< num_displayed
; i
++)
4398 memset(buffer
, '-', widths
[i
]);
4399 buffer
[widths
[i
]] = '\0';
4400 fputs(buffer
, stdout
);
4410 * 'print_xml_header()' - Print a standard XML plist header.
4414 print_xml_header(void)
4418 puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
4419 puts("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
4420 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
4421 puts("<plist version=\"1.0\">");
4423 puts("<key>ipptoolVersion</key>");
4424 puts("<string>" CUPS_SVERSION
"</string>");
4425 puts("<key>Transfer</key>");
4426 printf("<string>%s</string>\n",
4427 Transfer
== _CUPS_TRANSFER_AUTO
? "auto" :
4428 Transfer
== _CUPS_TRANSFER_CHUNKED
? "chunked" : "length");
4429 puts("<key>Tests</key>");
4438 * 'print_xml_string()' - Print an XML string with escaping.
4442 print_xml_string(const char *element
, /* I - Element name or NULL */
4443 const char *s
) /* I - String to print */
4446 printf("<%s>", element
);
4451 fputs("&", stdout
);
4453 fputs("<", stdout
);
4455 fputs(">", stdout
);
4456 else if ((*s
& 0xe0) == 0xc0)
4459 * Validate UTF-8 two-byte sequence...
4462 if ((s
[1] & 0xc0) != 0x80)
4473 else if ((*s
& 0xf0) == 0xe0)
4476 * Validate UTF-8 three-byte sequence...
4479 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80)
4491 else if ((*s
& 0xf8) == 0xf0)
4494 * Validate UTF-8 four-byte sequence...
4497 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80 ||
4498 (s
[3] & 0xc0) != 0x80)
4511 else if ((*s
& 0x80) || (*s
< ' ' && !isspace(*s
& 255)))
4514 * Invalid control character...
4526 printf("</%s>\n", element
);
4531 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
4535 print_xml_trailer(int success
, /* I - 1 on success, 0 on failure */
4536 const char *message
) /* I - Error message or NULL */
4541 puts("<key>Successful</key>");
4542 puts(success
? "<true />" : "<false />");
4545 puts("<key>ErrorMessage</key>");
4546 print_xml_string("string", message
);
4557 * 'set_variable()' - Set a variable value.
4561 set_variable(_cups_vars_t
*vars
, /* I - Variables */
4562 const char *name
, /* I - Variable name */
4563 const char *value
) /* I - Value string */
4565 _cups_var_t key
, /* Search key */
4566 *var
; /* New variable */
4569 if (!_cups_strcasecmp(name
, "filename"))
4572 free(vars
->filename
);
4574 vars
->filename
= strdup(value
);
4578 key
.name
= (char *)name
;
4579 if ((var
= cupsArrayFind(vars
->vars
, &key
)) != NULL
)
4582 var
->value
= strdup(value
);
4584 else if ((var
= malloc(sizeof(_cups_var_t
))) == NULL
)
4586 print_fatal_error("Unable to allocate memory for variable \"%s\".", name
);
4591 var
->name
= strdup(name
);
4592 var
->value
= strdup(value
);
4594 cupsArrayAdd(vars
->vars
, var
);
4601 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
4605 sigterm_handler(int sig
) /* I - Signal number (unused) */
4611 signal(SIGINT
, SIG_DFL
);
4612 signal(SIGTERM
, SIG_DFL
);
4618 * 'timeout_cb()' - Handle HTTP timeouts.
4621 static int /* O - 1 to continue, 0 to cancel */
4622 timeout_cb(http_t
*http
, /* I - Connection to server */
4623 void *user_data
) /* I - User data (unused) */
4625 int buffered
= 0; /* Bytes buffered but not yet sent */
4631 * If the socket still have data waiting to be sent to the printer (as can
4632 * happen if the printer runs out of paper), continue to wait until the output
4633 * buffer is empty...
4636 #ifdef SO_NWRITE /* OS X and some versions of Linux */
4637 socklen_t len
= sizeof(buffered
); /* Size of return value */
4639 if (getsockopt(httpGetFd(http
), SOL_SOCKET
, SO_NWRITE
, &buffered
, &len
))
4642 #elif defined(SIOCOUTQ) /* Others except Windows */
4643 if (ioctl(httpGetFd(http
), SIOCOUTQ
, &buffered
))
4646 #else /* Windows (not possible) */
4648 #endif /* SO_NWRITE */
4650 return (buffered
> 0);
4655 * 'usage()' - Show program usage.
4661 _cupsLangPuts(stderr
, _("Usage: ipptool [options] URI filename [ ... "
4663 _cupsLangPuts(stderr
, _("Options:"));
4664 _cupsLangPuts(stderr
, _(" -4 Connect using IPv4."));
4665 _cupsLangPuts(stderr
, _(" -6 Connect using IPv6."));
4666 _cupsLangPuts(stderr
, _(" -C Send requests using "
4667 "chunking (default)."));
4668 _cupsLangPuts(stdout
, _(" -E Test with HTTP Upgrade to "
4670 _cupsLangPuts(stderr
, _(" -I Ignore errors."));
4671 _cupsLangPuts(stderr
, _(" -L Send requests using "
4672 "content-length."));
4673 _cupsLangPuts(stderr
, _(" -S Test with SSL "
4675 _cupsLangPuts(stderr
, _(" -T seconds Set the receive/send "
4676 "timeout in seconds."));
4677 _cupsLangPuts(stderr
, _(" -V version Set default IPP "
4679 _cupsLangPuts(stderr
, _(" -X Produce XML plist instead "
4681 _cupsLangPuts(stderr
, _(" -d name=value Set named variable to "
4683 _cupsLangPuts(stderr
, _(" -f filename Set default request "
4685 _cupsLangPuts(stderr
, _(" -i seconds Repeat the last file with "
4686 "the given time interval."));
4687 _cupsLangPuts(stderr
, _(" -n count Repeat the last file the "
4688 "given number of times."));
4689 _cupsLangPuts(stderr
, _(" -q Run silently."));
4690 _cupsLangPuts(stderr
, _(" -t Produce a test report."));
4691 _cupsLangPuts(stderr
, _(" -v Be verbose."));
4698 * 'validate_attr()' - Determine whether an attribute is valid.
4701 static int /* O - 1 if valid, 0 otherwise */
4702 validate_attr(cups_array_t
*errors
, /* I - Errors array */
4703 ipp_attribute_t
*attr
) /* I - Attribute to validate */
4705 int i
; /* Looping var */
4706 char scheme
[64], /* Scheme from URI */
4707 userpass
[256], /* Username/password from URI */
4708 hostname
[256], /* Hostname from URI */
4709 resource
[1024]; /* Resource from URI */
4710 int port
, /* Port number from URI */
4711 uri_status
, /* URI separation status */
4712 valid
= 1; /* Is the attribute valid? */
4713 const char *ptr
; /* Pointer into string */
4714 ipp_attribute_t
*colattr
; /* Collection attribute */
4715 regex_t re
; /* Regular expression */
4716 ipp_uchar_t
*date
; /* Current date value */
4727 * Validate the attribute name.
4730 for (ptr
= attr
->name
; *ptr
; ptr
++)
4731 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' && *ptr
!= '_')
4734 if (*ptr
|| ptr
== attr
->name
)
4739 "\"%s\": Bad attribute name - invalid character "
4740 "(RFC 2911 section 4.1.3).", attr
->name
);
4743 if ((ptr
- attr
->name
) > 255)
4748 "\"%s\": Bad attribute name - bad length "
4749 "(RFC 2911 section 4.1.3).", attr
->name
);
4752 switch (attr
->value_tag
)
4754 case IPP_TAG_INTEGER
:
4757 case IPP_TAG_BOOLEAN
:
4758 for (i
= 0; i
< attr
->num_values
; i
++)
4760 if (attr
->values
[i
].boolean
!= 0 &&
4761 attr
->values
[i
].boolean
!= 1)
4766 "\"%s\": Bad boolen value %d "
4767 "(RFC 2911 section 4.1.11).", attr
->name
,
4768 attr
->values
[i
].boolean
);
4774 for (i
= 0; i
< attr
->num_values
; i
++)
4776 if (attr
->values
[i
].integer
< 1)
4781 "\"%s\": Bad enum value %d - out of range "
4782 "(RFC 2911 section 4.1.4).", attr
->name
,
4783 attr
->values
[i
].integer
);
4788 case IPP_TAG_STRING
:
4789 for (i
= 0; i
< attr
->num_values
; i
++)
4791 if (attr
->values
[i
].unknown
.length
> IPP_MAX_OCTETSTRING
)
4796 "\"%s\": Bad octetString value - bad length %d "
4797 "(RFC 2911 section 4.1.10).", attr
->name
,
4798 attr
->values
[i
].unknown
.length
);
4804 for (i
= 0; i
< attr
->num_values
; i
++)
4806 date
= attr
->values
[i
].date
;
4808 if (date
[2] < 1 || date
[2] > 12)
4813 "\"%s\": Bad dateTime month %u "
4814 "(RFC 2911 section 4.1.14).", attr
->name
, date
[2]);
4817 if (date
[3] < 1 || date
[3] > 31)
4822 "\"%s\": Bad dateTime day %u "
4823 "(RFC 2911 section 4.1.14).", attr
->name
, date
[3]);
4831 "\"%s\": Bad dateTime hours %u "
4832 "(RFC 2911 section 4.1.14).", attr
->name
, date
[4]);
4840 "\"%s\": Bad dateTime minutes %u "
4841 "(RFC 2911 section 4.1.14).", attr
->name
, date
[5]);
4849 "\"%s\": Bad dateTime seconds %u "
4850 "(RFC 2911 section 4.1.14).", attr
->name
, date
[6]);
4858 "\"%s\": Bad dateTime deciseconds %u "
4859 "(RFC 2911 section 4.1.14).", attr
->name
, date
[7]);
4862 if (date
[8] != '-' && date
[8] != '+')
4867 "\"%s\": Bad dateTime UTC sign '%c' "
4868 "(RFC 2911 section 4.1.14).", attr
->name
, date
[8]);
4876 "\"%s\": Bad dateTime UTC hours %u "
4877 "(RFC 2911 section 4.1.14).", attr
->name
, date
[9]);
4885 "\"%s\": Bad dateTime UTC minutes %u "
4886 "(RFC 2911 section 4.1.14).", attr
->name
, date
[10]);
4891 case IPP_TAG_RESOLUTION
:
4892 for (i
= 0; i
< attr
->num_values
; i
++)
4894 if (attr
->values
[i
].resolution
.xres
<= 0)
4899 "\"%s\": Bad resolution value %dx%d%s - cross "
4900 "feed resolution must be positive "
4901 "(RFC 2911 section 4.1.15).", attr
->name
,
4902 attr
->values
[i
].resolution
.xres
,
4903 attr
->values
[i
].resolution
.yres
,
4904 attr
->values
[i
].resolution
.units
==
4905 IPP_RES_PER_INCH
? "dpi" :
4906 attr
->values
[i
].resolution
.units
==
4907 IPP_RES_PER_CM
? "dpcm" : "unknown");
4910 if (attr
->values
[i
].resolution
.yres
<= 0)
4915 "\"%s\": Bad resolution value %dx%d%s - feed "
4916 "resolution must be positive "
4917 "(RFC 2911 section 4.1.15).", attr
->name
,
4918 attr
->values
[i
].resolution
.xres
,
4919 attr
->values
[i
].resolution
.yres
,
4920 attr
->values
[i
].resolution
.units
==
4921 IPP_RES_PER_INCH
? "dpi" :
4922 attr
->values
[i
].resolution
.units
==
4923 IPP_RES_PER_CM
? "dpcm" : "unknown");
4926 if (attr
->values
[i
].resolution
.units
!= IPP_RES_PER_INCH
&&
4927 attr
->values
[i
].resolution
.units
!= IPP_RES_PER_CM
)
4932 "\"%s\": Bad resolution value %dx%d%s - bad "
4933 "units value (RFC 2911 section 4.1.15).",
4934 attr
->name
, attr
->values
[i
].resolution
.xres
,
4935 attr
->values
[i
].resolution
.yres
,
4936 attr
->values
[i
].resolution
.units
==
4937 IPP_RES_PER_INCH
? "dpi" :
4938 attr
->values
[i
].resolution
.units
==
4939 IPP_RES_PER_CM
? "dpcm" : "unknown");
4944 case IPP_TAG_RANGE
:
4945 for (i
= 0; i
< attr
->num_values
; i
++)
4947 if (attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
4952 "\"%s\": Bad rangeOfInteger value %d-%d - lower "
4953 "greater than upper (RFC 2911 section 4.1.13).",
4954 attr
->name
, attr
->values
[i
].range
.lower
,
4955 attr
->values
[i
].range
.upper
);
4960 case IPP_TAG_BEGIN_COLLECTION
:
4961 for (i
= 0; i
< attr
->num_values
; i
++)
4963 for (colattr
= attr
->values
[i
].collection
->attrs
;
4965 colattr
= colattr
->next
)
4967 if (!validate_attr(NULL
, colattr
))
4974 if (colattr
&& errors
)
4976 add_stringf(errors
, "\"%s\": Bad collection value.", attr
->name
);
4980 validate_attr(errors
, colattr
);
4981 colattr
= colattr
->next
;
4988 case IPP_TAG_TEXTLANG
:
4989 for (i
= 0; i
< attr
->num_values
; i
++)
4991 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4993 if ((*ptr
& 0xe0) == 0xc0)
4996 if ((*ptr
& 0xc0) != 0x80)
4999 else if ((*ptr
& 0xf0) == 0xe0)
5002 if ((*ptr
& 0xc0) != 0x80)
5005 if ((*ptr
& 0xc0) != 0x80)
5008 else if ((*ptr
& 0xf8) == 0xf0)
5011 if ((*ptr
& 0xc0) != 0x80)
5014 if ((*ptr
& 0xc0) != 0x80)
5017 if ((*ptr
& 0xc0) != 0x80)
5020 else if (*ptr
& 0x80)
5029 "\"%s\": Bad text value \"%s\" - bad UTF-8 "
5030 "sequence (RFC 2911 section 4.1.1).", attr
->name
,
5031 attr
->values
[i
].string
.text
);
5034 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_TEXT
- 1))
5039 "\"%s\": Bad text value \"%s\" - bad length %d "
5040 "(RFC 2911 section 4.1.1).", attr
->name
,
5041 attr
->values
[i
].string
.text
,
5042 (int)strlen(attr
->values
[i
].string
.text
));
5048 case IPP_TAG_NAMELANG
:
5049 for (i
= 0; i
< attr
->num_values
; i
++)
5051 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5053 if ((*ptr
& 0xe0) == 0xc0)
5056 if ((*ptr
& 0xc0) != 0x80)
5059 else if ((*ptr
& 0xf0) == 0xe0)
5062 if ((*ptr
& 0xc0) != 0x80)
5065 if ((*ptr
& 0xc0) != 0x80)
5068 else if ((*ptr
& 0xf8) == 0xf0)
5071 if ((*ptr
& 0xc0) != 0x80)
5074 if ((*ptr
& 0xc0) != 0x80)
5077 if ((*ptr
& 0xc0) != 0x80)
5080 else if (*ptr
& 0x80)
5089 "\"%s\": Bad name value \"%s\" - bad UTF-8 "
5090 "sequence (RFC 2911 section 4.1.2).", attr
->name
,
5091 attr
->values
[i
].string
.text
);
5094 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_NAME
- 1))
5099 "\"%s\": Bad name value \"%s\" - bad length %d "
5100 "(RFC 2911 section 4.1.2).", attr
->name
,
5101 attr
->values
[i
].string
.text
,
5102 (int)strlen(attr
->values
[i
].string
.text
));
5107 case IPP_TAG_KEYWORD
:
5108 for (i
= 0; i
< attr
->num_values
; i
++)
5110 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5111 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' &&
5115 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5120 "\"%s\": Bad keyword value \"%s\" - invalid "
5121 "character (RFC 2911 section 4.1.3).",
5122 attr
->name
, attr
->values
[i
].string
.text
);
5125 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_KEYWORD
- 1))
5130 "\"%s\": Bad keyword value \"%s\" - bad "
5131 "length %d (RFC 2911 section 4.1.3).",
5132 attr
->name
, attr
->values
[i
].string
.text
,
5133 (int)strlen(attr
->values
[i
].string
.text
));
5139 for (i
= 0; i
< attr
->num_values
; i
++)
5141 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
,
5142 attr
->values
[i
].string
.text
,
5143 scheme
, sizeof(scheme
),
5144 userpass
, sizeof(userpass
),
5145 hostname
, sizeof(hostname
),
5146 &port
, resource
, sizeof(resource
));
5148 if (uri_status
< HTTP_URI_OK
)
5153 "\"%s\": Bad URI value \"%s\" - %s "
5154 "(RFC 2911 section 4.1.5).", attr
->name
,
5155 attr
->values
[i
].string
.text
,
5156 httpURIStatusString(uri_status
));
5159 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_URI
- 1))
5164 "\"%s\": Bad URI value \"%s\" - bad length %d "
5165 "(RFC 2911 section 4.1.5).", attr
->name
,
5166 attr
->values
[i
].string
.text
,
5167 (int)strlen(attr
->values
[i
].string
.text
));
5172 case IPP_TAG_URISCHEME
:
5173 for (i
= 0; i
< attr
->num_values
; i
++)
5175 ptr
= attr
->values
[i
].string
.text
;
5176 if (islower(*ptr
& 255))
5178 for (ptr
++; *ptr
; ptr
++)
5179 if (!islower(*ptr
& 255) && !isdigit(*ptr
& 255) &&
5180 *ptr
!= '+' && *ptr
!= '-' && *ptr
!= '.')
5184 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5189 "\"%s\": Bad uriScheme value \"%s\" - bad "
5190 "characters (RFC 2911 section 4.1.6).",
5191 attr
->name
, attr
->values
[i
].string
.text
);
5194 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_URISCHEME
- 1))
5199 "\"%s\": Bad uriScheme value \"%s\" - bad "
5200 "length %d (RFC 2911 section 4.1.6).",
5201 attr
->name
, attr
->values
[i
].string
.text
,
5202 (int)strlen(attr
->values
[i
].string
.text
));
5207 case IPP_TAG_CHARSET
:
5208 for (i
= 0; i
< attr
->num_values
; i
++)
5210 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5211 if (!isprint(*ptr
& 255) || isupper(*ptr
& 255) ||
5212 isspace(*ptr
& 255))
5215 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5220 "\"%s\": Bad charset value \"%s\" - bad "
5221 "characters (RFC 2911 section 4.1.7).",
5222 attr
->name
, attr
->values
[i
].string
.text
);
5225 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_CHARSET
- 1))
5230 "\"%s\": Bad charset value \"%s\" - bad "
5231 "length %d (RFC 2911 section 4.1.7).",
5232 attr
->name
, attr
->values
[i
].string
.text
,
5233 (int)strlen(attr
->values
[i
].string
.text
));
5238 case IPP_TAG_LANGUAGE
:
5240 * The following regular expression is derived from the ABNF for
5241 * language tags in RFC 4646. All I can say is that this is the
5242 * easiest way to check the values...
5245 if ((i
= regcomp(&re
,
5247 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
5249 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
5250 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
5251 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
5252 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
5253 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
5255 "x(-[a-z0-9]{1,8})+" /* privateuse */
5257 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
5259 REG_NOSUB
| REG_EXTENDED
)) != 0)
5261 char temp
[256]; /* Temporary error string */
5263 regerror(i
, &re
, temp
, sizeof(temp
));
5264 print_fatal_error("Unable to compile naturalLanguage regular "
5265 "expression: %s.", temp
);
5269 for (i
= 0; i
< attr
->num_values
; i
++)
5271 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5276 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5277 "characters (RFC 2911 section 4.1.8).",
5278 attr
->name
, attr
->values
[i
].string
.text
);
5281 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_LANGUAGE
- 1))
5286 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5287 "length %d (RFC 2911 section 4.1.8).",
5288 attr
->name
, attr
->values
[i
].string
.text
,
5289 (int)strlen(attr
->values
[i
].string
.text
));
5296 case IPP_TAG_MIMETYPE
:
5298 * The following regular expression is derived from the ABNF for
5299 * language tags in RFC 2045 and 4288. All I can say is that this is
5300 * the easiest way to check the values...
5303 if ((i
= regcomp(&re
,
5305 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
5307 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
5308 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
5309 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
5312 REG_NOSUB
| REG_EXTENDED
)) != 0)
5314 char temp
[256]; /* Temporary error string */
5316 regerror(i
, &re
, temp
, sizeof(temp
));
5317 print_fatal_error("Unable to compile mimeMediaType regular "
5318 "expression: %s.", temp
);
5322 for (i
= 0; i
< attr
->num_values
; i
++)
5324 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5329 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5330 "characters (RFC 2911 section 4.1.9).",
5331 attr
->name
, attr
->values
[i
].string
.text
);
5334 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_MIMETYPE
- 1))
5339 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5340 "length %d (RFC 2911 section 4.1.9).",
5341 attr
->name
, attr
->values
[i
].string
.text
,
5342 (int)strlen(attr
->values
[i
].string
.text
));
5358 * 'with_value()' - Test a WITH-VALUE predicate.
5361 static int /* O - 1 on match, 0 on non-match */
5362 with_value(cups_array_t
*errors
, /* I - Errors array */
5363 char *value
, /* I - Value string */
5364 int flags
, /* I - Flags for match */
5365 ipp_attribute_t
*attr
, /* I - Attribute to compare */
5366 char *matchbuf
, /* I - Buffer to hold matching value */
5367 size_t matchlen
) /* I - Length of match buffer */
5369 int i
, /* Looping var */
5371 char temp
[1024], /* Temporary value string */
5372 *valptr
; /* Pointer into value */
5376 match
= (flags
& _CUPS_WITH_ALL
) ? 1 : 0;
5379 * NULL matches everything.
5382 if (!value
|| !*value
)
5386 * Compare the value string to the attribute value.
5389 switch (attr
->value_tag
)
5391 case IPP_TAG_INTEGER
:
5393 for (i
= 0; i
< attr
->num_values
; i
++)
5395 char op
, /* Comparison operator */
5396 *nextptr
; /* Next pointer */
5397 int intvalue
, /* Integer value */
5398 valmatch
= 0; /* Does the current value match? */
5402 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5403 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5404 *valptr
== '=' || *valptr
== '>')
5407 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5409 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5417 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
5418 if (nextptr
== valptr
)
5422 if ((op
== '=' && attr
->values
[i
].integer
== intvalue
) ||
5423 (op
== '<' && attr
->values
[i
].integer
< intvalue
) ||
5424 (op
== '>' && attr
->values
[i
].integer
> intvalue
))
5427 snprintf(matchbuf
, matchlen
, "%d", attr
->values
[i
].integer
);
5434 if (flags
& _CUPS_WITH_ALL
)
5449 if (!match
&& errors
)
5451 for (i
= 0; i
< attr
->num_values
; i
++)
5452 add_stringf(errors
, "GOT: %s=%d", attr
->name
,
5453 attr
->values
[i
].integer
);
5457 case IPP_TAG_RANGE
:
5458 for (i
= 0; i
< attr
->num_values
; i
++)
5460 char op
, /* Comparison operator */
5461 *nextptr
; /* Next pointer */
5462 int intvalue
, /* Integer value */
5463 valmatch
= 0; /* Does the current value match? */
5467 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5468 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5469 *valptr
== '=' || *valptr
== '>')
5472 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5474 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5482 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
5483 if (nextptr
== valptr
)
5487 if ((op
== '=' && (attr
->values
[i
].range
.lower
== intvalue
||
5488 attr
->values
[i
].range
.upper
== intvalue
)) ||
5489 (op
== '<' && attr
->values
[i
].range
.upper
< intvalue
) ||
5490 (op
== '>' && attr
->values
[i
].range
.upper
> intvalue
))
5493 snprintf(matchbuf
, matchlen
, "%d-%d",
5494 attr
->values
[0].range
.lower
,
5495 attr
->values
[0].range
.upper
);
5502 if (flags
& _CUPS_WITH_ALL
)
5517 if (!match
&& errors
)
5519 for (i
= 0; i
< attr
->num_values
; i
++)
5520 add_stringf(errors
, "GOT: %s=%d-%d", attr
->name
,
5521 attr
->values
[i
].range
.lower
,
5522 attr
->values
[i
].range
.upper
);
5526 case IPP_TAG_BOOLEAN
:
5527 for (i
= 0; i
< attr
->num_values
; i
++)
5529 if (!strcmp(value
, "true") == attr
->values
[i
].boolean
)
5532 strlcpy(matchbuf
, value
, matchlen
);
5534 if (!(flags
& _CUPS_WITH_ALL
))
5540 else if (flags
& _CUPS_WITH_ALL
)
5547 if (!match
&& errors
)
5549 for (i
= 0; i
< attr
->num_values
; i
++)
5550 add_stringf(errors
, "GOT: %s=%s", attr
->name
,
5551 attr
->values
[i
].boolean
? "true" : "false");
5555 case IPP_TAG_RESOLUTION
:
5556 for (i
= 0; i
< attr
->num_values
; i
++)
5558 if (attr
->values
[i
].resolution
.xres
==
5559 attr
->values
[i
].resolution
.yres
)
5560 snprintf(temp
, sizeof(temp
), "%d%s",
5561 attr
->values
[i
].resolution
.xres
,
5562 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5565 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5566 attr
->values
[i
].resolution
.xres
,
5567 attr
->values
[i
].resolution
.yres
,
5568 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5571 if (!strcmp(value
, temp
))
5574 strlcpy(matchbuf
, value
, matchlen
);
5576 if (!(flags
& _CUPS_WITH_ALL
))
5582 else if (flags
& _CUPS_WITH_ALL
)
5589 if (!match
&& errors
)
5591 for (i
= 0; i
< attr
->num_values
; i
++)
5593 if (attr
->values
[i
].resolution
.xres
==
5594 attr
->values
[i
].resolution
.yres
)
5595 snprintf(temp
, sizeof(temp
), "%d%s",
5596 attr
->values
[i
].resolution
.xres
,
5597 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5600 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5601 attr
->values
[i
].resolution
.xres
,
5602 attr
->values
[i
].resolution
.yres
,
5603 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5606 if (strcmp(value
, temp
))
5607 add_stringf(errors
, "GOT: %s=%s", attr
->name
, temp
);
5612 case IPP_TAG_NOVALUE
:
5613 case IPP_TAG_UNKNOWN
:
5616 case IPP_TAG_CHARSET
:
5617 case IPP_TAG_KEYWORD
:
5618 case IPP_TAG_LANGUAGE
:
5619 case IPP_TAG_MIMETYPE
:
5621 case IPP_TAG_NAMELANG
:
5623 case IPP_TAG_TEXTLANG
:
5625 case IPP_TAG_URISCHEME
:
5626 if (flags
& _CUPS_WITH_REGEX
)
5629 * Value is an extended, case-sensitive POSIX regular expression...
5632 regex_t re
; /* Regular expression */
5634 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
5636 regerror(i
, &re
, temp
, sizeof(temp
));
5638 print_fatal_error("Unable to compile WITH-VALUE regular expression "
5639 "\"%s\" - %s", value
, temp
);
5644 * See if ALL of the values match the given regular expression.
5647 for (i
= 0; i
< attr
->num_values
; i
++)
5649 if (!regexec(&re
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5654 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5657 if (!(flags
& _CUPS_WITH_ALL
))
5663 else if (flags
& _CUPS_WITH_ALL
)
5675 * Value is a literal string, see if the value(s) match...
5678 for (i
= 0; i
< attr
->num_values
; i
++)
5680 if (!strcmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
))))
5684 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5687 if (!(flags
& _CUPS_WITH_ALL
))
5693 else if (flags
& _CUPS_WITH_ALL
)
5701 if (!match
&& errors
)
5703 for (i
= 0; i
< attr
->num_values
; i
++)
5704 add_stringf(errors
, "GOT: %s=\"%s\"", attr
->name
,
5705 attr
->values
[i
].string
.text
);