2 * ipptool command for CUPS.
4 * Copyright © 2007-2018 by Apple Inc.
5 * Copyright © 1997-2007 by Easy Software Products.
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
12 * Include necessary headers...
15 #include <cups/cups-private.h>
16 #include <cups/file-private.h>
30 #endif /* !O_BINARY */
37 typedef enum _cups_transfer_e
/**** How to send request data ****/
39 _CUPS_TRANSFER_AUTO
, /* Chunk for files, length for static */
40 _CUPS_TRANSFER_CHUNKED
, /* Chunk always */
41 _CUPS_TRANSFER_LENGTH
/* Length always */
44 typedef enum _cups_output_e
/**** Output mode ****/
46 _CUPS_OUTPUT_QUIET
, /* No output */
47 _CUPS_OUTPUT_TEST
, /* Traditional CUPS test output */
48 _CUPS_OUTPUT_PLIST
, /* XML plist test output */
49 _CUPS_OUTPUT_IPPSERVER
, /* ippserver attribute file output */
50 _CUPS_OUTPUT_LIST
, /* Tabular list output */
51 _CUPS_OUTPUT_CSV
/* Comma-separated values output */
54 typedef enum _cups_with_e
/**** WITH flags ****/
56 _CUPS_WITH_LITERAL
= 0, /* Match string is a literal value */
57 _CUPS_WITH_ALL
= 1, /* Must match all values */
58 _CUPS_WITH_REGEX
= 2, /* Match string is a regular expression */
59 _CUPS_WITH_HOSTNAME
= 4, /* Match string is a URI hostname */
60 _CUPS_WITH_RESOURCE
= 8, /* Match string is a URI resource */
61 _CUPS_WITH_SCHEME
= 16 /* Match string is a URI scheme */
64 typedef struct _cups_expect_s
/**** Expected attribute info ****/
66 int optional
, /* Optional attribute? */
67 not_expect
, /* Don't expect attribute? */
68 expect_all
; /* Expect all attributes to match/not match */
69 char *name
, /* Attribute name */
70 *of_type
, /* Type name */
71 *same_count_as
, /* Parallel attribute name */
72 *if_defined
, /* Only required if variable defined */
73 *if_not_defined
, /* Only required if variable is not defined */
74 *with_value
, /* Attribute must include this value */
75 *with_value_from
, /* Attribute must have one of the values in this attribute */
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_testdata_s
/**** Test Data ****/
103 http_encryption_t encryption
; /* Encryption for connection */
104 int family
; /* Address family */
105 _cups_output_t output
; /* Output mode */
106 int stop_after_include_error
;
107 /* Stop after include errors? */
108 double timeout
; /* Timeout for connection */
109 int validate_headers
, /* Validate HTTP headers in response? */
110 verbosity
; /* Show all attributes? */
113 int def_ignore_errors
; /* Default IGNORE-ERRORS value */
114 _cups_transfer_t def_transfer
; /* Default TRANSFER value */
115 int def_version
; /* Default IPP version */
118 http_t
*http
; /* HTTP connection to printer/server */
119 cups_file_t
*outfile
; /* Output file */
120 int show_header
, /* Show the test header? */
121 xml_header
; /* 1 if XML plist header was written */
122 int pass
, /* Have we passed all tests? */
123 test_count
, /* Number of tests (total) */
124 pass_count
, /* Number of tests that passed */
125 fail_count
, /* Number of tests that failed */
126 skip_count
; /* Number of tests that were skipped */
129 cups_array_t
*errors
; /* Errors array */
130 int prev_pass
, /* Result of previous test */
131 skip_previous
; /* Skip on previous test failure? */
132 char compression
[16]; /* COMPRESSION value */
133 useconds_t delay
; /* Initial delay */
134 int num_displayed
; /* Number of displayed attributes */
135 char *displayed
[200]; /* Displayed attributes */
136 int num_expects
; /* Number of expected attributes */
137 _cups_expect_t expects
[200], /* Expected attributes */
138 *expect
, /* Current expected attribute */
139 *last_expect
; /* Last EXPECT (for predicates) */
140 char file
[1024], /* Data filename */
141 file_id
[1024]; /* File identifier */
142 int ignore_errors
; /* Ignore test failures? */
143 char name
[1024]; /* Test name */
144 useconds_t repeat_interval
; /* Repeat interval (delay) */
145 int request_id
; /* Current request ID */
146 char resource
[512]; /* Resource for request */
147 int skip_test
, /* Skip this test? */
148 num_statuses
; /* Number of valid status codes */
149 _cups_status_t statuses
[100], /* Valid status codes */
150 *last_status
; /* Last STATUS (for predicates) */
151 char test_id
[1024]; /* Test identifier */
152 _cups_transfer_t transfer
; /* To chunk or not to chunk */
153 int version
; /* IPP version number to use */
161 static int Cancel
= 0; /* Cancel test? */
168 static void add_stringf(cups_array_t
*a
, const char *s
, ...) __attribute__ ((__format__ (__printf__
, 2, 3)));
169 static int compare_uris(const char *a
, const char *b
);
170 static int do_test(_ipp_file_t
*f
, _ipp_vars_t
*vars
, _cups_testdata_t
*data
);
171 static int do_tests(const char *testfile
, _ipp_vars_t
*vars
, _cups_testdata_t
*data
);
172 static int error_cb(_ipp_file_t
*f
, _cups_testdata_t
*data
, const char *error
);
173 static int expect_matches(_cups_expect_t
*expect
, ipp_tag_t value_tag
);
174 static char *get_filename(const char *testfile
, char *dst
, const char *src
, size_t dstsize
);
175 static const char *get_string(ipp_attribute_t
*attr
, int element
, int flags
, char *buffer
, size_t bufsize
);
176 static void init_data(_cups_testdata_t
*data
);
177 static char *iso_date(const ipp_uchar_t
*date
);
178 static void pause_message(const char *message
);
179 static void print_attr(cups_file_t
*outfile
, int output
, ipp_attribute_t
*attr
, ipp_tag_t
*group
);
180 static void print_csv(_cups_testdata_t
*data
, ipp_attribute_t
*attr
, int num_displayed
, char **displayed
, size_t *widths
);
181 static void print_fatal_error(_cups_testdata_t
*data
, const char *s
, ...) __attribute__ ((__format__ (__printf__
, 2, 3)));
182 static void print_ippserver_attr(_cups_testdata_t
*data
, ipp_attribute_t
*attr
, int indent
);
183 static void print_ippserver_string(_cups_testdata_t
*data
, const char *s
, size_t len
);
184 static void print_line(_cups_testdata_t
*data
, ipp_attribute_t
*attr
, int num_displayed
, char **displayed
, size_t *widths
);
185 static void print_xml_header(_cups_testdata_t
*data
);
186 static void print_xml_string(cups_file_t
*outfile
, const char *element
, const char *s
);
187 static void print_xml_trailer(_cups_testdata_t
*data
, int success
, const char *message
);
189 static void sigterm_handler(int sig
);
191 static int timeout_cb(http_t
*http
, void *user_data
);
192 static int token_cb(_ipp_file_t
*f
, _ipp_vars_t
*vars
, _cups_testdata_t
*data
, const char *token
);
193 static void usage(void) __attribute__((noreturn
));
194 static int validate_attr(_cups_testdata_t
*data
, cups_array_t
*errors
, ipp_attribute_t
*attr
);
195 static const char *with_flags_string(int flags
);
196 static int with_value(_cups_testdata_t
*data
, cups_array_t
*errors
, char *value
, int flags
, ipp_attribute_t
*attr
, char *matchbuf
, size_t matchlen
);
197 static int with_value_from(cups_array_t
*errors
, ipp_attribute_t
*fromattr
, ipp_attribute_t
*attr
, char *matchbuf
, size_t matchlen
);
201 * 'main()' - Parse options and do tests.
204 int /* O - Exit status */
205 main(int argc
, /* I - Number of command-line args */
206 char *argv
[]) /* I - Command-line arguments */
208 int i
; /* Looping var */
209 int status
; /* Status of tests... */
210 char *opt
, /* Current option */
211 name
[1024], /* Name/value buffer */
212 *value
, /* Pointer to value */
213 filename
[1024], /* Real filename */
214 testname
[1024]; /* Real test filename */
215 const char *ext
, /* Extension on filename */
216 *testfile
; /* Test file to use */
217 int interval
, /* Test interval in microseconds */
218 repeat
; /* Repeat count */
219 _cups_testdata_t data
; /* Test data */
220 _ipp_vars_t vars
; /* Variables */
221 _cups_globals_t
*cg
= _cupsGlobals();
227 * Catch SIGINT and SIGTERM...
230 signal(SIGINT
, sigterm_handler
);
231 signal(SIGTERM
, sigterm_handler
);
235 * Initialize the locale and variables...
238 _cupsSetLocale(argv
);
247 * ipptool URI testfile
255 for (i
= 1; i
< argc
; i
++)
257 if (!strcmp(argv
[i
], "--help"))
261 else if (!strcmp(argv
[i
], "--ippserver"))
267 _cupsLangPuts(stderr
, _("ipptool: Missing filename for \"--ippserver\"."));
271 if (data
.outfile
!= cupsFileStdout())
274 if ((data
.outfile
= cupsFileOpen(argv
[i
], "w")) == NULL
)
276 _cupsLangPrintf(stderr
, _("%s: Unable to open \"%s\": %s"), "ipptool", argv
[i
], strerror(errno
));
280 data
.output
= _CUPS_OUTPUT_IPPSERVER
;
282 else if (!strcmp(argv
[i
], "--stop-after-include-error"))
284 data
.stop_after_include_error
= 1;
286 else if (!strcmp(argv
[i
], "--version"))
291 else if (argv
[i
][0] == '-')
293 for (opt
= argv
[i
] + 1; *opt
; opt
++)
297 case '4' : /* Connect using IPv4 only */
298 data
.family
= AF_INET
;
302 case '6' : /* Connect using IPv6 only */
303 data
.family
= AF_INET6
;
305 #endif /* AF_INET6 */
307 case 'C' : /* Enable HTTP chunking */
308 data
.def_transfer
= _CUPS_TRANSFER_CHUNKED
;
311 case 'E' : /* Encrypt with TLS */
313 data
.encryption
= HTTP_ENCRYPT_REQUIRED
;
315 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
317 #endif /* HAVE_SSL */
320 case 'I' : /* Ignore errors */
321 data
.def_ignore_errors
= 1;
324 case 'L' : /* Disable HTTP chunking */
325 data
.def_transfer
= _CUPS_TRANSFER_LENGTH
;
328 case 'P' : /* Output to plist file */
333 _cupsLangPrintf(stderr
, _("%s: Missing filename for \"-P\"."), "ipptool");
337 if (data
.outfile
!= cupsFileStdout())
340 if ((data
.outfile
= cupsFileOpen(argv
[i
], "w")) == NULL
)
342 _cupsLangPrintf(stderr
, _("%s: Unable to open \"%s\": %s"), "ipptool", argv
[i
], strerror(errno
));
346 data
.output
= _CUPS_OUTPUT_PLIST
;
348 if (interval
|| repeat
)
350 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
355 case 'S' : /* Encrypt with SSL */
357 data
.encryption
= HTTP_ENCRYPT_ALWAYS
;
359 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
361 #endif /* HAVE_SSL */
364 case 'T' : /* Set timeout */
369 _cupsLangPrintf(stderr
,
370 _("%s: Missing timeout for \"-T\"."),
375 data
.timeout
= _cupsStrScand(argv
[i
], NULL
, localeconv());
378 case 'V' : /* Set IPP version */
383 _cupsLangPrintf(stderr
,
384 _("%s: Missing version for \"-V\"."),
389 if (!strcmp(argv
[i
], "1.0"))
391 data
.def_version
= 10;
393 else if (!strcmp(argv
[i
], "1.1"))
395 data
.def_version
= 11;
397 else if (!strcmp(argv
[i
], "2.0"))
399 data
.def_version
= 20;
401 else if (!strcmp(argv
[i
], "2.1"))
403 data
.def_version
= 21;
405 else if (!strcmp(argv
[i
], "2.2"))
407 data
.def_version
= 22;
411 _cupsLangPrintf(stderr
, _("%s: Bad version %s for \"-V\"."), "ipptool", argv
[i
]);
416 case 'X' : /* Produce XML output */
417 data
.output
= _CUPS_OUTPUT_PLIST
;
419 if (interval
|| repeat
)
421 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
426 case 'c' : /* CSV output */
427 data
.output
= _CUPS_OUTPUT_CSV
;
430 case 'd' : /* Define a variable */
435 _cupsLangPuts(stderr
,
436 _("ipptool: Missing name=value for \"-d\"."));
440 strlcpy(name
, argv
[i
], sizeof(name
));
441 if ((value
= strchr(name
, '=')) != NULL
)
444 value
= name
+ strlen(name
);
446 _ippVarsSet(&vars
, name
, value
);
449 case 'f' : /* Set the default test filename */
454 _cupsLangPuts(stderr
,
455 _("ipptool: Missing filename for \"-f\"."));
459 if (access(argv
[i
], 0))
465 snprintf(filename
, sizeof(filename
), "%s.gz", argv
[i
]);
466 if (access(filename
, 0) && filename
[0] != '/'
468 && (!isalpha(filename
[0] & 255) || filename
[1] != ':')
472 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s", cg
->cups_datadir
, argv
[i
]);
473 if (access(filename
, 0))
475 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s.gz", cg
->cups_datadir
, argv
[i
]);
476 if (access(filename
, 0))
477 strlcpy(filename
, argv
[i
], sizeof(filename
));
482 strlcpy(filename
, argv
[i
], sizeof(filename
));
484 _ippVarsSet(&vars
, "filename", filename
);
486 if ((ext
= strrchr(filename
, '.')) != NULL
)
489 * Guess the MIME media type based on the extension...
492 if (!_cups_strcasecmp(ext
, ".gif"))
493 _ippVarsSet(&vars
, "filetype", "image/gif");
494 else if (!_cups_strcasecmp(ext
, ".htm") ||
495 !_cups_strcasecmp(ext
, ".htm.gz") ||
496 !_cups_strcasecmp(ext
, ".html") ||
497 !_cups_strcasecmp(ext
, ".html.gz"))
498 _ippVarsSet(&vars
, "filetype", "text/html");
499 else if (!_cups_strcasecmp(ext
, ".jpg") ||
500 !_cups_strcasecmp(ext
, ".jpeg"))
501 _ippVarsSet(&vars
, "filetype", "image/jpeg");
502 else if (!_cups_strcasecmp(ext
, ".pcl") ||
503 !_cups_strcasecmp(ext
, ".pcl.gz"))
504 _ippVarsSet(&vars
, "filetype", "application/vnd.hp-PCL");
505 else if (!_cups_strcasecmp(ext
, ".pdf"))
506 _ippVarsSet(&vars
, "filetype", "application/pdf");
507 else if (!_cups_strcasecmp(ext
, ".png"))
508 _ippVarsSet(&vars
, "filetype", "image/png");
509 else if (!_cups_strcasecmp(ext
, ".ps") ||
510 !_cups_strcasecmp(ext
, ".ps.gz"))
511 _ippVarsSet(&vars
, "filetype", "application/postscript");
512 else if (!_cups_strcasecmp(ext
, ".pwg") ||
513 !_cups_strcasecmp(ext
, ".pwg.gz") ||
514 !_cups_strcasecmp(ext
, ".ras") ||
515 !_cups_strcasecmp(ext
, ".ras.gz"))
516 _ippVarsSet(&vars
, "filetype", "image/pwg-raster");
517 else if (!_cups_strcasecmp(ext
, ".tif") ||
518 !_cups_strcasecmp(ext
, ".tiff"))
519 _ippVarsSet(&vars
, "filetype", "image/tiff");
520 else if (!_cups_strcasecmp(ext
, ".txt") ||
521 !_cups_strcasecmp(ext
, ".txt.gz"))
522 _ippVarsSet(&vars
, "filetype", "text/plain");
523 else if (!_cups_strcasecmp(ext
, ".urf") ||
524 !_cups_strcasecmp(ext
, ".urf.gz"))
525 _ippVarsSet(&vars
, "filetype", "image/urf");
526 else if (!_cups_strcasecmp(ext
, ".xps"))
527 _ippVarsSet(&vars
, "filetype", "application/openxps");
529 _ippVarsSet(&vars
, "filetype", "application/octet-stream");
534 * Use the "auto-type" MIME media type...
537 _ippVarsSet(&vars
, "filetype", "application/octet-stream");
541 case 'h' : /* Validate response headers */
542 data
.validate_headers
= 1;
545 case 'i' : /* Test every N seconds */
550 _cupsLangPuts(stderr
, _("ipptool: Missing seconds for \"-i\"."));
555 interval
= (int)(_cupsStrScand(argv
[i
], NULL
, localeconv()) * 1000000.0);
558 _cupsLangPuts(stderr
, _("ipptool: Invalid seconds for \"-i\"."));
563 if ((data
.output
== _CUPS_OUTPUT_PLIST
|| data
.output
== _CUPS_OUTPUT_IPPSERVER
) && interval
)
565 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"--ippserver\", \"-P\", and \"-X\"."));
570 case 'l' : /* List as a table */
571 data
.output
= _CUPS_OUTPUT_LIST
;
574 case 'n' : /* Repeat count */
579 _cupsLangPuts(stderr
, _("ipptool: Missing count for \"-n\"."));
583 repeat
= atoi(argv
[i
]);
585 if ((data
.output
== _CUPS_OUTPUT_PLIST
|| data
.output
== _CUPS_OUTPUT_IPPSERVER
) && repeat
)
587 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"--ippserver\", \"-P\", and \"-X\"."));
592 case 'q' : /* Be quiet */
593 data
.output
= _CUPS_OUTPUT_QUIET
;
596 case 't' : /* CUPS test output */
597 data
.output
= _CUPS_OUTPUT_TEST
;
600 case 'v' : /* Be verbose */
605 _cupsLangPrintf(stderr
, _("%s: Unknown option \"-%c\"."), "ipptool", *opt
);
610 else if (!strncmp(argv
[i
], "ipp://", 6) || !strncmp(argv
[i
], "http://", 7)
612 || !strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8)
613 #endif /* HAVE_SSL */
622 _cupsLangPuts(stderr
, _("ipptool: May only specify a single URI."));
627 if (!strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8))
628 data
.encryption
= HTTP_ENCRYPT_ALWAYS
;
629 #endif /* HAVE_SSL */
631 if (!_ippVarsSet(&vars
, "uri", argv
[i
]))
633 _cupsLangPrintf(stderr
, _("ipptool: Bad URI \"%s\"."), argv
[i
]);
637 if (vars
.username
[0] && vars
.password
)
638 cupsSetPasswordCB2(_ippVarsPasswordCB
, &vars
);
648 _cupsLangPuts(stderr
, _("ipptool: URI required before test file."));
649 _cupsLangPuts(stderr
, argv
[i
]);
653 if (access(argv
[i
], 0) && argv
[i
][0] != '/'
655 && (!isalpha(argv
[i
][0] & 255) || argv
[i
][1] != ':')
659 snprintf(testname
, sizeof(testname
), "%s/ipptool/%s", cg
->cups_datadir
, argv
[i
]);
660 if (access(testname
, 0))
668 if (!do_tests(testfile
, &vars
, &data
))
673 if (!vars
.uri
|| !testfile
)
677 * Loop if the interval is set...
680 if (data
.output
== _CUPS_OUTPUT_PLIST
)
681 print_xml_trailer(&data
, !status
, NULL
);
682 else if (interval
> 0 && repeat
> 0)
686 usleep((useconds_t
)interval
);
687 do_tests(testfile
, &vars
, &data
);
691 else if (interval
> 0)
695 usleep((useconds_t
)interval
);
696 do_tests(testfile
, &vars
, &data
);
700 if ((data
.output
== _CUPS_OUTPUT_TEST
|| (data
.output
== _CUPS_OUTPUT_PLIST
&& data
.outfile
)) && data
.test_count
> 1)
703 * Show a summary report if there were multiple tests...
706 cupsFilePrintf(cupsFileStdout(), "\nSummary: %d tests, %d passed, %d failed, %d skipped\nScore: %d%%\n", data
.test_count
, data
.pass_count
, data
.fail_count
, data
.skip_count
, 100 * (data
.pass_count
+ data
.skip_count
) / data
.test_count
);
709 cupsFileClose(data
.outfile
);
720 * 'add_stringf()' - Add a formatted string to an array.
724 add_stringf(cups_array_t
*a
, /* I - Array */
725 const char *s
, /* I - Printf-style format string */
726 ...) /* I - Additional args as needed */
728 char buffer
[10240]; /* Format buffer */
729 va_list ap
; /* Argument pointer */
733 * Don't bother is the array is NULL...
740 * Format the message...
744 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
748 * Add it to the array...
751 cupsArrayAdd(a
, buffer
);
756 * 'compare_uris()' - Compare two URIs...
759 static int /* O - Result of comparison */
760 compare_uris(const char *a
, /* I - First URI */
761 const char *b
) /* I - Second URI */
763 char ascheme
[32], /* Components of first URI */
768 char bscheme
[32], /* Components of second URI */
773 char *ptr
; /* Pointer into string */
774 int result
; /* Result of comparison */
778 * Separate the URIs into their components...
781 if (httpSeparateURI(HTTP_URI_CODING_ALL
, a
, ascheme
, sizeof(ascheme
), auserpass
, sizeof(auserpass
), ahost
, sizeof(ahost
), &aport
, aresource
, sizeof(aresource
)) < HTTP_URI_STATUS_OK
)
784 if (httpSeparateURI(HTTP_URI_CODING_ALL
, b
, bscheme
, sizeof(bscheme
), buserpass
, sizeof(buserpass
), bhost
, sizeof(bhost
), &bport
, bresource
, sizeof(bresource
)) < HTTP_URI_STATUS_OK
)
788 * Strip trailing dots from the host components, if present...
791 if ((ptr
= ahost
+ strlen(ahost
) - 1) > ahost
&& *ptr
== '.')
794 if ((ptr
= bhost
+ strlen(bhost
) - 1) > bhost
&& *ptr
== '.')
798 * Compare each component...
801 if ((result
= _cups_strcasecmp(ascheme
, bscheme
)) != 0)
804 if ((result
= strcmp(auserpass
, buserpass
)) != 0)
807 if ((result
= _cups_strcasecmp(ahost
, bhost
)) != 0)
811 return (aport
- bport
);
813 if (!_cups_strcasecmp(ascheme
, "mailto") || !_cups_strcasecmp(ascheme
, "urn"))
814 return (_cups_strcasecmp(aresource
, bresource
));
816 return (strcmp(aresource
, bresource
));
821 * 'do_test()' - Do a single test from the test file.
824 static int /* O - 1 on success, 0 on failure */
825 do_test(_ipp_file_t
*f
, /* I - IPP data file */
826 _ipp_vars_t
*vars
, /* I - IPP variables */
827 _cups_testdata_t
*data
) /* I - Test data */
830 int i
, /* Looping var */
831 status_ok
, /* Did we get a matching status? */
832 repeat_count
= 0, /* Repeat count */
833 repeat_test
; /* Repeat the test? */
834 _cups_expect_t
*expect
; /* Current expected attribute */
835 ipp_t
*request
, /* IPP request */
836 *response
; /* IPP response */
837 size_t length
; /* Length of IPP request */
838 http_status_t status
; /* HTTP status */
839 cups_array_t
*a
; /* Duplicate attribute array */
840 ipp_tag_t group
; /* Current group */
841 ipp_attribute_t
*attrptr
, /* Attribute pointer */
842 *found
; /* Found attribute */
843 char temp
[1024]; /* Temporary string */
844 cups_file_t
*reqfile
; /* File to send */
845 ssize_t bytes
; /* Bytes read/written */
846 char buffer
[131072]; /* Copy buffer */
847 size_t widths
[200]; /* Width of columns */
848 const char *error
; /* Current error */
855 * Take over control of the attributes in the request...
862 * Submit the IPP request...
867 ippSetVersion(request
, data
->version
/ 10, data
->version
% 10);
868 ippSetRequestId(request
, data
->request_id
);
870 if (data
->output
== _CUPS_OUTPUT_PLIST
)
872 cupsFilePuts(data
->outfile
, "<dict>\n");
873 cupsFilePuts(data
->outfile
, "<key>Name</key>\n");
874 print_xml_string(data
->outfile
, "string", data
->name
);
875 if (data
->file_id
[0])
877 cupsFilePuts(data
->outfile
, "<key>FileId</key>\n");
878 print_xml_string(data
->outfile
, "string", data
->file_id
);
880 if (data
->test_id
[0])
882 cupsFilePuts(data
->outfile
, "<key>TestId</key>\n");
883 print_xml_string(data
->outfile
, "string", data
->test_id
);
885 cupsFilePuts(data
->outfile
, "<key>Version</key>\n");
886 cupsFilePrintf(data
->outfile
, "<string>%d.%d</string>\n", data
->version
/ 10, data
->version
% 10);
887 cupsFilePuts(data
->outfile
, "<key>Operation</key>\n");
888 print_xml_string(data
->outfile
, "string", ippOpString(ippGetOperation(request
)));
889 cupsFilePuts(data
->outfile
, "<key>RequestId</key>\n");
890 cupsFilePrintf(data
->outfile
, "<integer>%d</integer>\n", data
->request_id
);
891 cupsFilePuts(data
->outfile
, "<key>RequestAttributes</key>\n");
892 cupsFilePuts(data
->outfile
, "<array>\n");
895 cupsFilePuts(data
->outfile
, "<dict>\n");
896 for (attrptr
= ippFirstAttribute(request
), group
= ippGetGroupTag(attrptr
); attrptr
; attrptr
= ippNextAttribute(request
))
897 print_attr(data
->outfile
, data
->output
, attrptr
, &group
);
898 cupsFilePuts(data
->outfile
, "</dict>\n");
900 cupsFilePuts(data
->outfile
, "</array>\n");
903 if (data
->output
== _CUPS_OUTPUT_TEST
|| (data
->output
== _CUPS_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
907 cupsFilePrintf(cupsFileStdout(), " %s:\n", ippOpString(ippGetOperation(request
)));
909 for (attrptr
= ippFirstAttribute(request
); attrptr
; attrptr
= ippNextAttribute(request
))
910 print_attr(cupsFileStdout(), _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
913 cupsFilePrintf(cupsFileStdout(), " %-68.68s [", data
->name
);
916 if ((data
->skip_previous
&& !data
->prev_pass
) || data
->skip_test
)
924 if (data
->output
== _CUPS_OUTPUT_PLIST
)
926 cupsFilePuts(data
->outfile
, "<key>Successful</key>\n");
927 cupsFilePuts(data
->outfile
, "<true />\n");
928 cupsFilePuts(data
->outfile
, "<key>Skipped</key>\n");
929 cupsFilePuts(data
->outfile
, "<true />\n");
930 cupsFilePuts(data
->outfile
, "<key>StatusCode</key>\n");
931 print_xml_string(data
->outfile
, "string", "skip");
932 cupsFilePuts(data
->outfile
, "<key>ResponseAttributes</key>\n");
933 cupsFilePuts(data
->outfile
, "<dict />\n");
936 if (data
->output
== _CUPS_OUTPUT_TEST
|| (data
->output
== _CUPS_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
937 cupsFilePuts(cupsFileStdout(), "SKIP]\n");
942 vars
->password_tries
= 0;
949 data
->delay
= data
->repeat_interval
;
952 status
= HTTP_STATUS_OK
;
954 if (data
->transfer
== _CUPS_TRANSFER_CHUNKED
|| (data
->transfer
== _CUPS_TRANSFER_AUTO
&& data
->file
[0]))
957 * Send request using chunking - a 0 length means "chunk".
965 * Send request using content length...
968 length
= ippLength(request
);
970 if (data
->file
[0] && (reqfile
= cupsFileOpen(data
->file
, "r")) != NULL
)
973 * Read the file to get the uncompressed file size...
976 while ((bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
977 length
+= (size_t)bytes
;
979 cupsFileClose(reqfile
);
984 * Send the request...
991 if (status
!= HTTP_STATUS_ERROR
)
993 while (!response
&& !Cancel
&& data
->prev_pass
)
995 status
= cupsSendRequest(data
->http
, request
, data
->resource
, length
);
998 if (data
->compression
[0])
999 httpSetField(data
->http
, HTTP_FIELD_CONTENT_ENCODING
, data
->compression
);
1000 #endif /* HAVE_LIBZ */
1002 if (!Cancel
&& status
== HTTP_STATUS_CONTINUE
&& ippGetState(request
) == IPP_DATA
&& data
->file
[0])
1004 if ((reqfile
= cupsFileOpen(data
->file
, "r")) != NULL
)
1006 while (!Cancel
&& (bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
1008 if ((status
= cupsWriteRequestData(data
->http
, buffer
, (size_t)bytes
)) != HTTP_STATUS_CONTINUE
)
1012 cupsFileClose(reqfile
);
1016 snprintf(buffer
, sizeof(buffer
), "%s: %s", data
->file
, strerror(errno
));
1017 _cupsSetError(IPP_INTERNAL_ERROR
, buffer
, 0);
1019 status
= HTTP_STATUS_ERROR
;
1024 * Get the server's response...
1027 if (!Cancel
&& status
!= HTTP_STATUS_ERROR
)
1029 response
= cupsGetResponse(data
->http
, data
->resource
);
1030 status
= httpGetStatus(data
->http
);
1033 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& httpError(data
->http
) != EINVAL
&&
1035 httpError(data
->http
) != WSAETIMEDOUT
)
1037 httpError(data
->http
) != ETIMEDOUT
)
1040 if (httpReconnect2(data
->http
, 30000, NULL
))
1041 data
->prev_pass
= 0;
1043 else if (status
== HTTP_STATUS_ERROR
|| status
== HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED
)
1045 data
->prev_pass
= 0;
1048 else if (status
!= HTTP_STATUS_OK
)
1050 httpFlush(data
->http
);
1052 if (status
== HTTP_STATUS_UNAUTHORIZED
)
1060 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& httpError(data
->http
) != EINVAL
&&
1062 httpError(data
->http
) != WSAETIMEDOUT
)
1064 httpError(data
->http
) != ETIMEDOUT
)
1067 if (httpReconnect2(data
->http
, 30000, NULL
))
1068 data
->prev_pass
= 0;
1070 else if (status
== HTTP_STATUS_ERROR
)
1073 httpReconnect2(data
->http
, 30000, NULL
);
1075 data
->prev_pass
= 0;
1077 else if (status
!= HTTP_STATUS_OK
)
1079 httpFlush(data
->http
);
1080 data
->prev_pass
= 0;
1084 * Check results of request...
1087 cupsArrayClear(data
->errors
);
1089 if (httpGetVersion(data
->http
) != HTTP_1_1
)
1091 int version
= httpGetVersion(data
->http
);
1093 add_stringf(data
->errors
, "Bad HTTP version (%d.%d)", version
/ 100, version
% 100);
1096 if (data
->validate_headers
)
1098 const char *header
; /* HTTP header value */
1100 if ((header
= httpGetField(data
->http
, HTTP_FIELD_CONTENT_TYPE
)) == NULL
|| _cups_strcasecmp(header
, "application/ipp"))
1101 add_stringf(data
->errors
, "Bad HTTP Content-Type in response (%s)", header
&& *header
? header
: "<missing>");
1103 if ((header
= httpGetField(data
->http
, HTTP_FIELD_DATE
)) != NULL
&& *header
&& httpGetDateTime(header
) == 0)
1104 add_stringf(data
->errors
, "Bad HTTP Date in response (%s)", header
);
1110 * No response, log error...
1113 add_stringf(data
->errors
, "IPP request failed with status %s (%s)", ippErrorString(cupsLastError()), cupsLastErrorString());
1118 * Collect common attribute values...
1121 if ((attrptr
= ippFindAttribute(response
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
1123 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
1124 _ippVarsSet(vars
, "job-id", temp
);
1127 if ((attrptr
= ippFindAttribute(response
, "job-uri",
1128 IPP_TAG_URI
)) != NULL
)
1129 _ippVarsSet(vars
, "job-uri", attrptr
->values
[0].string
.text
);
1131 if ((attrptr
= ippFindAttribute(response
, "notify-subscription-id",
1132 IPP_TAG_INTEGER
)) != NULL
)
1134 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
1135 _ippVarsSet(vars
, "notify-subscription-id", temp
);
1139 * Check response, validating groups and attributes and logging errors
1143 if (ippGetState(response
) != IPP_DATA
)
1144 add_stringf(data
->errors
, "Missing end-of-attributes-tag in response (RFC 2910 section 3.5.1)");
1148 int major
, minor
; /* IPP version */
1150 major
= ippGetVersion(response
, &minor
);
1152 if (major
!= (data
->version
/ 10) || minor
!= (data
->version
% 10))
1153 add_stringf(data
->errors
, "Bad version %d.%d in response - expected %d.%d (RFC 2911 section 3.1.8).", major
, minor
, data
->version
/ 10, data
->version
% 10);
1156 if (ippGetRequestId(response
) != data
->request_id
)
1157 add_stringf(data
->errors
, "Bad request ID %d in response - expected %d (RFC 2911 section 3.1.1)", ippGetRequestId(response
), data
->request_id
);
1159 attrptr
= ippFirstAttribute(response
);
1162 add_stringf(data
->errors
, "Missing first attribute \"attributes-charset (charset)\" in group operation-attributes-tag (RFC 2911 section 3.1.4).");
1166 if (!ippGetName(attrptr
) || ippGetValueTag(attrptr
) != IPP_TAG_CHARSET
|| ippGetGroupTag(attrptr
) != IPP_TAG_OPERATION
|| ippGetCount(attrptr
) != 1 ||strcmp(ippGetName(attrptr
), "attributes-charset"))
1167 add_stringf(data
->errors
, "Bad first attribute \"%s (%s%s)\" in group %s, expected \"attributes-charset (charset)\" in group operation-attributes-tag (RFC 2911 section 3.1.4).", ippGetName(attrptr
) ? ippGetName(attrptr
) : "(null)", ippGetCount(attrptr
) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attrptr
)), ippTagString(ippGetGroupTag(attrptr
)));
1169 attrptr
= ippNextAttribute(response
);
1171 add_stringf(data
->errors
, "Missing second attribute \"attributes-natural-language (naturalLanguage)\" in group operation-attributes-tag (RFC 2911 section 3.1.4).");
1172 else if (!ippGetName(attrptr
) || ippGetValueTag(attrptr
) != IPP_TAG_LANGUAGE
|| ippGetGroupTag(attrptr
) != IPP_TAG_OPERATION
|| ippGetCount(attrptr
) != 1 || strcmp(ippGetName(attrptr
), "attributes-natural-language"))
1173 add_stringf(data
->errors
, "Bad first attribute \"%s (%s%s)\" in group %s, expected \"attributes-natural-language (naturalLanguage)\" in group operation-attributes-tag (RFC 2911 section 3.1.4).", ippGetName(attrptr
) ? ippGetName(attrptr
) : "(null)", ippGetCount(attrptr
) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attrptr
)), ippTagString(ippGetGroupTag(attrptr
)));
1176 if ((attrptr
= ippFindAttribute(response
, "status-message", IPP_TAG_ZERO
)) != NULL
)
1178 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
1179 add_stringf(data
->errors
, "status-message (text(255)) has wrong value tag %s (RFC 2911 section 3.1.6.2).", ippTagString(attrptr
->value_tag
));
1180 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
1181 add_stringf(data
->errors
, "status-message (text(255)) has wrong group tag %s (RFC 2911 section 3.1.6.2).", ippTagString(attrptr
->group_tag
));
1182 if (attrptr
->num_values
!= 1)
1183 add_stringf(data
->errors
, "status-message (text(255)) has %d values (RFC 2911 section 3.1.6.2).", attrptr
->num_values
);
1184 if (attrptr
->value_tag
== IPP_TAG_TEXT
&& strlen(attrptr
->values
[0].string
.text
) > 255)
1185 add_stringf(data
->errors
, "status-message (text(255)) has bad length %d (RFC 2911 section 3.1.6.2).", (int)strlen(attrptr
->values
[0].string
.text
));
1188 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
1189 IPP_TAG_ZERO
)) != NULL
)
1191 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
1192 add_stringf(data
->errors
,
1193 "detailed-status-message (text(MAX)) has wrong "
1194 "value tag %s (RFC 2911 section 3.1.6.3).",
1195 ippTagString(attrptr
->value_tag
));
1196 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
1197 add_stringf(data
->errors
,
1198 "detailed-status-message (text(MAX)) has wrong "
1199 "group tag %s (RFC 2911 section 3.1.6.3).",
1200 ippTagString(attrptr
->group_tag
));
1201 if (attrptr
->num_values
!= 1)
1202 add_stringf(data
->errors
,
1203 "detailed-status-message (text(MAX)) has %d values"
1204 " (RFC 2911 section 3.1.6.3).",
1205 attrptr
->num_values
);
1206 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
1207 strlen(attrptr
->values
[0].string
.text
) > 1023)
1208 add_stringf(data
->errors
,
1209 "detailed-status-message (text(MAX)) has bad "
1210 "length %d (RFC 2911 section 3.1.6.3).",
1211 (int)strlen(attrptr
->values
[0].string
.text
));
1214 a
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
1216 for (attrptr
= response
->attrs
,
1217 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
1219 attrptr
= attrptr
->next
)
1221 if (attrptr
->group_tag
!= group
)
1223 int out_of_order
= 0; /* Are attribute groups out-of-order? */
1226 switch (attrptr
->group_tag
)
1231 case IPP_TAG_OPERATION
:
1235 case IPP_TAG_UNSUPPORTED_GROUP
:
1236 if (group
!= IPP_TAG_OPERATION
)
1241 case IPP_TAG_PRINTER
:
1242 if (group
!= IPP_TAG_OPERATION
&&
1243 group
!= IPP_TAG_UNSUPPORTED_GROUP
)
1247 case IPP_TAG_SUBSCRIPTION
:
1248 if (group
> attrptr
->group_tag
&&
1249 group
!= IPP_TAG_DOCUMENT
)
1254 if (group
> attrptr
->group_tag
)
1260 add_stringf(data
->errors
, "Attribute groups out of order (%s < %s)",
1261 ippTagString(attrptr
->group_tag
),
1262 ippTagString(group
));
1264 if (attrptr
->group_tag
!= IPP_TAG_ZERO
)
1265 group
= attrptr
->group_tag
;
1268 validate_attr(data
, data
->errors
, attrptr
);
1272 if (cupsArrayFind(a
, attrptr
->name
))
1273 add_stringf(data
->errors
, "Duplicate \"%s\" attribute in %s group",
1274 attrptr
->name
, ippTagString(group
));
1276 cupsArrayAdd(a
, attrptr
->name
);
1283 * Now check the test-defined expected status-code and attribute
1287 for (i
= 0, status_ok
= 0; i
< data
->num_statuses
; i
++)
1289 if (data
->statuses
[i
].if_defined
&&
1290 !_ippVarsGet(vars
, data
->statuses
[i
].if_defined
))
1293 if (data
->statuses
[i
].if_not_defined
&&
1294 _ippVarsGet(vars
, data
->statuses
[i
].if_not_defined
))
1297 if (ippGetStatusCode(response
) == data
->statuses
[i
].status
)
1301 if (data
->statuses
[i
].repeat_match
&& repeat_count
< data
->statuses
[i
].repeat_limit
)
1304 if (data
->statuses
[i
].define_match
)
1305 _ippVarsSet(vars
, data
->statuses
[i
].define_match
, "1");
1309 if (data
->statuses
[i
].repeat_no_match
&& repeat_count
< data
->statuses
[i
].repeat_limit
)
1312 if (data
->statuses
[i
].define_no_match
)
1314 _ippVarsSet(vars
, data
->statuses
[i
].define_no_match
, "1");
1320 if (!status_ok
&& data
->num_statuses
> 0)
1322 for (i
= 0; i
< data
->num_statuses
; i
++)
1324 if (data
->statuses
[i
].if_defined
&&
1325 !_ippVarsGet(vars
, data
->statuses
[i
].if_defined
))
1328 if (data
->statuses
[i
].if_not_defined
&&
1329 _ippVarsGet(vars
, data
->statuses
[i
].if_not_defined
))
1332 if (!data
->statuses
[i
].repeat_match
|| repeat_count
>= data
->statuses
[i
].repeat_limit
)
1333 add_stringf(data
->errors
, "EXPECTED: STATUS %s (got %s)",
1334 ippErrorString(data
->statuses
[i
].status
),
1335 ippErrorString(cupsLastError()));
1338 if ((attrptr
= ippFindAttribute(response
, "status-message",
1339 IPP_TAG_TEXT
)) != NULL
)
1340 add_stringf(data
->errors
, "status-message=\"%s\"",
1341 attrptr
->values
[0].string
.text
);
1344 for (i
= data
->num_expects
, expect
= data
->expects
; i
> 0; i
--, expect
++)
1346 if (expect
->if_defined
&& !_ippVarsGet(vars
, expect
->if_defined
))
1349 if (expect
->if_not_defined
&&
1350 _ippVarsGet(vars
, expect
->if_not_defined
))
1353 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
1357 if ((found
&& expect
->not_expect
) ||
1358 (!found
&& !(expect
->not_expect
|| expect
->optional
)) ||
1359 (found
&& !expect_matches(expect
, found
->value_tag
)) ||
1360 (found
&& expect
->in_group
&&
1361 found
->group_tag
!= expect
->in_group
))
1363 if (expect
->define_no_match
)
1364 _ippVarsSet(vars
, expect
->define_no_match
, "1");
1365 else if (!expect
->define_match
&& !expect
->define_value
)
1367 if (found
&& expect
->not_expect
)
1368 add_stringf(data
->errors
, "NOT EXPECTED: %s", expect
->name
);
1369 else if (!found
&& !(expect
->not_expect
|| expect
->optional
))
1370 add_stringf(data
->errors
, "EXPECTED: %s", expect
->name
);
1373 if (!expect_matches(expect
, found
->value_tag
))
1374 add_stringf(data
->errors
, "EXPECTED: %s OF-TYPE %s (got %s)",
1375 expect
->name
, expect
->of_type
,
1376 ippTagString(found
->value_tag
));
1378 if (expect
->in_group
&& found
->group_tag
!= expect
->in_group
)
1379 add_stringf(data
->errors
, "EXPECTED: %s IN-GROUP %s (got %s).",
1380 expect
->name
, ippTagString(expect
->in_group
),
1381 ippTagString(found
->group_tag
));
1385 if (expect
->repeat_no_match
&& repeat_count
< expect
->repeat_limit
)
1392 ippAttributeString(found
, buffer
, sizeof(buffer
));
1394 if (found
&& expect
->with_value_from
&& !with_value_from(NULL
, ippFindAttribute(response
, expect
->with_value_from
, IPP_TAG_ZERO
), found
, buffer
, sizeof(buffer
)))
1396 if (expect
->define_no_match
)
1397 _ippVarsSet(vars
, expect
->define_no_match
, "1");
1398 else if (!expect
->define_match
&& !expect
->define_value
&& ((!expect
->repeat_match
&& !expect
->repeat_no_match
) || repeat_count
>= expect
->repeat_limit
))
1400 add_stringf(data
->errors
, "EXPECTED: %s WITH-VALUES-FROM %s", expect
->name
, expect
->with_value_from
);
1402 with_value_from(data
->errors
, ippFindAttribute(response
, expect
->with_value_from
, IPP_TAG_ZERO
), found
, buffer
, sizeof(buffer
));
1405 if (expect
->repeat_no_match
&& repeat_count
< expect
->repeat_limit
)
1410 else if (found
&& !with_value(data
, NULL
, expect
->with_value
, expect
->with_flags
, found
, buffer
, sizeof(buffer
)))
1412 if (expect
->define_no_match
)
1413 _ippVarsSet(vars
, expect
->define_no_match
, "1");
1414 else if (!expect
->define_match
&& !expect
->define_value
&&
1415 !expect
->repeat_match
&& (!expect
->repeat_no_match
|| repeat_count
>= expect
->repeat_limit
))
1417 if (expect
->with_flags
& _CUPS_WITH_REGEX
)
1418 add_stringf(data
->errors
, "EXPECTED: %s %s /%s/", expect
->name
, with_flags_string(expect
->with_flags
), expect
->with_value
);
1420 add_stringf(data
->errors
, "EXPECTED: %s %s \"%s\"", expect
->name
, with_flags_string(expect
->with_flags
), expect
->with_value
);
1422 with_value(data
, data
->errors
, expect
->with_value
, expect
->with_flags
, found
, buffer
, sizeof(buffer
));
1425 if (expect
->repeat_no_match
&&
1426 repeat_count
< expect
->repeat_limit
)
1432 if (found
&& expect
->count
> 0 &&
1433 found
->num_values
!= expect
->count
)
1435 if (expect
->define_no_match
)
1436 _ippVarsSet(vars
, expect
->define_no_match
, "1");
1437 else if (!expect
->define_match
&& !expect
->define_value
)
1439 add_stringf(data
->errors
, "EXPECTED: %s COUNT %d (got %d)", expect
->name
,
1440 expect
->count
, found
->num_values
);
1443 if (expect
->repeat_no_match
&&
1444 repeat_count
< expect
->repeat_limit
)
1450 if (found
&& expect
->same_count_as
)
1452 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
1455 if (!attrptr
|| attrptr
->num_values
!= found
->num_values
)
1457 if (expect
->define_no_match
)
1458 _ippVarsSet(vars
, expect
->define_no_match
, "1");
1459 else if (!expect
->define_match
&& !expect
->define_value
)
1462 add_stringf(data
->errors
,
1463 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
1464 "(not returned)", expect
->name
,
1465 found
->num_values
, expect
->same_count_as
);
1466 else if (attrptr
->num_values
!= found
->num_values
)
1467 add_stringf(data
->errors
,
1468 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
1469 "(%d values)", expect
->name
, found
->num_values
,
1470 expect
->same_count_as
, attrptr
->num_values
);
1473 if (expect
->repeat_no_match
&&
1474 repeat_count
< expect
->repeat_limit
)
1481 if (found
&& expect
->define_match
)
1482 _ippVarsSet(vars
, expect
->define_match
, "1");
1484 if (found
&& expect
->define_value
)
1486 if (!expect
->with_value
)
1488 int last
= ippGetCount(found
) - 1;
1489 /* Last element in attribute */
1491 switch (ippGetValueTag(found
))
1494 case IPP_TAG_INTEGER
:
1495 snprintf(buffer
, sizeof(buffer
), "%d", ippGetInteger(found
, last
));
1498 case IPP_TAG_BOOLEAN
:
1499 if (ippGetBoolean(found
, last
))
1500 strlcpy(buffer
, "true", sizeof(buffer
));
1502 strlcpy(buffer
, "false", sizeof(buffer
));
1505 case IPP_TAG_RESOLUTION
:
1507 int xres
, /* Horizontal resolution */
1508 yres
; /* Vertical resolution */
1509 ipp_res_t units
; /* Resolution units */
1511 xres
= ippGetResolution(found
, last
, &yres
, &units
);
1514 snprintf(buffer
, sizeof(buffer
), "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
1516 snprintf(buffer
, sizeof(buffer
), "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
1520 case IPP_TAG_CHARSET
:
1521 case IPP_TAG_KEYWORD
:
1522 case IPP_TAG_LANGUAGE
:
1523 case IPP_TAG_MIMETYPE
:
1525 case IPP_TAG_NAMELANG
:
1527 case IPP_TAG_TEXTLANG
:
1529 case IPP_TAG_URISCHEME
:
1530 strlcpy(buffer
, ippGetString(found
, last
, NULL
), sizeof(buffer
));
1534 ippAttributeString(found
, buffer
, sizeof(buffer
));
1539 _ippVarsSet(vars
, expect
->define_value
, buffer
);
1542 if (found
&& expect
->repeat_match
&&
1543 repeat_count
< expect
->repeat_limit
)
1546 while (expect
->expect_all
&& (found
= ippFindNextAttribute(response
, expect
->name
, IPP_TAG_ZERO
)) != NULL
);
1551 * If we are going to repeat this test, display intermediate results...
1556 if (data
->output
== _CUPS_OUTPUT_TEST
|| (data
->output
== _CUPS_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
1558 cupsFilePrintf(cupsFileStdout(), "%04d]\n", repeat_count
);
1560 if (data
->num_displayed
> 0)
1562 for (attrptr
= ippFirstAttribute(response
); attrptr
; attrptr
= ippNextAttribute(response
))
1564 const char *attrname
= ippGetName(attrptr
);
1567 for (i
= 0; i
< data
->num_displayed
; i
++)
1569 if (!strcmp(data
->displayed
[i
], attrname
))
1571 print_attr(cupsFileStdout(), _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
1580 if (data
->output
== _CUPS_OUTPUT_TEST
|| (data
->output
== _CUPS_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
1582 cupsFilePrintf(cupsFileStdout(), " %-68.68s [", data
->name
);
1585 ippDelete(response
);
1589 while (repeat_test
);
1595 if (cupsArrayCount(data
->errors
) > 0)
1596 data
->prev_pass
= data
->pass
= 0;
1598 if (data
->prev_pass
)
1599 data
->pass_count
++;
1601 data
->fail_count
++;
1603 if (data
->output
== _CUPS_OUTPUT_PLIST
)
1605 cupsFilePuts(data
->outfile
, "<key>Successful</key>\n");
1606 cupsFilePuts(data
->outfile
, data
->prev_pass
? "<true />\n" : "<false />\n");
1607 cupsFilePuts(data
->outfile
, "<key>StatusCode</key>\n");
1608 print_xml_string(data
->outfile
, "string", ippErrorString(cupsLastError()));
1609 cupsFilePuts(data
->outfile
, "<key>ResponseAttributes</key>\n");
1610 cupsFilePuts(data
->outfile
, "<array>\n");
1611 cupsFilePuts(data
->outfile
, "<dict>\n");
1612 for (attrptr
= response
? response
->attrs
: NULL
,
1613 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
1615 attrptr
= attrptr
->next
)
1616 print_attr(data
->outfile
, data
->output
, attrptr
, &group
);
1617 cupsFilePuts(data
->outfile
, "</dict>\n");
1618 cupsFilePuts(data
->outfile
, "</array>\n");
1620 else if (data
->output
== _CUPS_OUTPUT_IPPSERVER
&& response
)
1622 for (attrptr
= ippFirstAttribute(response
), group
= IPP_TAG_ZERO
; attrptr
; attrptr
= ippNextAttribute(response
))
1624 if (!ippGetName(attrptr
) || ippGetGroupTag(attrptr
) != IPP_TAG_PRINTER
)
1627 print_ippserver_attr(data
, attrptr
, 0);
1631 if (data
->output
== _CUPS_OUTPUT_TEST
|| (data
->output
== _CUPS_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
1633 cupsFilePuts(cupsFileStdout(), data
->prev_pass
? "PASS]\n" : "FAIL]\n");
1635 if (!data
->prev_pass
|| (data
->verbosity
&& response
))
1637 cupsFilePrintf(cupsFileStdout(), " RECEIVED: %lu bytes in response\n", (unsigned long)ippLength(response
));
1638 cupsFilePrintf(cupsFileStdout(), " status-code = %s (%s)\n", ippErrorString(cupsLastError()), cupsLastErrorString());
1640 if (data
->verbosity
&& response
)
1642 for (attrptr
= response
->attrs
;
1644 attrptr
= attrptr
->next
)
1645 print_attr(cupsFileStdout(), _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
1649 else if (!data
->prev_pass
&& data
->output
!= _CUPS_OUTPUT_QUIET
)
1650 fprintf(stderr
, "%s\n", cupsLastErrorString());
1652 if (data
->prev_pass
&& data
->output
>= _CUPS_OUTPUT_LIST
&& !data
->verbosity
&& data
->num_displayed
> 0)
1654 size_t width
; /* Length of value */
1656 for (i
= 0; i
< data
->num_displayed
; i
++)
1658 widths
[i
] = strlen(data
->displayed
[i
]);
1660 for (attrptr
= ippFindAttribute(response
, data
->displayed
[i
], IPP_TAG_ZERO
);
1662 attrptr
= ippFindNextAttribute(response
, data
->displayed
[i
], IPP_TAG_ZERO
))
1664 width
= ippAttributeString(attrptr
, NULL
, 0);
1665 if (width
> widths
[i
])
1670 if (data
->output
== _CUPS_OUTPUT_CSV
)
1671 print_csv(data
, NULL
, data
->num_displayed
, data
->displayed
, widths
);
1673 print_line(data
, NULL
, data
->num_displayed
, data
->displayed
, widths
);
1675 attrptr
= response
->attrs
;
1679 while (attrptr
&& attrptr
->group_tag
<= IPP_TAG_OPERATION
)
1680 attrptr
= attrptr
->next
;
1684 if (data
->output
== _CUPS_OUTPUT_CSV
)
1685 print_csv(data
, attrptr
, data
->num_displayed
, data
->displayed
, widths
);
1687 print_line(data
, attrptr
, data
->num_displayed
, data
->displayed
, widths
);
1689 while (attrptr
&& attrptr
->group_tag
> IPP_TAG_OPERATION
)
1690 attrptr
= attrptr
->next
;
1694 else if (!data
->prev_pass
)
1696 if (data
->output
== _CUPS_OUTPUT_PLIST
)
1698 cupsFilePuts(data
->outfile
, "<key>Errors</key>\n");
1699 cupsFilePuts(data
->outfile
, "<array>\n");
1701 for (error
= (char *)cupsArrayFirst(data
->errors
);
1703 error
= (char *)cupsArrayNext(data
->errors
))
1704 print_xml_string(data
->outfile
, "string", error
);
1706 cupsFilePuts(data
->outfile
, "</array>\n");
1709 if (data
->output
== _CUPS_OUTPUT_TEST
|| (data
->output
== _CUPS_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
1711 for (error
= (char *)cupsArrayFirst(data
->errors
);
1713 error
= (char *)cupsArrayNext(data
->errors
))
1714 cupsFilePrintf(cupsFileStdout(), " %s\n", error
);
1718 if (data
->num_displayed
> 0 && !data
->verbosity
&& response
&& (data
->output
== _CUPS_OUTPUT_TEST
|| (data
->output
== _CUPS_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout())))
1720 for (attrptr
= response
->attrs
;
1722 attrptr
= attrptr
->next
)
1726 for (i
= 0; i
< data
->num_displayed
; i
++)
1728 if (!strcmp(data
->displayed
[i
], attrptr
->name
))
1730 print_attr(data
->outfile
, data
->output
, attrptr
, NULL
);
1740 if (data
->output
== _CUPS_OUTPUT_PLIST
)
1741 cupsFilePuts(data
->outfile
, "</dict>\n");
1743 ippDelete(response
);
1746 for (i
= 0; i
< data
->num_statuses
; i
++)
1748 if (data
->statuses
[i
].if_defined
)
1749 free(data
->statuses
[i
].if_defined
);
1750 if (data
->statuses
[i
].if_not_defined
)
1751 free(data
->statuses
[i
].if_not_defined
);
1752 if (data
->statuses
[i
].define_match
)
1753 free(data
->statuses
[i
].define_match
);
1754 if (data
->statuses
[i
].define_no_match
)
1755 free(data
->statuses
[i
].define_no_match
);
1757 data
->num_statuses
= 0;
1759 for (i
= data
->num_expects
, expect
= data
->expects
; i
> 0; i
--, expect
++)
1762 if (expect
->of_type
)
1763 free(expect
->of_type
);
1764 if (expect
->same_count_as
)
1765 free(expect
->same_count_as
);
1766 if (expect
->if_defined
)
1767 free(expect
->if_defined
);
1768 if (expect
->if_not_defined
)
1769 free(expect
->if_not_defined
);
1770 if (expect
->with_value
)
1771 free(expect
->with_value
);
1772 if (expect
->define_match
)
1773 free(expect
->define_match
);
1774 if (expect
->define_no_match
)
1775 free(expect
->define_no_match
);
1776 if (expect
->define_value
)
1777 free(expect
->define_value
);
1779 data
->num_expects
= 0;
1781 for (i
= 0; i
< data
->num_displayed
; i
++)
1782 free(data
->displayed
[i
]);
1783 data
->num_displayed
= 0;
1785 return (data
->ignore_errors
|| data
->prev_pass
);
1790 * 'do_tests()' - Do tests as specified in the test file.
1793 static int /* O - 1 on success, 0 on failure */
1794 do_tests(const char *testfile
, /* I - Test file to use */
1795 _ipp_vars_t
*vars
, /* I - Variables */
1796 _cups_testdata_t
*data
) /* I - Test data */
1798 http_encryption_t encryption
; /* Encryption mode */
1802 * Connect to the printer/server...
1805 if (!_cups_strcasecmp(vars
->scheme
, "https") || !_cups_strcasecmp(vars
->scheme
, "ipps"))
1806 encryption
= HTTP_ENCRYPTION_ALWAYS
;
1808 encryption
= data
->encryption
;
1810 if ((data
->http
= httpConnect2(vars
->host
, vars
->port
, NULL
, data
->family
, encryption
, 1, 30000, NULL
)) == NULL
)
1812 print_fatal_error(data
, "Unable to connect to \"%s\" on port %d - %s", vars
->host
, vars
->port
, cupsLastErrorString());
1817 httpSetDefaultField(data
->http
, HTTP_FIELD_ACCEPT_ENCODING
, "deflate, gzip, identity");
1819 httpSetDefaultField(data
->http
, HTTP_FIELD_ACCEPT_ENCODING
, "identity");
1820 #endif /* HAVE_LIBZ */
1822 if (data
->timeout
> 0.0)
1823 httpSetTimeout(data
->http
, data
->timeout
, timeout_cb
, NULL
);
1829 _ippFileParse(vars
, testfile
, (_ipp_ftoken_cb_t
)token_cb
, (_ipp_ferror_cb_t
)error_cb
, (void *)data
);
1832 * Close connection and return...
1835 httpClose(data
->http
);
1838 return (data
->pass
);
1843 * 'error_cb()' - Print/add an error message.
1846 static int /* O - 1 to continue, 0 to stop */
1847 error_cb(_ipp_file_t
*f
, /* I - IPP file data */
1848 _cups_testdata_t
*data
, /* I - Test data */
1849 const char *error
) /* I - Error message */
1853 print_fatal_error(data
, "%s", error
);
1860 * 'expect_matches()' - Return true if the tag matches the specification.
1863 static int /* O - 1 if matches, 0 otherwise */
1865 _cups_expect_t
*expect
, /* I - Expected attribute */
1866 ipp_tag_t value_tag
) /* I - Value tag for attribute */
1868 int match
; /* Match? */
1869 char *of_type
, /* Type name to match */
1870 *next
, /* Next name to match */
1871 sep
; /* Separator character */
1875 * If we don't expect a particular type, return immediately...
1878 if (!expect
->of_type
)
1882 * Parse the "of_type" value since the string can contain multiple attribute
1883 * types separated by "," or "|"...
1886 for (of_type
= expect
->of_type
, match
= 0; !match
&& *of_type
; of_type
= next
)
1889 * Find the next separator, and set it (temporarily) to nul if present.
1892 for (next
= of_type
; *next
&& *next
!= '|' && *next
!= ','; next
++);
1894 if ((sep
= *next
) != '\0')
1898 * Support some meta-types to make it easier to write the test file.
1901 if (!strcmp(of_type
, "text"))
1902 match
= value_tag
== IPP_TAG_TEXTLANG
|| value_tag
== IPP_TAG_TEXT
;
1903 else if (!strcmp(of_type
, "name"))
1904 match
= value_tag
== IPP_TAG_NAMELANG
|| value_tag
== IPP_TAG_NAME
;
1905 else if (!strcmp(of_type
, "collection"))
1906 match
= value_tag
== IPP_TAG_BEGIN_COLLECTION
;
1908 match
= value_tag
== ippTagValue(of_type
);
1911 * Restore the separator if we have one...
1923 * 'get_filename()' - Get a filename based on the current test file.
1926 static char * /* O - Filename */
1927 get_filename(const char *testfile
, /* I - Current test file */
1928 char *dst
, /* I - Destination filename */
1929 const char *src
, /* I - Source filename */
1930 size_t dstsize
) /* I - Size of destination buffer */
1932 char *dstptr
; /* Pointer into destination */
1933 _cups_globals_t
*cg
= _cupsGlobals();
1937 if (*src
== '<' && src
[strlen(src
) - 1] == '>')
1940 * Map <filename> to CUPS_DATADIR/ipptool/filename...
1943 snprintf(dst
, dstsize
, "%s/ipptool/%s", cg
->cups_datadir
, src
+ 1);
1944 dstptr
= dst
+ strlen(dst
) - 1;
1948 else if (!access(src
, R_OK
) || *src
== '/'
1950 || (isalpha(*src
& 255) && src
[1] == ':')
1955 * Use the path as-is...
1958 strlcpy(dst
, src
, dstsize
);
1963 * Make path relative to testfile...
1966 strlcpy(dst
, testfile
, dstsize
);
1967 if ((dstptr
= strrchr(dst
, '/')) != NULL
)
1970 dstptr
= dst
; /* Should never happen */
1972 strlcpy(dstptr
, src
, dstsize
- (size_t)(dstptr
- dst
));
1980 * 'get_string()' - Get a pointer to a string value or the portion of interest.
1983 static const char * /* O - Pointer to string */
1984 get_string(ipp_attribute_t
*attr
, /* I - IPP attribute */
1985 int element
, /* I - Element to fetch */
1986 int flags
, /* I - Value ("with") flags */
1987 char *buffer
, /* I - Temporary buffer */
1988 size_t bufsize
) /* I - Size of temporary buffer */
1990 const char *value
; /* Value */
1991 char *ptr
, /* Pointer into value */
1992 scheme
[256], /* URI scheme */
1993 userpass
[256], /* Username/password */
1994 hostname
[256], /* Hostname */
1995 resource
[1024]; /* Resource */
1996 int port
; /* Port number */
1999 value
= ippGetString(attr
, element
, NULL
);
2001 if (flags
& _CUPS_WITH_HOSTNAME
)
2003 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), buffer
, (int)bufsize
, &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
2006 ptr
= buffer
+ strlen(buffer
) - 1;
2007 if (ptr
>= buffer
&& *ptr
== '.')
2008 *ptr
= '\0'; /* Drop trailing "." */
2012 else if (flags
& _CUPS_WITH_RESOURCE
)
2014 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, buffer
, (int)bufsize
) < HTTP_URI_STATUS_OK
)
2019 else if (flags
& _CUPS_WITH_SCHEME
)
2021 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, buffer
, (int)bufsize
, userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
2026 else if (ippGetValueTag(attr
) == IPP_TAG_URI
&& (!strncmp(value
, "ipp://", 6) || !strncmp(value
, "http://", 7) || !strncmp(value
, "ipps://", 7) || !strncmp(value
, "https://", 8)))
2028 http_uri_status_t status
= httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
));
2030 if (status
< HTTP_URI_STATUS_OK
)
2041 * Normalize URI with no trailing dot...
2044 if ((ptr
= hostname
+ strlen(hostname
) - 1) >= hostname
&& *ptr
== '.')
2047 httpAssembleURI(HTTP_URI_CODING_ALL
, buffer
, (int)bufsize
, scheme
, userpass
, hostname
, port
, resource
);
2058 * 'init_data()' - Initialize test data.
2062 init_data(_cups_testdata_t
*data
) /* I - Data */
2064 memset(data
, 0, sizeof(_cups_testdata_t
));
2066 data
->output
= _CUPS_OUTPUT_LIST
;
2067 data
->outfile
= cupsFileStdout();
2068 data
->family
= AF_UNSPEC
;
2069 data
->def_transfer
= _CUPS_TRANSFER_AUTO
;
2070 data
->def_version
= 11;
2071 data
->errors
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
, (cups_afree_func_t
)free
);
2073 data
->prev_pass
= 1;
2074 data
->request_id
= (CUPS_RAND() % 1000) * 137 + 1;
2075 data
->show_header
= 1;
2080 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
2084 static char * /* O - ISO 8601 date/time string */
2085 iso_date(const ipp_uchar_t
*date
) /* I - IPP (RFC 1903) date/time value */
2087 time_t utctime
; /* UTC time since 1970 */
2088 struct tm
*utcdate
; /* UTC date/time */
2089 static char buffer
[255]; /* String buffer */
2092 utctime
= ippDateToTime(date
);
2093 utcdate
= gmtime(&utctime
);
2095 snprintf(buffer
, sizeof(buffer
), "%04d-%02d-%02dT%02d:%02d:%02dZ",
2096 utcdate
->tm_year
+ 1900, utcdate
->tm_mon
+ 1, utcdate
->tm_mday
,
2097 utcdate
->tm_hour
, utcdate
->tm_min
, utcdate
->tm_sec
);
2104 * 'pause_message()' - Display the message and pause until the user presses a key.
2108 pause_message(const char *message
) /* I - Message */
2111 HANDLE tty
; /* Console handle */
2112 DWORD mode
; /* Console mode */
2113 char key
; /* Key press */
2114 DWORD bytes
; /* Bytes read for key press */
2118 * Disable input echo and set raw input...
2121 if ((tty
= GetStdHandle(STD_INPUT_HANDLE
)) == INVALID_HANDLE_VALUE
)
2124 if (!GetConsoleMode(tty
, &mode
))
2127 if (!SetConsoleMode(tty
, 0))
2131 int tty
; /* /dev/tty - never read from stdin */
2132 struct termios original
, /* Original input mode */
2133 noecho
; /* No echo input mode */
2134 char key
; /* Current key press */
2138 * Disable input echo and set raw input...
2141 if ((tty
= open("/dev/tty", O_RDONLY
)) < 0)
2144 if (tcgetattr(tty
, &original
))
2151 noecho
.c_lflag
&= (tcflag_t
)~(ICANON
| ECHO
| ECHOE
| ISIG
);
2153 if (tcsetattr(tty
, TCSAFLUSH
, &noecho
))
2161 * Display the prompt...
2164 cupsFilePrintf(cupsFileStdout(), "%s\n---- PRESS ANY KEY ----", message
);
2171 ReadFile(tty
, &key
, 1, &bytes
, NULL
);
2177 SetConsoleMode(tty
, mode
);
2190 tcsetattr(tty
, TCSAFLUSH
, &original
);
2195 * Erase the "press any key" prompt...
2198 cupsFilePuts(cupsFileStdout(), "\r \r");
2203 * 'print_attr()' - Print an attribute on the screen.
2207 print_attr(cups_file_t
*outfile
, /* I - Output file */
2208 int output
, /* I - Output format */
2209 ipp_attribute_t
*attr
, /* I - Attribute to print */
2210 ipp_tag_t
*group
) /* IO - Current group */
2212 int i
; /* Looping var */
2213 ipp_attribute_t
*colattr
; /* Collection attribute */
2216 if (output
== _CUPS_OUTPUT_PLIST
)
2218 if (!attr
->name
|| (group
&& *group
!= attr
->group_tag
))
2220 if (attr
->group_tag
!= IPP_TAG_ZERO
)
2222 cupsFilePuts(outfile
, "</dict>\n");
2223 cupsFilePuts(outfile
, "<dict>\n");
2227 *group
= attr
->group_tag
;
2233 print_xml_string(outfile
, "key", attr
->name
);
2234 if (attr
->num_values
> 1)
2235 cupsFilePuts(outfile
, "<array>\n");
2237 switch (attr
->value_tag
)
2239 case IPP_TAG_INTEGER
:
2241 for (i
= 0; i
< attr
->num_values
; i
++)
2242 cupsFilePrintf(outfile
, "<integer>%d</integer>\n", attr
->values
[i
].integer
);
2245 case IPP_TAG_BOOLEAN
:
2246 for (i
= 0; i
< attr
->num_values
; i
++)
2247 cupsFilePuts(outfile
, attr
->values
[i
].boolean
? "<true />\n" : "<false />\n");
2250 case IPP_TAG_RANGE
:
2251 for (i
= 0; i
< attr
->num_values
; i
++)
2252 cupsFilePrintf(outfile
, "<dict><key>lower</key><integer>%d</integer>"
2253 "<key>upper</key><integer>%d</integer></dict>\n",
2254 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
2257 case IPP_TAG_RESOLUTION
:
2258 for (i
= 0; i
< attr
->num_values
; i
++)
2259 cupsFilePrintf(outfile
, "<dict><key>xres</key><integer>%d</integer>"
2260 "<key>yres</key><integer>%d</integer>"
2261 "<key>units</key><string>%s</string></dict>\n",
2262 attr
->values
[i
].resolution
.xres
,
2263 attr
->values
[i
].resolution
.yres
,
2264 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
2269 for (i
= 0; i
< attr
->num_values
; i
++)
2270 cupsFilePrintf(outfile
, "<date>%s</date>\n", iso_date(attr
->values
[i
].date
));
2273 case IPP_TAG_STRING
:
2274 for (i
= 0; i
< attr
->num_values
; i
++)
2276 char buffer
[IPP_MAX_LENGTH
* 5 / 4 + 1];
2279 cupsFilePrintf(outfile
, "<data>%s</data>\n",
2280 httpEncode64_2(buffer
, sizeof(buffer
),
2281 attr
->values
[i
].unknown
.data
,
2282 attr
->values
[i
].unknown
.length
));
2288 case IPP_TAG_KEYWORD
:
2290 case IPP_TAG_URISCHEME
:
2291 case IPP_TAG_CHARSET
:
2292 case IPP_TAG_LANGUAGE
:
2293 case IPP_TAG_MIMETYPE
:
2294 for (i
= 0; i
< attr
->num_values
; i
++)
2295 print_xml_string(outfile
, "string", attr
->values
[i
].string
.text
);
2298 case IPP_TAG_TEXTLANG
:
2299 case IPP_TAG_NAMELANG
:
2300 for (i
= 0; i
< attr
->num_values
; i
++)
2302 cupsFilePuts(outfile
, "<dict><key>language</key><string>");
2303 print_xml_string(outfile
, NULL
, attr
->values
[i
].string
.language
);
2304 cupsFilePuts(outfile
, "</string><key>string</key><string>");
2305 print_xml_string(outfile
, NULL
, attr
->values
[i
].string
.text
);
2306 cupsFilePuts(outfile
, "</string></dict>\n");
2310 case IPP_TAG_BEGIN_COLLECTION
:
2311 for (i
= 0; i
< attr
->num_values
; i
++)
2313 cupsFilePuts(outfile
, "<dict>\n");
2314 for (colattr
= attr
->values
[i
].collection
->attrs
;
2316 colattr
= colattr
->next
)
2317 print_attr(outfile
, output
, colattr
, NULL
);
2318 cupsFilePuts(outfile
, "</dict>\n");
2323 cupsFilePrintf(outfile
, "<string><<%s>></string>\n", ippTagString(attr
->value_tag
));
2327 if (attr
->num_values
> 1)
2328 cupsFilePuts(outfile
, "</array>\n");
2332 char buffer
[131072]; /* Value buffer */
2334 if (output
== _CUPS_OUTPUT_TEST
)
2338 cupsFilePuts(outfile
, " -- separator --\n");
2342 cupsFilePrintf(outfile
, " %s (%s%s) = ", attr
->name
, attr
->num_values
> 1 ? "1setOf " : "", ippTagString(attr
->value_tag
));
2345 ippAttributeString(attr
, buffer
, sizeof(buffer
));
2346 cupsFilePrintf(outfile
, "%s\n", buffer
);
2352 * 'print_csv()' - Print a line of CSV text.
2357 _cups_testdata_t
*data
, /* I - Test data */
2358 ipp_attribute_t
*attr
, /* I - First attribute for line */
2359 int num_displayed
, /* I - Number of attributes to display */
2360 char **displayed
, /* I - Attributes to display */
2361 size_t *widths
) /* I - Column widths */
2363 int i
; /* Looping var */
2364 size_t maxlength
; /* Max length of all columns */
2365 char *buffer
, /* String buffer */
2366 *bufptr
; /* Pointer into buffer */
2367 ipp_attribute_t
*current
; /* Current attribute */
2371 * Get the maximum string length we have to show and allocate...
2374 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
2375 if (widths
[i
] > maxlength
)
2376 maxlength
= widths
[i
];
2380 if ((buffer
= malloc(maxlength
)) == NULL
)
2384 * Loop through the attributes to display...
2389 for (i
= 0; i
< num_displayed
; i
++)
2392 cupsFilePutChar(data
->outfile
, ',');
2396 for (current
= attr
; current
; current
= current
->next
)
2400 else if (!strcmp(current
->name
, displayed
[i
]))
2402 ippAttributeString(current
, buffer
, maxlength
);
2407 if (strchr(buffer
, ',') != NULL
|| strchr(buffer
, '\"') != NULL
||
2408 strchr(buffer
, '\\') != NULL
)
2410 cupsFilePutChar(cupsFileStdout(), '\"');
2411 for (bufptr
= buffer
; *bufptr
; bufptr
++)
2413 if (*bufptr
== '\\' || *bufptr
== '\"')
2414 cupsFilePutChar(cupsFileStdout(), '\\');
2415 cupsFilePutChar(cupsFileStdout(), *bufptr
);
2417 cupsFilePutChar(cupsFileStdout(), '\"');
2420 cupsFilePuts(data
->outfile
, buffer
);
2422 cupsFilePutChar(cupsFileStdout(), '\n');
2426 for (i
= 0; i
< num_displayed
; i
++)
2429 cupsFilePutChar(cupsFileStdout(), ',');
2431 cupsFilePuts(data
->outfile
, displayed
[i
]);
2433 cupsFilePutChar(cupsFileStdout(), '\n');
2441 * 'print_fatal_error()' - Print a fatal error message.
2446 _cups_testdata_t
*data
, /* I - Test data */
2447 const char *s
, /* I - Printf-style format string */
2448 ...) /* I - Additional arguments as needed */
2450 char buffer
[10240]; /* Format buffer */
2451 va_list ap
; /* Pointer to arguments */
2455 * Format the error message...
2459 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
2466 if (data
->output
== _CUPS_OUTPUT_PLIST
)
2468 print_xml_header(data
);
2469 print_xml_trailer(data
, 0, buffer
);
2472 _cupsLangPrintf(stderr
, "ipptool: %s", buffer
);
2477 * 'print_ippserver_attr()' - Print a attribute suitable for use by ippserver.
2481 print_ippserver_attr(
2482 _cups_testdata_t
*data
, /* I - Test data */
2483 ipp_attribute_t
*attr
, /* I - Attribute to print */
2484 int indent
) /* I - Indentation level */
2486 int i
, /* Looping var */
2487 count
= ippGetCount(attr
);
2488 /* Number of values */
2489 ipp_attribute_t
*colattr
; /* Collection attribute */
2493 cupsFilePrintf(data
->outfile
, "ATTR %s %s", ippTagString(ippGetValueTag(attr
)), ippGetName(attr
));
2495 cupsFilePrintf(data
->outfile
, "%*sMEMBER %s %s", indent
, "", ippTagString(ippGetValueTag(attr
)), ippGetName(attr
));
2497 switch (ippGetValueTag(attr
))
2499 case IPP_TAG_INTEGER
:
2501 for (i
= 0; i
< count
; i
++)
2502 cupsFilePrintf(data
->outfile
, "%s%d", i
? "," : " ", ippGetInteger(attr
, i
));
2505 case IPP_TAG_BOOLEAN
:
2506 cupsFilePuts(data
->outfile
, ippGetBoolean(attr
, 0) ? " true" : " false");
2508 for (i
= 1; i
< count
; i
++)
2509 cupsFilePuts(data
->outfile
, ippGetBoolean(attr
, 1) ? ",true" : ",false");
2512 case IPP_TAG_RANGE
:
2513 for (i
= 0; i
< count
; i
++)
2515 int upper
, lower
= ippGetRange(attr
, i
, &upper
);
2517 cupsFilePrintf(data
->outfile
, "%s%d-%d", i
? "," : " ", lower
, upper
);
2521 case IPP_TAG_RESOLUTION
:
2522 for (i
= 0; i
< count
; i
++)
2525 int yres
, xres
= ippGetResolution(attr
, i
, &yres
, &units
);
2527 cupsFilePrintf(data
->outfile
, "%s%dx%d%s", i
? "," : " ", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
2532 for (i
= 0; i
< count
; i
++)
2533 cupsFilePrintf(data
->outfile
, "%s%s", i
? "," : " ", iso_date(ippGetDate(attr
, i
)));
2536 case IPP_TAG_STRING
:
2537 for (i
= 0; i
< count
; i
++)
2540 const char *s
= (const char *)ippGetOctetString(attr
, i
, &len
);
2542 cupsFilePuts(data
->outfile
, i
? "," : " ");
2543 print_ippserver_string(data
, s
, (size_t)len
);
2548 case IPP_TAG_TEXTLANG
:
2550 case IPP_TAG_NAMELANG
:
2551 case IPP_TAG_KEYWORD
:
2553 case IPP_TAG_URISCHEME
:
2554 case IPP_TAG_CHARSET
:
2555 case IPP_TAG_LANGUAGE
:
2556 case IPP_TAG_MIMETYPE
:
2557 for (i
= 0; i
< count
; i
++)
2559 const char *s
= ippGetString(attr
, i
, NULL
);
2561 cupsFilePuts(data
->outfile
, i
? "," : " ");
2562 print_ippserver_string(data
, s
, strlen(s
));
2566 case IPP_TAG_BEGIN_COLLECTION
:
2567 for (i
= 0; i
< attr
->num_values
; i
++)
2569 ipp_t
*col
= ippGetCollection(attr
, i
);
2571 cupsFilePuts(data
->outfile
, i
? ",{\n" : " {\n");
2572 for (colattr
= ippFirstAttribute(col
); colattr
; colattr
= ippNextAttribute(col
))
2573 print_ippserver_attr(data
, colattr
, indent
+ 4);
2574 cupsFilePrintf(data
->outfile
, "%*s}", indent
, "");
2579 cupsFilePuts(data
->outfile
, " \"\"");
2583 cupsFilePuts(data
->outfile
, "\n");
2588 * 'print_ippserver_string()' - Print a string suitable for use by ippserver.
2592 print_ippserver_string(
2593 _cups_testdata_t
*data
, /* I - Test data */
2594 const char *s
, /* I - String to print */
2595 size_t len
) /* I - Length of string */
2597 cupsFilePutChar(data
->outfile
, '\"');
2601 cupsFilePutChar(data
->outfile
, '\\');
2602 cupsFilePutChar(data
->outfile
, *s
);
2607 cupsFilePutChar(data
->outfile
, '\"');
2612 * 'print_line()' - Print a line of formatted or CSV text.
2617 _cups_testdata_t
*data
, /* I - Test data */
2618 ipp_attribute_t
*attr
, /* I - First attribute for line */
2619 int num_displayed
, /* I - Number of attributes to display */
2620 char **displayed
, /* I - Attributes to display */
2621 size_t *widths
) /* I - Column widths */
2623 int i
; /* Looping var */
2624 size_t maxlength
; /* Max length of all columns */
2625 char *buffer
; /* String buffer */
2626 ipp_attribute_t
*current
; /* Current attribute */
2630 * Get the maximum string length we have to show and allocate...
2633 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
2634 if (widths
[i
] > maxlength
)
2635 maxlength
= widths
[i
];
2639 if ((buffer
= malloc(maxlength
)) == NULL
)
2643 * Loop through the attributes to display...
2648 for (i
= 0; i
< num_displayed
; i
++)
2651 cupsFilePutChar(cupsFileStdout(), ' ');
2655 for (current
= attr
; current
; current
= current
->next
)
2659 else if (!strcmp(current
->name
, displayed
[i
]))
2661 ippAttributeString(current
, buffer
, maxlength
);
2666 cupsFilePrintf(data
->outfile
, "%*s", (int)-widths
[i
], buffer
);
2668 cupsFilePutChar(cupsFileStdout(), '\n');
2672 for (i
= 0; i
< num_displayed
; i
++)
2675 cupsFilePutChar(cupsFileStdout(), ' ');
2677 cupsFilePrintf(data
->outfile
, "%*s", (int)-widths
[i
], displayed
[i
]);
2679 cupsFilePutChar(cupsFileStdout(), '\n');
2681 for (i
= 0; i
< num_displayed
; i
++)
2684 cupsFilePutChar(cupsFileStdout(), ' ');
2686 memset(buffer
, '-', widths
[i
]);
2687 buffer
[widths
[i
]] = '\0';
2688 cupsFilePuts(data
->outfile
, buffer
);
2690 cupsFilePutChar(cupsFileStdout(), '\n');
2698 * 'print_xml_header()' - Print a standard XML plist header.
2702 print_xml_header(_cups_testdata_t
*data
)/* I - Test data */
2704 if (!data
->xml_header
)
2706 cupsFilePuts(data
->outfile
, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
2707 cupsFilePuts(data
->outfile
, "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n");
2708 cupsFilePuts(data
->outfile
, "<plist version=\"1.0\">\n");
2709 cupsFilePuts(data
->outfile
, "<dict>\n");
2710 cupsFilePuts(data
->outfile
, "<key>ipptoolVersion</key>\n");
2711 cupsFilePuts(data
->outfile
, "<string>" CUPS_SVERSION
"</string>\n");
2712 cupsFilePuts(data
->outfile
, "<key>Transfer</key>\n");
2713 cupsFilePrintf(data
->outfile
, "<string>%s</string>\n", data
->transfer
== _CUPS_TRANSFER_AUTO
? "auto" : data
->transfer
== _CUPS_TRANSFER_CHUNKED
? "chunked" : "length");
2714 cupsFilePuts(data
->outfile
, "<key>Tests</key>\n");
2715 cupsFilePuts(data
->outfile
, "<array>\n");
2717 data
->xml_header
= 1;
2723 * 'print_xml_string()' - Print an XML string with escaping.
2727 print_xml_string(cups_file_t
*outfile
, /* I - Test data */
2728 const char *element
, /* I - Element name or NULL */
2729 const char *s
) /* I - String to print */
2732 cupsFilePrintf(outfile
, "<%s>", element
);
2737 cupsFilePuts(outfile
, "&");
2739 cupsFilePuts(outfile
, "<");
2741 cupsFilePuts(outfile
, ">");
2742 else if ((*s
& 0xe0) == 0xc0)
2745 * Validate UTF-8 two-byte sequence...
2748 if ((s
[1] & 0xc0) != 0x80)
2750 cupsFilePutChar(outfile
, '?');
2755 cupsFilePutChar(outfile
, *s
++);
2756 cupsFilePutChar(outfile
, *s
);
2759 else if ((*s
& 0xf0) == 0xe0)
2762 * Validate UTF-8 three-byte sequence...
2765 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80)
2767 cupsFilePutChar(outfile
, '?');
2772 cupsFilePutChar(outfile
, *s
++);
2773 cupsFilePutChar(outfile
, *s
++);
2774 cupsFilePutChar(outfile
, *s
);
2777 else if ((*s
& 0xf8) == 0xf0)
2780 * Validate UTF-8 four-byte sequence...
2783 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80 ||
2784 (s
[3] & 0xc0) != 0x80)
2786 cupsFilePutChar(outfile
, '?');
2791 cupsFilePutChar(outfile
, *s
++);
2792 cupsFilePutChar(outfile
, *s
++);
2793 cupsFilePutChar(outfile
, *s
++);
2794 cupsFilePutChar(outfile
, *s
);
2797 else if ((*s
& 0x80) || (*s
< ' ' && !isspace(*s
& 255)))
2800 * Invalid control character...
2803 cupsFilePutChar(outfile
, '?');
2806 cupsFilePutChar(outfile
, *s
);
2812 cupsFilePrintf(outfile
, "</%s>\n", element
);
2817 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
2822 _cups_testdata_t
*data
, /* I - Test data */
2823 int success
, /* I - 1 on success, 0 on failure */
2824 const char *message
) /* I - Error message or NULL */
2826 if (data
->xml_header
)
2828 cupsFilePuts(data
->outfile
, "</array>\n");
2829 cupsFilePuts(data
->outfile
, "<key>Successful</key>\n");
2830 cupsFilePuts(data
->outfile
, success
? "<true />\n" : "<false />\n");
2833 cupsFilePuts(data
->outfile
, "<key>ErrorMessage</key>\n");
2834 print_xml_string(data
->outfile
, "string", message
);
2836 cupsFilePuts(data
->outfile
, "</dict>\n");
2837 cupsFilePuts(data
->outfile
, "</plist>\n");
2839 data
->xml_header
= 0;
2846 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
2850 sigterm_handler(int sig
) /* I - Signal number (unused) */
2856 signal(SIGINT
, SIG_DFL
);
2857 signal(SIGTERM
, SIG_DFL
);
2863 * 'timeout_cb()' - Handle HTTP timeouts.
2866 static int /* O - 1 to continue, 0 to cancel */
2867 timeout_cb(http_t
*http
, /* I - Connection to server */
2868 void *user_data
) /* I - User data (unused) */
2870 int buffered
= 0; /* Bytes buffered but not yet sent */
2876 * If the socket still have data waiting to be sent to the printer (as can
2877 * happen if the printer runs out of paper), continue to wait until the output
2878 * buffer is empty...
2881 #ifdef SO_NWRITE /* macOS and some versions of Linux */
2882 socklen_t len
= sizeof(buffered
); /* Size of return value */
2884 if (getsockopt(httpGetFd(http
), SOL_SOCKET
, SO_NWRITE
, &buffered
, &len
))
2887 #elif defined(SIOCOUTQ) /* Others except Windows */
2888 if (ioctl(httpGetFd(http
), SIOCOUTQ
, &buffered
))
2891 #else /* Windows (not possible) */
2893 #endif /* SO_NWRITE */
2895 return (buffered
> 0);
2900 * 'token_cb()' - Parse test file-specific tokens and run tests.
2903 static int /* O - 1 to continue, 0 to stop */
2904 token_cb(_ipp_file_t
*f
, /* I - IPP file data */
2905 _ipp_vars_t
*vars
, /* I - IPP variables */
2906 _cups_testdata_t
*data
, /* I - Test data */
2907 const char *token
) /* I - Current token */
2909 char name
[1024], /* Name string */
2910 temp
[1024], /* Temporary string */
2911 value
[1024], /* Value string */
2912 *ptr
; /* Pointer into value */
2918 * Initialize state as needed (nothing for now...)
2926 * Parse until we see a close brace...
2929 if (_cups_strcasecmp(token
, "COUNT") &&
2930 _cups_strcasecmp(token
, "DEFINE-MATCH") &&
2931 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
2932 _cups_strcasecmp(token
, "DEFINE-VALUE") &&
2933 _cups_strcasecmp(token
, "IF-DEFINED") &&
2934 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
2935 _cups_strcasecmp(token
, "IN-GROUP") &&
2936 _cups_strcasecmp(token
, "OF-TYPE") &&
2937 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
2938 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
2939 _cups_strcasecmp(token
, "REPEAT-NO-MATCH") &&
2940 _cups_strcasecmp(token
, "SAME-COUNT-AS") &&
2941 _cups_strcasecmp(token
, "WITH-ALL-VALUES") &&
2942 _cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") &&
2943 _cups_strcasecmp(token
, "WITH-ALL-RESOURCES") &&
2944 _cups_strcasecmp(token
, "WITH-ALL-SCHEMES") &&
2945 _cups_strcasecmp(token
, "WITH-HOSTNAME") &&
2946 _cups_strcasecmp(token
, "WITH-RESOURCE") &&
2947 _cups_strcasecmp(token
, "WITH-SCHEME") &&
2948 _cups_strcasecmp(token
, "WITH-VALUE") &&
2949 _cups_strcasecmp(token
, "WITH-VALUE-FROM"))
2950 data
->last_expect
= NULL
;
2952 if (_cups_strcasecmp(token
, "DEFINE-MATCH") &&
2953 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
2954 _cups_strcasecmp(token
, "IF-DEFINED") &&
2955 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
2956 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
2957 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
2958 _cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
2959 data
->last_status
= NULL
;
2961 if (!strcmp(token
, "}"))
2963 return (do_test(f
, vars
, data
));
2965 else if (!strcmp(token
, "COMPRESSION"))
2969 * COMPRESSION deflate
2973 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
2975 _ippVarsExpand(vars
, data
->compression
, temp
, sizeof(data
->compression
));
2977 if (strcmp(data
->compression
, "none") && strcmp(data
->compression
, "deflate") &&
2978 strcmp(data
->compression
, "gzip"))
2980 if (strcmp(data
->compression
, "none"))
2981 #endif /* HAVE_LIBZ */
2983 print_fatal_error(data
, "Unsupported COMPRESSION value \"%s\" on line %d of \"%s\".", data
->compression
, f
->linenum
, f
->filename
);
2987 if (!strcmp(data
->compression
, "none"))
2988 data
->compression
[0] = '\0';
2992 print_fatal_error(data
, "Missing COMPRESSION value on line %d of \"%s\".", f
->linenum
, f
->filename
);
2996 else if (!strcmp(token
, "DEFINE"))
3002 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
3004 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3005 _ippVarsSet(vars
, name
, value
);
3009 print_fatal_error(data
, "Missing DEFINE name and/or value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3013 else if (!strcmp(token
, "IGNORE-ERRORS"))
3020 if (_ippFileReadToken(f
, temp
, sizeof(temp
)) && (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
3022 data
->ignore_errors
= !_cups_strcasecmp(temp
, "yes");
3026 print_fatal_error(data
, "Missing IGNORE-ERRORS value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3030 else if (!_cups_strcasecmp(token
, "NAME"))
3036 _ippFileReadToken(f
, temp
, sizeof(temp
));
3037 _ippVarsExpand(vars
, data
->name
, temp
, sizeof(data
->name
));
3039 else if (!_cups_strcasecmp(token
, "PAUSE"))
3042 * Pause with a message...
3045 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3047 pause_message(temp
);
3051 print_fatal_error(data
, "Missing PAUSE message on line %d of \"%s\".", f
->linenum
, f
->filename
);
3055 else if (!strcmp(token
, "REQUEST-ID"))
3062 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3064 if (isdigit(temp
[0] & 255))
3066 data
->request_id
= atoi(temp
);
3068 else if (!_cups_strcasecmp(temp
, "random"))
3070 data
->request_id
= (CUPS_RAND() % 1000) * 137 + 1;
3074 print_fatal_error(data
, "Bad REQUEST-ID value \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
3080 print_fatal_error(data
, "Missing REQUEST-ID value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3084 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
3087 * SKIP-IF-DEFINED variable
3090 if (_ippFileReadToken(f
, name
, sizeof(name
)))
3092 if (_ippVarsGet(vars
, name
))
3093 data
->skip_test
= 1;
3097 print_fatal_error(data
, "Missing SKIP-IF-DEFINED value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3101 else if (!strcmp(token
, "SKIP-IF-MISSING"))
3104 * SKIP-IF-MISSING filename
3107 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3109 char filename
[1024]; /* Filename */
3111 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3112 get_filename(f
->filename
, filename
, temp
, sizeof(filename
));
3114 if (access(filename
, R_OK
))
3115 data
->skip_test
= 1;
3119 print_fatal_error(data
, "Missing SKIP-IF-MISSING filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
3123 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
3126 * SKIP-IF-NOT-DEFINED variable
3129 if (_ippFileReadToken(f
, name
, sizeof(name
)))
3131 if (!_ippVarsGet(vars
, name
))
3132 data
->skip_test
= 1;
3136 print_fatal_error(data
, "Missing SKIP-IF-NOT-DEFINED value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3140 else if (!strcmp(token
, "SKIP-PREVIOUS-ERROR"))
3143 * SKIP-PREVIOUS-ERROR yes
3144 * SKIP-PREVIOUS-ERROR no
3147 if (_ippFileReadToken(f
, temp
, sizeof(temp
)) && (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
3149 data
->skip_previous
= !_cups_strcasecmp(temp
, "yes");
3153 print_fatal_error(data
, "Missing SKIP-PREVIOUS-ERROR value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3157 else if (!strcmp(token
, "TEST-ID"))
3163 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3165 _ippVarsExpand(vars
, data
->test_id
, temp
, sizeof(data
->test_id
));
3169 print_fatal_error(data
, "Missing TEST-ID value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3173 else if (!strcmp(token
, "TRANSFER"))
3181 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3183 if (!strcmp(temp
, "auto"))
3185 data
->transfer
= _CUPS_TRANSFER_AUTO
;
3187 else if (!strcmp(temp
, "chunked"))
3189 data
->transfer
= _CUPS_TRANSFER_CHUNKED
;
3191 else if (!strcmp(temp
, "length"))
3193 data
->transfer
= _CUPS_TRANSFER_LENGTH
;
3197 print_fatal_error(data
, "Bad TRANSFER value \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
3203 print_fatal_error(data
, "Missing TRANSFER value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3207 else if (!_cups_strcasecmp(token
, "VERSION"))
3209 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3211 if (!strcmp(temp
, "0.0"))
3215 else if (!strcmp(temp
, "1.0"))
3219 else if (!strcmp(temp
, "1.1"))
3223 else if (!strcmp(temp
, "2.0"))
3227 else if (!strcmp(temp
, "2.1"))
3231 else if (!strcmp(temp
, "2.2"))
3237 print_fatal_error(data
, "Bad VERSION \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
3243 print_fatal_error(data
, "Missing VERSION number on line %d of \"%s\".", f
->linenum
, f
->filename
);
3247 else if (!_cups_strcasecmp(token
, "RESOURCE"))
3253 if (!_ippFileReadToken(f
, data
->resource
, sizeof(data
->resource
)))
3255 print_fatal_error(data
, "Missing RESOURCE path on line %d of \"%s\".", f
->linenum
, f
->filename
);
3259 else if (!_cups_strcasecmp(token
, "OPERATION"))
3265 ipp_op_t op
; /* Operation code */
3267 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3269 print_fatal_error(data
, "Missing OPERATION code on line %d of \"%s\".", f
->linenum
, f
->filename
);
3273 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3275 if ((op
= ippOpValue(value
)) == (ipp_op_t
)-1 && (op
= (ipp_op_t
)strtol(value
, NULL
, 0)) == 0)
3277 print_fatal_error(data
, "Bad OPERATION code \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
3281 ippSetOperation(f
->attrs
, op
);
3283 else if (!_cups_strcasecmp(token
, "GROUP"))
3286 * Attribute group...
3289 ipp_tag_t group_tag
; /* Group tag */
3291 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3293 print_fatal_error(data
, "Missing GROUP tag on line %d of \"%s\".", f
->linenum
, f
->filename
);
3297 if ((group_tag
= ippTagValue(temp
)) == IPP_TAG_ZERO
|| group_tag
>= IPP_TAG_UNSUPPORTED_VALUE
)
3299 print_fatal_error(data
, "Bad GROUP tag \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
3303 if (group_tag
== f
->group_tag
)
3304 ippAddSeparator(f
->attrs
);
3306 f
->group_tag
= group_tag
;
3308 else if (!_cups_strcasecmp(token
, "DELAY"))
3311 * Delay before operation...
3314 double dval
; /* Delay value */
3316 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3318 print_fatal_error(data
, "Missing DELAY value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3322 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3324 if ((dval
= _cupsStrScand(value
, &ptr
, localeconv())) < 0.0 || (*ptr
&& *ptr
!= ','))
3326 print_fatal_error(data
, "Bad DELAY value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
3330 data
->delay
= (useconds_t
)(1000000.0 * dval
);
3334 if ((dval
= _cupsStrScand(ptr
+ 1, &ptr
, localeconv())) <= 0.0 || *ptr
)
3336 print_fatal_error(data
, "Bad DELAY value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
3340 data
->repeat_interval
= (useconds_t
)(1000000.0 * dval
);
3343 data
->repeat_interval
= data
->delay
;
3345 else if (!_cups_strcasecmp(token
, "FILE"))
3351 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3353 print_fatal_error(data
, "Missing FILE filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
3357 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3358 get_filename(f
->filename
, data
->file
, value
, sizeof(data
->file
));
3360 if (access(data
->file
, R_OK
))
3362 print_fatal_error(data
, "Filename \"%s\" (mapped to \"%s\") on line %d of \"%s\" cannot be read.", value
, data
->file
, f
->linenum
, f
->filename
);
3366 else if (!_cups_strcasecmp(token
, "STATUS"))
3372 if (data
->num_statuses
>= (int)(sizeof(data
->statuses
) / sizeof(data
->statuses
[0])))
3374 print_fatal_error(data
, "Too many STATUS's on line %d of \"%s\".", f
->linenum
, f
->filename
);
3378 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3380 print_fatal_error(data
, "Missing STATUS code on line %d of \"%s\".", f
->linenum
, f
->filename
);
3384 if ((data
->statuses
[data
->num_statuses
].status
= ippErrorValue(temp
)) == (ipp_status_t
)-1 && (data
->statuses
[data
->num_statuses
].status
= (ipp_status_t
)strtol(temp
, NULL
, 0)) == 0)
3386 print_fatal_error(data
, "Bad STATUS code \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
3390 data
->last_status
= data
->statuses
+ data
->num_statuses
;
3391 data
->num_statuses
++;
3393 data
->last_status
->define_match
= NULL
;
3394 data
->last_status
->define_no_match
= NULL
;
3395 data
->last_status
->if_defined
= NULL
;
3396 data
->last_status
->if_not_defined
= NULL
;
3397 data
->last_status
->repeat_limit
= 1000;
3398 data
->last_status
->repeat_match
= 0;
3399 data
->last_status
->repeat_no_match
= 0;
3401 else if (!_cups_strcasecmp(token
, "EXPECT") || !_cups_strcasecmp(token
, "EXPECT-ALL"))
3404 * Expected attributes...
3407 int expect_all
= !_cups_strcasecmp(token
, "EXPECT-ALL");
3409 if (data
->num_expects
>= (int)(sizeof(data
->expects
) / sizeof(data
->expects
[0])))
3411 print_fatal_error(data
, "Too many EXPECT's on line %d of \"%s\".", f
->linenum
, f
->filename
);
3415 if (!_ippFileReadToken(f
, name
, sizeof(name
)))
3417 print_fatal_error(data
, "Missing EXPECT name on line %d of \"%s\".", f
->linenum
, f
->filename
);
3421 data
->last_expect
= data
->expects
+ data
->num_expects
;
3422 data
->num_expects
++;
3424 memset(data
->last_expect
, 0, sizeof(_cups_expect_t
));
3425 data
->last_expect
->repeat_limit
= 1000;
3426 data
->last_expect
->expect_all
= expect_all
;
3430 data
->last_expect
->not_expect
= 1;
3431 data
->last_expect
->name
= strdup(name
+ 1);
3433 else if (name
[0] == '?')
3435 data
->last_expect
->optional
= 1;
3436 data
->last_expect
->name
= strdup(name
+ 1);
3439 data
->last_expect
->name
= strdup(name
);
3441 else if (!_cups_strcasecmp(token
, "COUNT"))
3443 int count
; /* Count value */
3445 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3447 print_fatal_error(data
, "Missing COUNT number on line %d of \"%s\".", f
->linenum
, f
->filename
);
3451 if ((count
= atoi(temp
)) <= 0)
3453 print_fatal_error(data
, "Bad COUNT \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
3457 if (data
->last_expect
)
3459 data
->last_expect
->count
= count
;
3463 print_fatal_error(data
, "COUNT without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
3467 else if (!_cups_strcasecmp(token
, "DEFINE-MATCH"))
3469 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3471 print_fatal_error(data
, "Missing DEFINE-MATCH variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
3475 if (data
->last_expect
)
3477 data
->last_expect
->define_match
= strdup(temp
);
3479 else if (data
->last_status
)
3481 data
->last_status
->define_match
= strdup(temp
);
3485 print_fatal_error(data
, "DEFINE-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
3489 else if (!_cups_strcasecmp(token
, "DEFINE-NO-MATCH"))
3491 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3493 print_fatal_error(data
, "Missing DEFINE-NO-MATCH variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
3497 if (data
->last_expect
)
3499 data
->last_expect
->define_no_match
= strdup(temp
);
3501 else if (data
->last_status
)
3503 data
->last_status
->define_no_match
= strdup(temp
);
3507 print_fatal_error(data
, "DEFINE-NO-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
3511 else if (!_cups_strcasecmp(token
, "DEFINE-VALUE"))
3513 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3515 print_fatal_error(data
, "Missing DEFINE-VALUE variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
3519 if (data
->last_expect
)
3521 data
->last_expect
->define_value
= strdup(temp
);
3525 print_fatal_error(data
, "DEFINE-VALUE without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
3529 else if (!_cups_strcasecmp(token
, "OF-TYPE"))
3531 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3533 print_fatal_error(data
, "Missing OF-TYPE value tag(s) on line %d of \"%s\".", f
->linenum
, f
->filename
);
3537 if (data
->last_expect
)
3539 data
->last_expect
->of_type
= strdup(temp
);
3543 print_fatal_error(data
, "OF-TYPE without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
3547 else if (!_cups_strcasecmp(token
, "IN-GROUP"))
3549 ipp_tag_t in_group
; /* IN-GROUP value */
3551 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3553 print_fatal_error(data
, "Missing IN-GROUP group tag on line %d of \"%s\".", f
->linenum
, f
->filename
);
3557 if ((in_group
= ippTagValue(temp
)) == IPP_TAG_ZERO
|| in_group
>= IPP_TAG_UNSUPPORTED_VALUE
)
3559 print_fatal_error(data
, "Bad IN-GROUP group tag \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
3562 else if (data
->last_expect
)
3564 data
->last_expect
->in_group
= in_group
;
3568 print_fatal_error(data
, "IN-GROUP without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
3572 else if (!_cups_strcasecmp(token
, "REPEAT-LIMIT"))
3574 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3576 print_fatal_error(data
, "Missing REPEAT-LIMIT value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3579 else if (atoi(temp
) <= 0)
3581 print_fatal_error(data
, "Bad REPEAT-LIMIT value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3585 if (data
->last_status
)
3587 data
->last_status
->repeat_limit
= atoi(temp
);
3589 else if (data
->last_expect
)
3591 data
->last_expect
->repeat_limit
= atoi(temp
);
3595 print_fatal_error(data
, "REPEAT-LIMIT without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
3599 else if (!_cups_strcasecmp(token
, "REPEAT-MATCH"))
3601 if (data
->last_status
)
3603 data
->last_status
->repeat_match
= 1;
3605 else if (data
->last_expect
)
3607 data
->last_expect
->repeat_match
= 1;
3611 print_fatal_error(data
, "REPEAT-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
3615 else if (!_cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
3617 if (data
->last_status
)
3619 data
->last_status
->repeat_no_match
= 1;
3621 else if (data
->last_expect
)
3623 data
->last_expect
->repeat_no_match
= 1;
3627 print_fatal_error(data
, "REPEAT-NO-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
3631 else if (!_cups_strcasecmp(token
, "SAME-COUNT-AS"))
3633 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3635 print_fatal_error(data
, "Missing SAME-COUNT-AS name on line %d of \"%s\".", f
->linenum
, f
->filename
);
3639 if (data
->last_expect
)
3641 data
->last_expect
->same_count_as
= strdup(temp
);
3645 print_fatal_error(data
, "SAME-COUNT-AS without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
3649 else if (!_cups_strcasecmp(token
, "IF-DEFINED"))
3651 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3653 print_fatal_error(data
, "Missing IF-DEFINED name on line %d of \"%s\".", f
->linenum
, f
->filename
);
3657 if (data
->last_expect
)
3659 data
->last_expect
->if_defined
= strdup(temp
);
3661 else if (data
->last_status
)
3663 data
->last_status
->if_defined
= strdup(temp
);
3667 print_fatal_error(data
, "IF-DEFINED without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
3671 else if (!_cups_strcasecmp(token
, "IF-NOT-DEFINED"))
3673 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3675 print_fatal_error(data
, "Missing IF-NOT-DEFINED name on line %d of \"%s\".", f
->linenum
, f
->filename
);
3679 if (data
->last_expect
)
3681 data
->last_expect
->if_not_defined
= strdup(temp
);
3683 else if (data
->last_status
)
3685 data
->last_status
->if_not_defined
= strdup(temp
);
3689 print_fatal_error(data
, "IF-NOT-DEFINED without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
3693 else if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") ||
3694 !_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
3695 !_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
3696 !_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
3697 !_cups_strcasecmp(token
, "WITH-HOSTNAME") ||
3698 !_cups_strcasecmp(token
, "WITH-RESOURCE") ||
3699 !_cups_strcasecmp(token
, "WITH-SCHEME") ||
3700 !_cups_strcasecmp(token
, "WITH-VALUE"))
3702 off_t lastpos
; /* Last file position */
3704 if (data
->last_expect
)
3706 if (!_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") || !_cups_strcasecmp(token
, "WITH-HOSTNAME"))
3707 data
->last_expect
->with_flags
= _CUPS_WITH_HOSTNAME
;
3708 else if (!_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") || !_cups_strcasecmp(token
, "WITH-RESOURCE"))
3709 data
->last_expect
->with_flags
= _CUPS_WITH_RESOURCE
;
3710 else if (!_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") || !_cups_strcasecmp(token
, "WITH-SCHEME"))
3711 data
->last_expect
->with_flags
= _CUPS_WITH_SCHEME
;
3713 if (!_cups_strncasecmp(token
, "WITH-ALL-", 9))
3714 data
->last_expect
->with_flags
|= _CUPS_WITH_ALL
;
3717 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3719 print_fatal_error(data
, "Missing %s value on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
3724 * Read additional comma-delimited values - needed since legacy test files
3725 * will have unquoted WITH-VALUE values with commas...
3728 ptr
= temp
+ strlen(temp
);
3732 lastpos
= cupsFileTell(f
->fp
);
3735 if (!_ippFileReadToken(f
, ptr
, (sizeof(temp
) - (size_t)(ptr
- temp
))))
3738 if (!strcmp(ptr
, ","))
3746 if (!_ippFileReadToken(f
, ptr
, (sizeof(temp
) - (size_t)(ptr
- temp
))))
3752 * Not another value, stop here...
3755 cupsFileSeek(f
->fp
, lastpos
);
3761 if (data
->last_expect
)
3764 * Expand any variables in the value and then save it.
3767 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3769 ptr
= value
+ strlen(value
) - 1;
3771 if (value
[0] == '/' && ptr
> value
&& *ptr
== '/')
3774 * WITH-VALUE is a POSIX extended regular expression.
3777 data
->last_expect
->with_value
= calloc(1, (size_t)(ptr
- value
));
3778 data
->last_expect
->with_flags
|= _CUPS_WITH_REGEX
;
3780 if (data
->last_expect
->with_value
)
3781 memcpy(data
->last_expect
->with_value
, value
+ 1, (size_t)(ptr
- value
- 1));
3786 * WITH-VALUE is a literal value...
3789 for (ptr
= value
; *ptr
; ptr
++)
3791 if (*ptr
== '\\' && ptr
[1])
3794 * Remove \ from \foo...
3797 _cups_strcpy(ptr
, ptr
+ 1);
3801 data
->last_expect
->with_value
= strdup(value
);
3802 data
->last_expect
->with_flags
|= _CUPS_WITH_LITERAL
;
3807 print_fatal_error(data
, "%s without a preceding EXPECT on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
3811 else if (!_cups_strcasecmp(token
, "WITH-VALUE-FROM"))
3813 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3815 print_fatal_error(data
, "Missing %s value on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
3819 if (data
->last_expect
)
3822 * Expand any variables in the value and then save it.
3825 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3827 data
->last_expect
->with_value_from
= strdup(value
);
3828 data
->last_expect
->with_flags
= _CUPS_WITH_LITERAL
;
3832 print_fatal_error(data
, "%s without a preceding EXPECT on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
3836 else if (!_cups_strcasecmp(token
, "DISPLAY"))
3839 * Display attributes...
3842 if (data
->num_displayed
>= (int)(sizeof(data
->displayed
) / sizeof(data
->displayed
[0])))
3844 print_fatal_error(data
, "Too many DISPLAY's on line %d of \"%s\".", f
->linenum
, f
->filename
);
3848 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3850 print_fatal_error(data
, "Missing DISPLAY name on line %d of \"%s\".", f
->linenum
, f
->filename
);
3854 data
->displayed
[data
->num_displayed
] = strdup(temp
);
3855 data
->num_displayed
++;
3859 print_fatal_error(data
, "Unexpected token %s seen on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
3866 * Scan for the start of a test (open brace)...
3869 if (!strcmp(token
, "{"))
3875 if (data
->show_header
)
3877 if (data
->output
== _CUPS_OUTPUT_PLIST
)
3878 print_xml_header(data
);
3880 if (data
->output
== _CUPS_OUTPUT_TEST
|| (data
->output
== _CUPS_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
3881 cupsFilePrintf(cupsFileStdout(), "\"%s\":\n", f
->filename
);
3883 data
->show_header
= 0;
3886 data
->compression
[0] = '\0';
3888 data
->num_expects
= 0;
3889 data
->last_expect
= NULL
;
3890 data
->file
[0] = '\0';
3891 data
->ignore_errors
= data
->def_ignore_errors
;
3892 strlcpy(data
->name
, f
->filename
, sizeof(data
->name
));
3893 if ((ptr
= strrchr(data
->name
, '.')) != NULL
)
3895 data
->repeat_interval
= 5000000;
3896 data
->request_id
++;
3897 strlcpy(data
->resource
, vars
->resource
, sizeof(data
->resource
));
3898 data
->skip_previous
= 0;
3899 data
->skip_test
= 0;
3900 data
->num_statuses
= 0;
3901 data
->last_status
= NULL
;
3902 data
->test_id
[0] = '\0';
3903 data
->transfer
= data
->def_transfer
;
3904 data
->version
= data
->def_version
;
3906 f
->attrs
= ippNew();
3907 f
->group_tag
= IPP_TAG_ZERO
;
3909 else if (!strcmp(token
, "DEFINE"))
3915 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
3917 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3918 _ippVarsSet(vars
, name
, value
);
3922 print_fatal_error(data
, "Missing DEFINE name and/or value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3926 else if (!strcmp(token
, "DEFINE-DEFAULT"))
3929 * DEFINE-DEFAULT name value
3932 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
3934 if (!_ippVarsGet(vars
, name
))
3936 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3937 _ippVarsSet(vars
, name
, value
);
3942 print_fatal_error(data
, "Missing DEFINE-DEFAULT name and/or value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3946 else if (!strcmp(token
, "FILE-ID"))
3952 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3954 _ippVarsExpand(vars
, data
->file_id
, temp
, sizeof(data
->file_id
));
3958 print_fatal_error(data
, "Missing FILE-ID value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3962 else if (!strcmp(token
, "IGNORE-ERRORS"))
3969 if (_ippFileReadToken(f
, temp
, sizeof(temp
)) && (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
3971 data
->def_ignore_errors
= !_cups_strcasecmp(temp
, "yes");
3975 print_fatal_error(data
, "Missing IGNORE-ERRORS value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3979 else if (!strcmp(token
, "INCLUDE"))
3982 * INCLUDE "filename"
3983 * INCLUDE <filename>
3986 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3989 * Map the filename to and then run the tests...
3992 _cups_testdata_t inc_data
; /* Data for included file */
3993 char filename
[1024]; /* Mapped filename */
3995 memcpy(&inc_data
, data
, sizeof(inc_data
));
3996 inc_data
.http
= NULL
;
3998 inc_data
.prev_pass
= 1;
3999 inc_data
.show_header
= 1;
4001 if (!do_tests(get_filename(f
->filename
, filename
, temp
, sizeof(filename
)), vars
, &inc_data
) && data
->stop_after_include_error
)
4003 data
->pass
= data
->prev_pass
= 0;
4009 print_fatal_error(data
, "Missing INCLUDE filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
4013 data
->show_header
= 1;
4015 else if (!strcmp(token
, "INCLUDE-IF-DEFINED"))
4018 * INCLUDE-IF-DEFINED name "filename"
4019 * INCLUDE-IF-DEFINED name <filename>
4022 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
4025 * Map the filename to and then run the tests...
4028 _cups_testdata_t inc_data
; /* Data for included file */
4029 char filename
[1024]; /* Mapped filename */
4031 memcpy(&inc_data
, data
, sizeof(inc_data
));
4032 inc_data
.http
= NULL
;
4034 inc_data
.prev_pass
= 1;
4035 inc_data
.show_header
= 1;
4037 if (!do_tests(get_filename(f
->filename
, filename
, temp
, sizeof(filename
)), vars
, &inc_data
) && data
->stop_after_include_error
)
4039 data
->pass
= data
->prev_pass
= 0;
4045 print_fatal_error(data
, "Missing INCLUDE-IF-DEFINED name or filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
4049 data
->show_header
= 1;
4051 else if (!strcmp(token
, "INCLUDE-IF-NOT-DEFINED"))
4054 * INCLUDE-IF-NOT-DEFINED name "filename"
4055 * INCLUDE-IF-NOT-DEFINED name <filename>
4058 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
4061 * Map the filename to and then run the tests...
4064 _cups_testdata_t inc_data
; /* Data for included file */
4065 char filename
[1024]; /* Mapped filename */
4067 memcpy(&inc_data
, data
, sizeof(inc_data
));
4068 inc_data
.http
= NULL
;
4070 inc_data
.prev_pass
= 1;
4071 inc_data
.show_header
= 1;
4073 if (!do_tests(get_filename(f
->filename
, filename
, temp
, sizeof(filename
)), vars
, &inc_data
) && data
->stop_after_include_error
)
4075 data
->pass
= data
->prev_pass
= 0;
4081 print_fatal_error(data
, "Missing INCLUDE-IF-NOT-DEFINED name or filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
4085 data
->show_header
= 1;
4087 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
4090 * SKIP-IF-DEFINED variable
4093 if (_ippFileReadToken(f
, name
, sizeof(name
)))
4095 if (_ippVarsGet(vars
, name
))
4096 data
->skip_test
= 1;
4100 print_fatal_error(data
, "Missing SKIP-IF-DEFINED variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
4104 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
4107 * SKIP-IF-NOT-DEFINED variable
4110 if (_ippFileReadToken(f
, name
, sizeof(name
)))
4112 if (!_ippVarsGet(vars
, name
))
4113 data
->skip_test
= 1;
4117 print_fatal_error(data
, "Missing SKIP-IF-NOT-DEFINED variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
4121 else if (!strcmp(token
, "STOP-AFTER-INCLUDE-ERROR"))
4124 * STOP-AFTER-INCLUDE-ERROR yes
4125 * STOP-AFTER-INCLUDE-ERROR no
4128 if (_ippFileReadToken(f
, temp
, sizeof(temp
)) && (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
4130 data
->stop_after_include_error
= !_cups_strcasecmp(temp
, "yes");
4134 print_fatal_error(data
, "Missing STOP-AFTER-INCLUDE-ERROR value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4138 else if (!strcmp(token
, "TRANSFER"))
4146 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
4148 if (!strcmp(temp
, "auto"))
4149 data
->def_transfer
= _CUPS_TRANSFER_AUTO
;
4150 else if (!strcmp(temp
, "chunked"))
4151 data
->def_transfer
= _CUPS_TRANSFER_CHUNKED
;
4152 else if (!strcmp(temp
, "length"))
4153 data
->def_transfer
= _CUPS_TRANSFER_LENGTH
;
4156 print_fatal_error(data
, "Bad TRANSFER value \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
4162 print_fatal_error(data
, "Missing TRANSFER value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4166 else if (!strcmp(token
, "VERSION"))
4168 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
4170 if (!strcmp(temp
, "1.0"))
4171 data
->def_version
= 10;
4172 else if (!strcmp(temp
, "1.1"))
4173 data
->def_version
= 11;
4174 else if (!strcmp(temp
, "2.0"))
4175 data
->def_version
= 20;
4176 else if (!strcmp(temp
, "2.1"))
4177 data
->def_version
= 21;
4178 else if (!strcmp(temp
, "2.2"))
4179 data
->def_version
= 22;
4182 print_fatal_error(data
, "Bad VERSION \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
4188 print_fatal_error(data
, "Missing VERSION number on line %d of \"%s\".", f
->linenum
, f
->filename
);
4194 print_fatal_error(data
, "Unexpected token %s seen on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
4204 * 'usage()' - Show program usage.
4210 _cupsLangPuts(stderr
, _("Usage: ipptool [options] URI filename [ ... filenameN ]"));
4211 _cupsLangPuts(stderr
, _("Options:"));
4212 _cupsLangPuts(stderr
, _(" --help Show help."));
4213 _cupsLangPuts(stderr
, _(" --ippserver filename Produce ippserver attribute file."));
4214 _cupsLangPuts(stderr
, _(" --stop-after-include-error\n"
4215 " Stop tests after a failed INCLUDE."));
4216 _cupsLangPuts(stderr
, _(" --version Show version."));
4217 _cupsLangPuts(stderr
, _(" -4 Connect using IPv4."));
4218 _cupsLangPuts(stderr
, _(" -6 Connect using IPv6."));
4219 _cupsLangPuts(stderr
, _(" -C Send requests using "
4220 "chunking (default)."));
4221 _cupsLangPuts(stderr
, _(" -E Test with encryption using HTTP Upgrade to TLS."));
4222 _cupsLangPuts(stderr
, _(" -I Ignore errors."));
4223 _cupsLangPuts(stderr
, _(" -L Send requests using content-length."));
4224 _cupsLangPuts(stderr
, _(" -P filename.plist Produce XML plist to a file and test report to standard output."));
4225 _cupsLangPuts(stderr
, _(" -S Test with encryption using HTTPS."));
4226 _cupsLangPuts(stderr
, _(" -T seconds Set the receive/send timeout in seconds."));
4227 _cupsLangPuts(stderr
, _(" -V version Set default IPP version."));
4228 _cupsLangPuts(stderr
, _(" -X Produce XML plist instead of plain text."));
4229 _cupsLangPuts(stderr
, _(" -c Produce CSV output."));
4230 _cupsLangPuts(stderr
, _(" -d name=value Set named variable to value."));
4231 _cupsLangPuts(stderr
, _(" -f filename Set default request filename."));
4232 _cupsLangPuts(stderr
, _(" -h Validate HTTP response headers."));
4233 _cupsLangPuts(stderr
, _(" -i seconds Repeat the last file with the given time interval."));
4234 _cupsLangPuts(stderr
, _(" -l Produce plain text output."));
4235 _cupsLangPuts(stderr
, _(" -n count Repeat the last file the given number of times."));
4236 _cupsLangPuts(stderr
, _(" -q Run silently."));
4237 _cupsLangPuts(stderr
, _(" -t Produce a test report."));
4238 _cupsLangPuts(stderr
, _(" -v Be verbose."));
4245 * 'validate_attr()' - Determine whether an attribute is valid.
4248 static int /* O - 1 if valid, 0 otherwise */
4249 validate_attr(_cups_testdata_t
*data
, /* I - Test data */
4250 cups_array_t
*errors
, /* I - Errors array */
4251 ipp_attribute_t
*attr
) /* I - Attribute to validate */
4253 int i
; /* Looping var */
4254 char scheme
[64], /* Scheme from URI */
4255 userpass
[256], /* Username/password from URI */
4256 hostname
[256], /* Hostname from URI */
4257 resource
[1024]; /* Resource from URI */
4258 int port
, /* Port number from URI */
4259 uri_status
, /* URI separation status */
4260 valid
= 1; /* Is the attribute valid? */
4261 const char *ptr
; /* Pointer into string */
4262 ipp_attribute_t
*colattr
; /* Collection attribute */
4263 regex_t re
; /* Regular expression */
4264 ipp_uchar_t
*date
; /* Current date value */
4275 * Validate the attribute name.
4278 for (ptr
= attr
->name
; *ptr
; ptr
++)
4279 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' && *ptr
!= '_')
4282 if (*ptr
|| ptr
== attr
->name
)
4286 add_stringf(data
->errors
,
4287 "\"%s\": Bad attribute name - invalid character "
4288 "(RFC 2911 section 4.1.3).", attr
->name
);
4291 if ((ptr
- attr
->name
) > 255)
4295 add_stringf(data
->errors
,
4296 "\"%s\": Bad attribute name - bad length "
4297 "(RFC 2911 section 4.1.3).", attr
->name
);
4300 switch (attr
->value_tag
)
4302 case IPP_TAG_INTEGER
:
4305 case IPP_TAG_BOOLEAN
:
4306 for (i
= 0; i
< attr
->num_values
; i
++)
4308 if (attr
->values
[i
].boolean
!= 0 &&
4309 attr
->values
[i
].boolean
!= 1)
4313 add_stringf(data
->errors
,
4314 "\"%s\": Bad boolen value %d "
4315 "(RFC 2911 section 4.1.11).", attr
->name
,
4316 attr
->values
[i
].boolean
);
4322 for (i
= 0; i
< attr
->num_values
; i
++)
4324 if (attr
->values
[i
].integer
< 1)
4328 add_stringf(data
->errors
,
4329 "\"%s\": Bad enum value %d - out of range "
4330 "(RFC 2911 section 4.1.4).", attr
->name
,
4331 attr
->values
[i
].integer
);
4336 case IPP_TAG_STRING
:
4337 for (i
= 0; i
< attr
->num_values
; i
++)
4339 if (attr
->values
[i
].unknown
.length
> IPP_MAX_OCTETSTRING
)
4343 add_stringf(data
->errors
,
4344 "\"%s\": Bad octetString value - bad length %d "
4345 "(RFC 2911 section 4.1.10).", attr
->name
,
4346 attr
->values
[i
].unknown
.length
);
4352 for (i
= 0; i
< attr
->num_values
; i
++)
4354 date
= attr
->values
[i
].date
;
4356 if (date
[2] < 1 || date
[2] > 12)
4360 add_stringf(data
->errors
,
4361 "\"%s\": Bad dateTime month %u "
4362 "(RFC 2911 section 4.1.14).", attr
->name
, date
[2]);
4365 if (date
[3] < 1 || date
[3] > 31)
4369 add_stringf(data
->errors
,
4370 "\"%s\": Bad dateTime day %u "
4371 "(RFC 2911 section 4.1.14).", attr
->name
, date
[3]);
4378 add_stringf(data
->errors
,
4379 "\"%s\": Bad dateTime hours %u "
4380 "(RFC 2911 section 4.1.14).", attr
->name
, date
[4]);
4387 add_stringf(data
->errors
,
4388 "\"%s\": Bad dateTime minutes %u "
4389 "(RFC 2911 section 4.1.14).", attr
->name
, date
[5]);
4396 add_stringf(data
->errors
,
4397 "\"%s\": Bad dateTime seconds %u "
4398 "(RFC 2911 section 4.1.14).", attr
->name
, date
[6]);
4405 add_stringf(data
->errors
,
4406 "\"%s\": Bad dateTime deciseconds %u "
4407 "(RFC 2911 section 4.1.14).", attr
->name
, date
[7]);
4410 if (date
[8] != '-' && date
[8] != '+')
4414 add_stringf(data
->errors
,
4415 "\"%s\": Bad dateTime UTC sign '%c' "
4416 "(RFC 2911 section 4.1.14).", attr
->name
, date
[8]);
4423 add_stringf(data
->errors
,
4424 "\"%s\": Bad dateTime UTC hours %u "
4425 "(RFC 2911 section 4.1.14).", attr
->name
, date
[9]);
4432 add_stringf(data
->errors
,
4433 "\"%s\": Bad dateTime UTC minutes %u "
4434 "(RFC 2911 section 4.1.14).", attr
->name
, date
[10]);
4439 case IPP_TAG_RESOLUTION
:
4440 for (i
= 0; i
< attr
->num_values
; i
++)
4442 if (attr
->values
[i
].resolution
.xres
<= 0)
4446 add_stringf(data
->errors
,
4447 "\"%s\": Bad resolution value %dx%d%s - cross "
4448 "feed resolution must be positive "
4449 "(RFC 2911 section 4.1.15).", attr
->name
,
4450 attr
->values
[i
].resolution
.xres
,
4451 attr
->values
[i
].resolution
.yres
,
4452 attr
->values
[i
].resolution
.units
==
4453 IPP_RES_PER_INCH
? "dpi" :
4454 attr
->values
[i
].resolution
.units
==
4455 IPP_RES_PER_CM
? "dpcm" : "unknown");
4458 if (attr
->values
[i
].resolution
.yres
<= 0)
4462 add_stringf(data
->errors
,
4463 "\"%s\": Bad resolution value %dx%d%s - feed "
4464 "resolution must be positive "
4465 "(RFC 2911 section 4.1.15).", attr
->name
,
4466 attr
->values
[i
].resolution
.xres
,
4467 attr
->values
[i
].resolution
.yres
,
4468 attr
->values
[i
].resolution
.units
==
4469 IPP_RES_PER_INCH
? "dpi" :
4470 attr
->values
[i
].resolution
.units
==
4471 IPP_RES_PER_CM
? "dpcm" : "unknown");
4474 if (attr
->values
[i
].resolution
.units
!= IPP_RES_PER_INCH
&&
4475 attr
->values
[i
].resolution
.units
!= IPP_RES_PER_CM
)
4479 add_stringf(data
->errors
,
4480 "\"%s\": Bad resolution value %dx%d%s - bad "
4481 "units value (RFC 2911 section 4.1.15).",
4482 attr
->name
, attr
->values
[i
].resolution
.xres
,
4483 attr
->values
[i
].resolution
.yres
,
4484 attr
->values
[i
].resolution
.units
==
4485 IPP_RES_PER_INCH
? "dpi" :
4486 attr
->values
[i
].resolution
.units
==
4487 IPP_RES_PER_CM
? "dpcm" : "unknown");
4492 case IPP_TAG_RANGE
:
4493 for (i
= 0; i
< attr
->num_values
; i
++)
4495 if (attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
4499 add_stringf(data
->errors
,
4500 "\"%s\": Bad rangeOfInteger value %d-%d - lower "
4501 "greater than upper (RFC 2911 section 4.1.13).",
4502 attr
->name
, attr
->values
[i
].range
.lower
,
4503 attr
->values
[i
].range
.upper
);
4508 case IPP_TAG_BEGIN_COLLECTION
:
4509 for (i
= 0; i
< attr
->num_values
; i
++)
4511 for (colattr
= attr
->values
[i
].collection
->attrs
;
4513 colattr
= colattr
->next
)
4515 if (!validate_attr(data
, NULL
, colattr
))
4522 if (colattr
&& errors
)
4524 add_stringf(data
->errors
, "\"%s\": Bad collection value.", attr
->name
);
4528 validate_attr(data
, errors
, colattr
);
4529 colattr
= colattr
->next
;
4536 case IPP_TAG_TEXTLANG
:
4537 for (i
= 0; i
< attr
->num_values
; i
++)
4539 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4541 if ((*ptr
& 0xe0) == 0xc0)
4544 if ((*ptr
& 0xc0) != 0x80)
4547 else if ((*ptr
& 0xf0) == 0xe0)
4550 if ((*ptr
& 0xc0) != 0x80)
4553 if ((*ptr
& 0xc0) != 0x80)
4556 else if ((*ptr
& 0xf8) == 0xf0)
4559 if ((*ptr
& 0xc0) != 0x80)
4562 if ((*ptr
& 0xc0) != 0x80)
4565 if ((*ptr
& 0xc0) != 0x80)
4568 else if (*ptr
& 0x80)
4576 add_stringf(data
->errors
,
4577 "\"%s\": Bad text value \"%s\" - bad UTF-8 "
4578 "sequence (RFC 2911 section 4.1.1).", attr
->name
,
4579 attr
->values
[i
].string
.text
);
4582 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_TEXT
- 1))
4586 add_stringf(data
->errors
,
4587 "\"%s\": Bad text value \"%s\" - bad length %d "
4588 "(RFC 2911 section 4.1.1).", attr
->name
,
4589 attr
->values
[i
].string
.text
,
4590 (int)strlen(attr
->values
[i
].string
.text
));
4596 case IPP_TAG_NAMELANG
:
4597 for (i
= 0; i
< attr
->num_values
; i
++)
4599 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4601 if ((*ptr
& 0xe0) == 0xc0)
4604 if ((*ptr
& 0xc0) != 0x80)
4607 else if ((*ptr
& 0xf0) == 0xe0)
4610 if ((*ptr
& 0xc0) != 0x80)
4613 if ((*ptr
& 0xc0) != 0x80)
4616 else if ((*ptr
& 0xf8) == 0xf0)
4619 if ((*ptr
& 0xc0) != 0x80)
4622 if ((*ptr
& 0xc0) != 0x80)
4625 if ((*ptr
& 0xc0) != 0x80)
4628 else if (*ptr
& 0x80)
4636 add_stringf(data
->errors
,
4637 "\"%s\": Bad name value \"%s\" - bad UTF-8 "
4638 "sequence (RFC 2911 section 4.1.2).", attr
->name
,
4639 attr
->values
[i
].string
.text
);
4642 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_NAME
- 1))
4646 add_stringf(data
->errors
,
4647 "\"%s\": Bad name value \"%s\" - bad length %d "
4648 "(RFC 2911 section 4.1.2).", attr
->name
,
4649 attr
->values
[i
].string
.text
,
4650 (int)strlen(attr
->values
[i
].string
.text
));
4655 case IPP_TAG_KEYWORD
:
4656 for (i
= 0; i
< attr
->num_values
; i
++)
4658 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4659 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' &&
4663 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
4667 add_stringf(data
->errors
,
4668 "\"%s\": Bad keyword value \"%s\" - invalid "
4669 "character (RFC 2911 section 4.1.3).",
4670 attr
->name
, attr
->values
[i
].string
.text
);
4673 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_KEYWORD
- 1))
4677 add_stringf(data
->errors
,
4678 "\"%s\": Bad keyword value \"%s\" - bad "
4679 "length %d (RFC 2911 section 4.1.3).",
4680 attr
->name
, attr
->values
[i
].string
.text
,
4681 (int)strlen(attr
->values
[i
].string
.text
));
4687 for (i
= 0; i
< attr
->num_values
; i
++)
4689 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
,
4690 attr
->values
[i
].string
.text
,
4691 scheme
, sizeof(scheme
),
4692 userpass
, sizeof(userpass
),
4693 hostname
, sizeof(hostname
),
4694 &port
, resource
, sizeof(resource
));
4696 if (uri_status
< HTTP_URI_OK
)
4700 add_stringf(data
->errors
,
4701 "\"%s\": Bad URI value \"%s\" - %s "
4702 "(RFC 2911 section 4.1.5).", attr
->name
,
4703 attr
->values
[i
].string
.text
,
4704 httpURIStatusString(uri_status
));
4707 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_URI
- 1))
4711 add_stringf(data
->errors
,
4712 "\"%s\": Bad URI value \"%s\" - bad length %d "
4713 "(RFC 2911 section 4.1.5).", attr
->name
,
4714 attr
->values
[i
].string
.text
,
4715 (int)strlen(attr
->values
[i
].string
.text
));
4720 case IPP_TAG_URISCHEME
:
4721 for (i
= 0; i
< attr
->num_values
; i
++)
4723 ptr
= attr
->values
[i
].string
.text
;
4724 if (islower(*ptr
& 255))
4726 for (ptr
++; *ptr
; ptr
++)
4727 if (!islower(*ptr
& 255) && !isdigit(*ptr
& 255) &&
4728 *ptr
!= '+' && *ptr
!= '-' && *ptr
!= '.')
4732 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
4736 add_stringf(data
->errors
,
4737 "\"%s\": Bad uriScheme value \"%s\" - bad "
4738 "characters (RFC 2911 section 4.1.6).",
4739 attr
->name
, attr
->values
[i
].string
.text
);
4742 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_URISCHEME
- 1))
4746 add_stringf(data
->errors
,
4747 "\"%s\": Bad uriScheme value \"%s\" - bad "
4748 "length %d (RFC 2911 section 4.1.6).",
4749 attr
->name
, attr
->values
[i
].string
.text
,
4750 (int)strlen(attr
->values
[i
].string
.text
));
4755 case IPP_TAG_CHARSET
:
4756 for (i
= 0; i
< attr
->num_values
; i
++)
4758 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4759 if (!isprint(*ptr
& 255) || isupper(*ptr
& 255) ||
4760 isspace(*ptr
& 255))
4763 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
4767 add_stringf(data
->errors
,
4768 "\"%s\": Bad charset value \"%s\" - bad "
4769 "characters (RFC 2911 section 4.1.7).",
4770 attr
->name
, attr
->values
[i
].string
.text
);
4773 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_CHARSET
- 1))
4777 add_stringf(data
->errors
,
4778 "\"%s\": Bad charset value \"%s\" - bad "
4779 "length %d (RFC 2911 section 4.1.7).",
4780 attr
->name
, attr
->values
[i
].string
.text
,
4781 (int)strlen(attr
->values
[i
].string
.text
));
4786 case IPP_TAG_LANGUAGE
:
4788 * The following regular expression is derived from the ABNF for
4789 * language tags in RFC 4646. All I can say is that this is the
4790 * easiest way to check the values...
4793 if ((i
= regcomp(&re
,
4795 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
4797 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
4798 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
4799 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
4800 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
4801 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
4803 "x(-[a-z0-9]{1,8})+" /* privateuse */
4805 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
4807 REG_NOSUB
| REG_EXTENDED
)) != 0)
4809 char temp
[256]; /* Temporary error string */
4811 regerror(i
, &re
, temp
, sizeof(temp
));
4812 print_fatal_error(data
, "Unable to compile naturalLanguage regular "
4813 "expression: %s.", temp
);
4817 for (i
= 0; i
< attr
->num_values
; i
++)
4819 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
4823 add_stringf(data
->errors
,
4824 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
4825 "characters (RFC 2911 section 4.1.8).",
4826 attr
->name
, attr
->values
[i
].string
.text
);
4829 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_LANGUAGE
- 1))
4833 add_stringf(data
->errors
,
4834 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
4835 "length %d (RFC 2911 section 4.1.8).",
4836 attr
->name
, attr
->values
[i
].string
.text
,
4837 (int)strlen(attr
->values
[i
].string
.text
));
4844 case IPP_TAG_MIMETYPE
:
4846 * The following regular expression is derived from the ABNF for
4847 * language tags in RFC 2045 and 4288. All I can say is that this is
4848 * the easiest way to check the values...
4851 if ((i
= regcomp(&re
,
4853 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
4855 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
4856 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
4857 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
4860 REG_NOSUB
| REG_EXTENDED
)) != 0)
4862 char temp
[256]; /* Temporary error string */
4864 regerror(i
, &re
, temp
, sizeof(temp
));
4865 print_fatal_error(data
, "Unable to compile mimeMediaType regular "
4866 "expression: %s.", temp
);
4870 for (i
= 0; i
< attr
->num_values
; i
++)
4872 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
4876 add_stringf(data
->errors
,
4877 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
4878 "characters (RFC 2911 section 4.1.9).",
4879 attr
->name
, attr
->values
[i
].string
.text
);
4882 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_MIMETYPE
- 1))
4886 add_stringf(data
->errors
,
4887 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
4888 "length %d (RFC 2911 section 4.1.9).",
4889 attr
->name
, attr
->values
[i
].string
.text
,
4890 (int)strlen(attr
->values
[i
].string
.text
));
4906 * 'with_flags_string()' - Return the "WITH-xxx" predicate that corresponds to
4910 static const char * /* O - WITH-xxx string */
4911 with_flags_string(int flags
) /* I - WITH flags */
4913 if (flags
& _CUPS_WITH_ALL
)
4915 if (flags
& _CUPS_WITH_HOSTNAME
)
4916 return ("WITH-ALL-HOSTNAMES");
4917 else if (flags
& _CUPS_WITH_RESOURCE
)
4918 return ("WITH-ALL-RESOURCES");
4919 else if (flags
& _CUPS_WITH_SCHEME
)
4920 return ("WITH-ALL-SCHEMES");
4922 return ("WITH-ALL-VALUES");
4924 else if (flags
& _CUPS_WITH_HOSTNAME
)
4925 return ("WITH-HOSTNAME");
4926 else if (flags
& _CUPS_WITH_RESOURCE
)
4927 return ("WITH-RESOURCE");
4928 else if (flags
& _CUPS_WITH_SCHEME
)
4929 return ("WITH-SCHEME");
4931 return ("WITH-VALUE");
4936 * 'with_value()' - Test a WITH-VALUE predicate.
4939 static int /* O - 1 on match, 0 on non-match */
4940 with_value(_cups_testdata_t
*data
, /* I - Test data */
4941 cups_array_t
*errors
, /* I - Errors array */
4942 char *value
, /* I - Value string */
4943 int flags
, /* I - Flags for match */
4944 ipp_attribute_t
*attr
, /* I - Attribute to compare */
4945 char *matchbuf
, /* I - Buffer to hold matching value */
4946 size_t matchlen
) /* I - Length of match buffer */
4948 int i
, /* Looping var */
4950 char temp
[1024], /* Temporary value string */
4951 *valptr
; /* Pointer into value */
4955 match
= (flags
& _CUPS_WITH_ALL
) ? 1 : 0;
4958 * NULL matches everything.
4961 if (!value
|| !*value
)
4965 * Compare the value string to the attribute value.
4968 switch (attr
->value_tag
)
4970 case IPP_TAG_INTEGER
:
4972 for (i
= 0; i
< attr
->num_values
; i
++)
4974 char op
, /* Comparison operator */
4975 *nextptr
; /* Next pointer */
4976 int intvalue
, /* Integer value */
4977 valmatch
= 0; /* Does the current value match? */
4981 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
4982 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
4983 *valptr
== '=' || *valptr
== '>')
4986 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
4988 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
4996 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
4997 if (nextptr
== valptr
)
5001 if ((op
== '=' && attr
->values
[i
].integer
== intvalue
) ||
5002 (op
== '<' && attr
->values
[i
].integer
< intvalue
) ||
5003 (op
== '>' && attr
->values
[i
].integer
> intvalue
))
5006 snprintf(matchbuf
, matchlen
, "%d", attr
->values
[i
].integer
);
5013 if (flags
& _CUPS_WITH_ALL
)
5028 if (!match
&& errors
)
5030 for (i
= 0; i
< attr
->num_values
; i
++)
5031 add_stringf(data
->errors
, "GOT: %s=%d", attr
->name
,
5032 attr
->values
[i
].integer
);
5036 case IPP_TAG_RANGE
:
5037 for (i
= 0; i
< attr
->num_values
; i
++)
5039 char op
, /* Comparison operator */
5040 *nextptr
; /* Next pointer */
5041 int intvalue
, /* Integer value */
5042 valmatch
= 0; /* Does the current value match? */
5046 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5047 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5048 *valptr
== '=' || *valptr
== '>')
5051 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5053 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5061 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
5062 if (nextptr
== valptr
)
5066 if ((op
== '=' && (attr
->values
[i
].range
.lower
== intvalue
||
5067 attr
->values
[i
].range
.upper
== intvalue
)) ||
5068 (op
== '<' && attr
->values
[i
].range
.upper
< intvalue
) ||
5069 (op
== '>' && attr
->values
[i
].range
.upper
> intvalue
))
5072 snprintf(matchbuf
, matchlen
, "%d-%d",
5073 attr
->values
[0].range
.lower
,
5074 attr
->values
[0].range
.upper
);
5081 if (flags
& _CUPS_WITH_ALL
)
5096 if (!match
&& errors
)
5098 for (i
= 0; i
< attr
->num_values
; i
++)
5099 add_stringf(data
->errors
, "GOT: %s=%d-%d", attr
->name
,
5100 attr
->values
[i
].range
.lower
,
5101 attr
->values
[i
].range
.upper
);
5105 case IPP_TAG_BOOLEAN
:
5106 for (i
= 0; i
< attr
->num_values
; i
++)
5108 if ((!strcmp(value
, "true")) == attr
->values
[i
].boolean
)
5111 strlcpy(matchbuf
, value
, matchlen
);
5113 if (!(flags
& _CUPS_WITH_ALL
))
5119 else if (flags
& _CUPS_WITH_ALL
)
5126 if (!match
&& errors
)
5128 for (i
= 0; i
< attr
->num_values
; i
++)
5129 add_stringf(data
->errors
, "GOT: %s=%s", attr
->name
,
5130 attr
->values
[i
].boolean
? "true" : "false");
5134 case IPP_TAG_RESOLUTION
:
5135 for (i
= 0; i
< attr
->num_values
; i
++)
5137 if (attr
->values
[i
].resolution
.xres
==
5138 attr
->values
[i
].resolution
.yres
)
5139 snprintf(temp
, sizeof(temp
), "%d%s",
5140 attr
->values
[i
].resolution
.xres
,
5141 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5144 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5145 attr
->values
[i
].resolution
.xres
,
5146 attr
->values
[i
].resolution
.yres
,
5147 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5150 if (!strcmp(value
, temp
))
5153 strlcpy(matchbuf
, value
, matchlen
);
5155 if (!(flags
& _CUPS_WITH_ALL
))
5161 else if (flags
& _CUPS_WITH_ALL
)
5168 if (!match
&& errors
)
5170 for (i
= 0; i
< attr
->num_values
; i
++)
5172 if (attr
->values
[i
].resolution
.xres
==
5173 attr
->values
[i
].resolution
.yres
)
5174 snprintf(temp
, sizeof(temp
), "%d%s",
5175 attr
->values
[i
].resolution
.xres
,
5176 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5179 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5180 attr
->values
[i
].resolution
.xres
,
5181 attr
->values
[i
].resolution
.yres
,
5182 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5185 if (strcmp(value
, temp
))
5186 add_stringf(data
->errors
, "GOT: %s=%s", attr
->name
, temp
);
5191 case IPP_TAG_NOVALUE
:
5192 case IPP_TAG_UNKNOWN
:
5195 case IPP_TAG_CHARSET
:
5196 case IPP_TAG_KEYWORD
:
5197 case IPP_TAG_LANGUAGE
:
5198 case IPP_TAG_MIMETYPE
:
5200 case IPP_TAG_NAMELANG
:
5202 case IPP_TAG_TEXTLANG
:
5204 case IPP_TAG_URISCHEME
:
5205 if (flags
& _CUPS_WITH_REGEX
)
5208 * Value is an extended, case-sensitive POSIX regular expression...
5211 regex_t re
; /* Regular expression */
5213 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
5215 regerror(i
, &re
, temp
, sizeof(temp
));
5217 print_fatal_error(data
, "Unable to compile WITH-VALUE regular expression "
5218 "\"%s\" - %s", value
, temp
);
5223 * See if ALL of the values match the given regular expression.
5226 for (i
= 0; i
< attr
->num_values
; i
++)
5228 if (!regexec(&re
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5233 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5236 if (!(flags
& _CUPS_WITH_ALL
))
5242 else if (flags
& _CUPS_WITH_ALL
)
5251 else if (ippGetValueTag(attr
) == IPP_TAG_URI
&& !(flags
& (_CUPS_WITH_SCHEME
| _CUPS_WITH_HOSTNAME
| _CUPS_WITH_RESOURCE
)))
5254 * Value is a literal URI string, see if the value(s) match...
5257 for (i
= 0; i
< attr
->num_values
; i
++)
5259 if (!compare_uris(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
))))
5263 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5266 if (!(flags
& _CUPS_WITH_ALL
))
5272 else if (flags
& _CUPS_WITH_ALL
)
5282 * Value is a literal string, see if the value(s) match...
5285 for (i
= 0; i
< attr
->num_values
; i
++)
5289 switch (ippGetValueTag(attr
))
5293 * Some URI components are case-sensitive, some not...
5296 if (flags
& (_CUPS_WITH_SCHEME
| _CUPS_WITH_HOSTNAME
))
5297 result
= _cups_strcasecmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
5299 result
= strcmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
5302 case IPP_TAG_MIMETYPE
:
5304 case IPP_TAG_NAMELANG
:
5306 case IPP_TAG_TEXTLANG
:
5308 * mimeMediaType, nameWithoutLanguage, nameWithLanguage,
5309 * textWithoutLanguage, and textWithLanguage are defined to
5310 * be case-insensitive strings...
5313 result
= _cups_strcasecmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
5318 * Other string syntaxes are defined as lowercased so we use
5319 * case-sensitive comparisons to catch problems...
5322 result
= strcmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
5330 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5333 if (!(flags
& _CUPS_WITH_ALL
))
5339 else if (flags
& _CUPS_WITH_ALL
)
5347 if (!match
&& errors
)
5349 for (i
= 0; i
< attr
->num_values
; i
++)
5350 add_stringf(data
->errors
, "GOT: %s=\"%s\"", attr
->name
,
5351 attr
->values
[i
].string
.text
);
5364 * 'with_value_from()' - Test a WITH-VALUE-FROM predicate.
5367 static int /* O - 1 on match, 0 on non-match */
5369 cups_array_t
*errors
, /* I - Errors array */
5370 ipp_attribute_t
*fromattr
, /* I - "From" attribute */
5371 ipp_attribute_t
*attr
, /* I - Attribute to compare */
5372 char *matchbuf
, /* I - Buffer to hold matching value */
5373 size_t matchlen
) /* I - Length of match buffer */
5375 int i
, j
, /* Looping vars */
5376 count
= ippGetCount(attr
), /* Number of attribute values */
5377 match
= 1; /* Match? */
5383 * Compare the from value(s) to the attribute value(s)...
5386 switch (ippGetValueTag(attr
))
5388 case IPP_TAG_INTEGER
:
5389 if (ippGetValueTag(fromattr
) != IPP_TAG_INTEGER
&& ippGetValueTag(fromattr
) != IPP_TAG_RANGE
)
5390 goto wrong_value_tag
;
5392 for (i
= 0; i
< count
; i
++)
5394 int value
= ippGetInteger(attr
, i
);
5395 /* Current integer value */
5397 if (ippContainsInteger(fromattr
, value
))
5400 snprintf(matchbuf
, matchlen
, "%d", value
);
5404 add_stringf(errors
, "GOT: %s=%d", ippGetName(attr
), value
);
5411 if (ippGetValueTag(fromattr
) != IPP_TAG_ENUM
)
5412 goto wrong_value_tag
;
5414 for (i
= 0; i
< count
; i
++)
5416 int value
= ippGetInteger(attr
, i
);
5417 /* Current integer value */
5419 if (ippContainsInteger(fromattr
, value
))
5422 snprintf(matchbuf
, matchlen
, "%d", value
);
5426 add_stringf(errors
, "GOT: %s=%d", ippGetName(attr
), value
);
5432 case IPP_TAG_RESOLUTION
:
5433 if (ippGetValueTag(fromattr
) != IPP_TAG_RESOLUTION
)
5434 goto wrong_value_tag
;
5436 for (i
= 0; i
< count
; i
++)
5440 int fromcount
= ippGetCount(fromattr
);
5441 int fromxres
, fromyres
;
5442 ipp_res_t fromunits
;
5444 xres
= ippGetResolution(attr
, i
, &yres
, &units
);
5446 for (j
= 0; j
< fromcount
; j
++)
5448 fromxres
= ippGetResolution(fromattr
, j
, &fromyres
, &fromunits
);
5449 if (fromxres
== xres
&& fromyres
== yres
&& fromunits
== units
)
5458 snprintf(matchbuf
, matchlen
, "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5460 snprintf(matchbuf
, matchlen
, "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5466 add_stringf(errors
, "GOT: %s=%d%s", ippGetName(attr
), xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5468 add_stringf(errors
, "GOT: %s=%dx%d%s", ippGetName(attr
), xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5475 case IPP_TAG_NOVALUE
:
5476 case IPP_TAG_UNKNOWN
:
5479 case IPP_TAG_CHARSET
:
5480 case IPP_TAG_KEYWORD
:
5481 case IPP_TAG_LANGUAGE
:
5482 case IPP_TAG_MIMETYPE
:
5484 case IPP_TAG_NAMELANG
:
5486 case IPP_TAG_TEXTLANG
:
5487 case IPP_TAG_URISCHEME
:
5488 for (i
= 0; i
< count
; i
++)
5490 const char *value
= ippGetString(attr
, i
, NULL
);
5491 /* Current string value */
5493 if (ippContainsString(fromattr
, value
))
5496 strlcpy(matchbuf
, value
, matchlen
);
5500 add_stringf(errors
, "GOT: %s='%s'", ippGetName(attr
), value
);
5507 for (i
= 0; i
< count
; i
++)
5509 const char *value
= ippGetString(attr
, i
, NULL
);
5510 /* Current string value */
5511 int fromcount
= ippGetCount(fromattr
);
5513 for (j
= 0; j
< fromcount
; j
++)
5515 if (!compare_uris(value
, ippGetString(fromattr
, j
, NULL
)))
5518 strlcpy(matchbuf
, value
, matchlen
);
5525 add_stringf(errors
, "GOT: %s='%s'", ippGetName(attr
), value
);
5538 /* value tag mismatch between fromattr and attr */
5541 add_stringf(errors
, "GOT: %s OF-TYPE %s", ippGetName(attr
), ippTagString(ippGetValueTag(attr
)));