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 #define _IPP_PRIVATE_STRUCTURES 0 /* Disable private IPP stuff */
16 #include <cups/cups-private.h>
17 #include <cups/file-private.h>
31 #endif /* !O_BINARY */
38 typedef enum _cups_transfer_e
/**** How to send request data ****/
40 _CUPS_TRANSFER_AUTO
, /* Chunk for files, length for static */
41 _CUPS_TRANSFER_CHUNKED
, /* Chunk always */
42 _CUPS_TRANSFER_LENGTH
/* Length always */
45 typedef enum _cups_output_e
/**** Output mode ****/
47 _CUPS_OUTPUT_QUIET
, /* No output */
48 _CUPS_OUTPUT_TEST
, /* Traditional CUPS test output */
49 _CUPS_OUTPUT_PLIST
, /* XML plist test output */
50 _CUPS_OUTPUT_IPPSERVER
, /* ippserver attribute file output */
51 _CUPS_OUTPUT_LIST
, /* Tabular list output */
52 _CUPS_OUTPUT_CSV
/* Comma-separated values output */
55 typedef enum _cups_with_e
/**** WITH flags ****/
57 _CUPS_WITH_LITERAL
= 0, /* Match string is a literal value */
58 _CUPS_WITH_ALL
= 1, /* Must match all values */
59 _CUPS_WITH_REGEX
= 2, /* Match string is a regular expression */
60 _CUPS_WITH_HOSTNAME
= 4, /* Match string is a URI hostname */
61 _CUPS_WITH_RESOURCE
= 8, /* Match string is a URI resource */
62 _CUPS_WITH_SCHEME
= 16 /* Match string is a URI scheme */
65 typedef struct _cups_expect_s
/**** Expected attribute info ****/
67 int optional
, /* Optional attribute? */
68 not_expect
, /* Don't expect attribute? */
69 expect_all
; /* Expect all attributes to match/not match */
70 char *name
, /* Attribute name */
71 *of_type
, /* Type name */
72 *same_count_as
, /* Parallel attribute name */
73 *if_defined
, /* Only required if variable defined */
74 *if_not_defined
, /* Only required if variable is not defined */
75 *with_value
, /* Attribute must include this value */
76 *with_value_from
, /* Attribute must have one of the values in this attribute */
77 *define_match
, /* Variable to define on match */
78 *define_no_match
, /* Variable to define on no-match */
79 *define_value
; /* Variable to define with value */
80 int repeat_limit
, /* Maximum number of times to repeat */
81 repeat_match
, /* Repeat test on match */
82 repeat_no_match
, /* Repeat test on no match */
83 with_flags
, /* WITH flags */
84 count
; /* Expected count if > 0 */
85 ipp_tag_t in_group
; /* IN-GROUP value */
88 typedef struct _cups_status_s
/**** Status info ****/
90 ipp_status_t status
; /* Expected status code */
91 char *if_defined
, /* Only if variable is defined */
92 *if_not_defined
, /* Only if variable is not defined */
93 *define_match
, /* Variable to define on match */
94 *define_no_match
, /* Variable to define on no-match */
95 *define_value
; /* Variable to define with value */
96 int repeat_limit
, /* Maximum number of times to repeat */
97 repeat_match
, /* Repeat the test when it does not match */
98 repeat_no_match
; /* Repeat the test when it matches */
101 typedef struct _cups_testdata_s
/**** Test Data ****/
104 http_encryption_t encryption
; /* Encryption for connection */
105 int family
; /* Address family */
106 _cups_output_t output
; /* Output mode */
107 int stop_after_include_error
;
108 /* Stop after include errors? */
109 double timeout
; /* Timeout for connection */
110 int validate_headers
, /* Validate HTTP headers in response? */
111 verbosity
; /* Show all attributes? */
114 int def_ignore_errors
; /* Default IGNORE-ERRORS value */
115 _cups_transfer_t def_transfer
; /* Default TRANSFER value */
116 int def_version
; /* Default IPP version */
119 http_t
*http
; /* HTTP connection to printer/server */
120 cups_file_t
*outfile
; /* Output file */
121 int show_header
, /* Show the test header? */
122 xml_header
; /* 1 if XML plist header was written */
123 int pass
, /* Have we passed all tests? */
124 test_count
, /* Number of tests (total) */
125 pass_count
, /* Number of tests that passed */
126 fail_count
, /* Number of tests that failed */
127 skip_count
; /* Number of tests that were skipped */
130 cups_array_t
*errors
; /* Errors array */
131 int prev_pass
, /* Result of previous test */
132 skip_previous
; /* Skip on previous test failure? */
133 char compression
[16]; /* COMPRESSION value */
134 useconds_t delay
; /* Initial delay */
135 int num_displayed
; /* Number of displayed attributes */
136 char *displayed
[200]; /* Displayed attributes */
137 int num_expects
; /* Number of expected attributes */
138 _cups_expect_t expects
[200], /* Expected attributes */
139 *expect
, /* Current expected attribute */
140 *last_expect
; /* Last EXPECT (for predicates) */
141 char file
[1024], /* Data filename */
142 file_id
[1024]; /* File identifier */
143 int ignore_errors
; /* Ignore test failures? */
144 char name
[1024]; /* Test name */
145 useconds_t repeat_interval
; /* Repeat interval (delay) */
146 int request_id
; /* Current request ID */
147 char resource
[512]; /* Resource for request */
148 int skip_test
, /* Skip this test? */
149 num_statuses
; /* Number of valid status codes */
150 _cups_status_t statuses
[100], /* Valid status codes */
151 *last_status
; /* Last STATUS (for predicates) */
152 char test_id
[1024]; /* Test identifier */
153 _cups_transfer_t transfer
; /* To chunk or not to chunk */
154 int version
; /* IPP version number to use */
162 static int Cancel
= 0; /* Cancel test? */
169 static void add_stringf(cups_array_t
*a
, const char *s
, ...) __attribute__ ((__format__ (__printf__
, 2, 3)));
170 static int compare_uris(const char *a
, const char *b
);
171 static int do_test(_ipp_file_t
*f
, _ipp_vars_t
*vars
, _cups_testdata_t
*data
);
172 static int do_tests(const char *testfile
, _ipp_vars_t
*vars
, _cups_testdata_t
*data
);
173 static int error_cb(_ipp_file_t
*f
, _cups_testdata_t
*data
, const char *error
);
174 static int expect_matches(_cups_expect_t
*expect
, ipp_tag_t value_tag
);
175 static char *get_filename(const char *testfile
, char *dst
, const char *src
, size_t dstsize
);
176 static const char *get_string(ipp_attribute_t
*attr
, int element
, int flags
, char *buffer
, size_t bufsize
);
177 static void init_data(_cups_testdata_t
*data
);
178 static char *iso_date(const ipp_uchar_t
*date
);
179 static void pause_message(const char *message
);
180 static void print_attr(cups_file_t
*outfile
, int output
, ipp_attribute_t
*attr
, ipp_tag_t
*group
);
181 static void print_csv(_cups_testdata_t
*data
, ipp_t
*ipp
, ipp_attribute_t
*attr
, int num_displayed
, char **displayed
, size_t *widths
);
182 static void print_fatal_error(_cups_testdata_t
*data
, const char *s
, ...) __attribute__ ((__format__ (__printf__
, 2, 3)));
183 static void print_ippserver_attr(_cups_testdata_t
*data
, ipp_attribute_t
*attr
, int indent
);
184 static void print_ippserver_string(_cups_testdata_t
*data
, const char *s
, size_t len
);
185 static void print_line(_cups_testdata_t
*data
, ipp_t
*ipp
, ipp_attribute_t
*attr
, int num_displayed
, char **displayed
, size_t *widths
);
186 static void print_xml_header(_cups_testdata_t
*data
);
187 static void print_xml_string(cups_file_t
*outfile
, const char *element
, const char *s
);
188 static void print_xml_trailer(_cups_testdata_t
*data
, int success
, const char *message
);
190 static void sigterm_handler(int sig
);
192 static int timeout_cb(http_t
*http
, void *user_data
);
193 static int token_cb(_ipp_file_t
*f
, _ipp_vars_t
*vars
, _cups_testdata_t
*data
, const char *token
);
194 static void usage(void) __attribute__((noreturn
));
195 static int validate_attr(_cups_testdata_t
*data
, cups_array_t
*errors
, ipp_attribute_t
*attr
);
196 static const char *with_flags_string(int flags
);
197 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
);
198 static int with_value_from(cups_array_t
*errors
, ipp_attribute_t
*fromattr
, ipp_attribute_t
*attr
, char *matchbuf
, size_t matchlen
);
202 * 'main()' - Parse options and do tests.
205 int /* O - Exit status */
206 main(int argc
, /* I - Number of command-line args */
207 char *argv
[]) /* I - Command-line arguments */
209 int i
; /* Looping var */
210 int status
; /* Status of tests... */
211 char *opt
, /* Current option */
212 name
[1024], /* Name/value buffer */
213 *value
, /* Pointer to value */
214 filename
[1024], /* Real filename */
215 testname
[1024]; /* Real test filename */
216 const char *ext
, /* Extension on filename */
217 *testfile
; /* Test file to use */
218 int interval
, /* Test interval in microseconds */
219 repeat
; /* Repeat count */
220 _cups_testdata_t data
; /* Test data */
221 _ipp_vars_t vars
; /* Variables */
222 _cups_globals_t
*cg
= _cupsGlobals();
228 * Catch SIGINT and SIGTERM...
231 signal(SIGINT
, sigterm_handler
);
232 signal(SIGTERM
, sigterm_handler
);
236 * Initialize the locale and variables...
239 _cupsSetLocale(argv
);
248 * ipptool URI testfile
256 for (i
= 1; i
< argc
; i
++)
258 if (!strcmp(argv
[i
], "--help"))
262 else if (!strcmp(argv
[i
], "--ippserver"))
268 _cupsLangPuts(stderr
, _("ipptool: Missing filename for \"--ippserver\"."));
272 if (data
.outfile
!= cupsFileStdout())
275 if ((data
.outfile
= cupsFileOpen(argv
[i
], "w")) == NULL
)
277 _cupsLangPrintf(stderr
, _("%s: Unable to open \"%s\": %s"), "ipptool", argv
[i
], strerror(errno
));
281 data
.output
= _CUPS_OUTPUT_IPPSERVER
;
283 else if (!strcmp(argv
[i
], "--stop-after-include-error"))
285 data
.stop_after_include_error
= 1;
287 else if (!strcmp(argv
[i
], "--version"))
292 else if (argv
[i
][0] == '-')
294 for (opt
= argv
[i
] + 1; *opt
; opt
++)
298 case '4' : /* Connect using IPv4 only */
299 data
.family
= AF_INET
;
303 case '6' : /* Connect using IPv6 only */
304 data
.family
= AF_INET6
;
306 #endif /* AF_INET6 */
308 case 'C' : /* Enable HTTP chunking */
309 data
.def_transfer
= _CUPS_TRANSFER_CHUNKED
;
312 case 'E' : /* Encrypt with TLS */
314 data
.encryption
= HTTP_ENCRYPT_REQUIRED
;
316 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
318 #endif /* HAVE_SSL */
321 case 'I' : /* Ignore errors */
322 data
.def_ignore_errors
= 1;
325 case 'L' : /* Disable HTTP chunking */
326 data
.def_transfer
= _CUPS_TRANSFER_LENGTH
;
329 case 'P' : /* Output to plist file */
334 _cupsLangPrintf(stderr
, _("%s: Missing filename for \"-P\"."), "ipptool");
338 if (data
.outfile
!= cupsFileStdout())
341 if ((data
.outfile
= cupsFileOpen(argv
[i
], "w")) == NULL
)
343 _cupsLangPrintf(stderr
, _("%s: Unable to open \"%s\": %s"), "ipptool", argv
[i
], strerror(errno
));
347 data
.output
= _CUPS_OUTPUT_PLIST
;
349 if (interval
|| repeat
)
351 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
356 case 'S' : /* Encrypt with SSL */
358 data
.encryption
= HTTP_ENCRYPT_ALWAYS
;
360 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
362 #endif /* HAVE_SSL */
365 case 'T' : /* Set timeout */
370 _cupsLangPrintf(stderr
,
371 _("%s: Missing timeout for \"-T\"."),
376 data
.timeout
= _cupsStrScand(argv
[i
], NULL
, localeconv());
379 case 'V' : /* Set IPP version */
384 _cupsLangPrintf(stderr
,
385 _("%s: Missing version for \"-V\"."),
390 if (!strcmp(argv
[i
], "1.0"))
392 data
.def_version
= 10;
394 else if (!strcmp(argv
[i
], "1.1"))
396 data
.def_version
= 11;
398 else if (!strcmp(argv
[i
], "2.0"))
400 data
.def_version
= 20;
402 else if (!strcmp(argv
[i
], "2.1"))
404 data
.def_version
= 21;
406 else if (!strcmp(argv
[i
], "2.2"))
408 data
.def_version
= 22;
412 _cupsLangPrintf(stderr
, _("%s: Bad version %s for \"-V\"."), "ipptool", argv
[i
]);
417 case 'X' : /* Produce XML output */
418 data
.output
= _CUPS_OUTPUT_PLIST
;
420 if (interval
|| repeat
)
422 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
427 case 'c' : /* CSV output */
428 data
.output
= _CUPS_OUTPUT_CSV
;
431 case 'd' : /* Define a variable */
436 _cupsLangPuts(stderr
,
437 _("ipptool: Missing name=value for \"-d\"."));
441 strlcpy(name
, argv
[i
], sizeof(name
));
442 if ((value
= strchr(name
, '=')) != NULL
)
445 value
= name
+ strlen(name
);
447 _ippVarsSet(&vars
, name
, value
);
450 case 'f' : /* Set the default test filename */
455 _cupsLangPuts(stderr
,
456 _("ipptool: Missing filename for \"-f\"."));
460 if (access(argv
[i
], 0))
466 snprintf(filename
, sizeof(filename
), "%s.gz", argv
[i
]);
467 if (access(filename
, 0) && filename
[0] != '/'
469 && (!isalpha(filename
[0] & 255) || filename
[1] != ':')
473 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s", cg
->cups_datadir
, argv
[i
]);
474 if (access(filename
, 0))
476 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s.gz", cg
->cups_datadir
, argv
[i
]);
477 if (access(filename
, 0))
478 strlcpy(filename
, argv
[i
], sizeof(filename
));
483 strlcpy(filename
, argv
[i
], sizeof(filename
));
485 _ippVarsSet(&vars
, "filename", filename
);
487 if ((ext
= strrchr(filename
, '.')) != NULL
)
490 * Guess the MIME media type based on the extension...
493 if (!_cups_strcasecmp(ext
, ".gif"))
494 _ippVarsSet(&vars
, "filetype", "image/gif");
495 else if (!_cups_strcasecmp(ext
, ".htm") ||
496 !_cups_strcasecmp(ext
, ".htm.gz") ||
497 !_cups_strcasecmp(ext
, ".html") ||
498 !_cups_strcasecmp(ext
, ".html.gz"))
499 _ippVarsSet(&vars
, "filetype", "text/html");
500 else if (!_cups_strcasecmp(ext
, ".jpg") ||
501 !_cups_strcasecmp(ext
, ".jpeg"))
502 _ippVarsSet(&vars
, "filetype", "image/jpeg");
503 else if (!_cups_strcasecmp(ext
, ".pcl") ||
504 !_cups_strcasecmp(ext
, ".pcl.gz"))
505 _ippVarsSet(&vars
, "filetype", "application/vnd.hp-PCL");
506 else if (!_cups_strcasecmp(ext
, ".pdf"))
507 _ippVarsSet(&vars
, "filetype", "application/pdf");
508 else if (!_cups_strcasecmp(ext
, ".png"))
509 _ippVarsSet(&vars
, "filetype", "image/png");
510 else if (!_cups_strcasecmp(ext
, ".ps") ||
511 !_cups_strcasecmp(ext
, ".ps.gz"))
512 _ippVarsSet(&vars
, "filetype", "application/postscript");
513 else if (!_cups_strcasecmp(ext
, ".pwg") ||
514 !_cups_strcasecmp(ext
, ".pwg.gz") ||
515 !_cups_strcasecmp(ext
, ".ras") ||
516 !_cups_strcasecmp(ext
, ".ras.gz"))
517 _ippVarsSet(&vars
, "filetype", "image/pwg-raster");
518 else if (!_cups_strcasecmp(ext
, ".tif") ||
519 !_cups_strcasecmp(ext
, ".tiff"))
520 _ippVarsSet(&vars
, "filetype", "image/tiff");
521 else if (!_cups_strcasecmp(ext
, ".txt") ||
522 !_cups_strcasecmp(ext
, ".txt.gz"))
523 _ippVarsSet(&vars
, "filetype", "text/plain");
524 else if (!_cups_strcasecmp(ext
, ".urf") ||
525 !_cups_strcasecmp(ext
, ".urf.gz"))
526 _ippVarsSet(&vars
, "filetype", "image/urf");
527 else if (!_cups_strcasecmp(ext
, ".xps"))
528 _ippVarsSet(&vars
, "filetype", "application/openxps");
530 _ippVarsSet(&vars
, "filetype", "application/octet-stream");
535 * Use the "auto-type" MIME media type...
538 _ippVarsSet(&vars
, "filetype", "application/octet-stream");
542 case 'h' : /* Validate response headers */
543 data
.validate_headers
= 1;
546 case 'i' : /* Test every N seconds */
551 _cupsLangPuts(stderr
, _("ipptool: Missing seconds for \"-i\"."));
556 interval
= (int)(_cupsStrScand(argv
[i
], NULL
, localeconv()) * 1000000.0);
559 _cupsLangPuts(stderr
, _("ipptool: Invalid seconds for \"-i\"."));
564 if ((data
.output
== _CUPS_OUTPUT_PLIST
|| data
.output
== _CUPS_OUTPUT_IPPSERVER
) && interval
)
566 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"--ippserver\", \"-P\", and \"-X\"."));
571 case 'l' : /* List as a table */
572 data
.output
= _CUPS_OUTPUT_LIST
;
575 case 'n' : /* Repeat count */
580 _cupsLangPuts(stderr
, _("ipptool: Missing count for \"-n\"."));
584 repeat
= atoi(argv
[i
]);
586 if ((data
.output
== _CUPS_OUTPUT_PLIST
|| data
.output
== _CUPS_OUTPUT_IPPSERVER
) && repeat
)
588 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"--ippserver\", \"-P\", and \"-X\"."));
593 case 'q' : /* Be quiet */
594 data
.output
= _CUPS_OUTPUT_QUIET
;
597 case 't' : /* CUPS test output */
598 data
.output
= _CUPS_OUTPUT_TEST
;
601 case 'v' : /* Be verbose */
606 _cupsLangPrintf(stderr
, _("%s: Unknown option \"-%c\"."), "ipptool", *opt
);
611 else if (!strncmp(argv
[i
], "ipp://", 6) || !strncmp(argv
[i
], "http://", 7)
613 || !strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8)
614 #endif /* HAVE_SSL */
623 _cupsLangPuts(stderr
, _("ipptool: May only specify a single URI."));
628 if (!strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8))
629 data
.encryption
= HTTP_ENCRYPT_ALWAYS
;
630 #endif /* HAVE_SSL */
632 if (!_ippVarsSet(&vars
, "uri", argv
[i
]))
634 _cupsLangPrintf(stderr
, _("ipptool: Bad URI \"%s\"."), argv
[i
]);
638 if (vars
.username
[0] && vars
.password
)
639 cupsSetPasswordCB2(_ippVarsPasswordCB
, &vars
);
649 _cupsLangPuts(stderr
, _("ipptool: URI required before test file."));
650 _cupsLangPuts(stderr
, argv
[i
]);
654 if (access(argv
[i
], 0) && argv
[i
][0] != '/'
656 && (!isalpha(argv
[i
][0] & 255) || argv
[i
][1] != ':')
660 snprintf(testname
, sizeof(testname
), "%s/ipptool/%s", cg
->cups_datadir
, argv
[i
]);
661 if (access(testname
, 0))
669 if (!do_tests(testfile
, &vars
, &data
))
674 if (!vars
.uri
|| !testfile
)
678 * Loop if the interval is set...
681 if (data
.output
== _CUPS_OUTPUT_PLIST
)
682 print_xml_trailer(&data
, !status
, NULL
);
683 else if (interval
> 0 && repeat
> 0)
687 usleep((useconds_t
)interval
);
688 do_tests(testfile
, &vars
, &data
);
692 else if (interval
> 0)
696 usleep((useconds_t
)interval
);
697 do_tests(testfile
, &vars
, &data
);
701 if ((data
.output
== _CUPS_OUTPUT_TEST
|| (data
.output
== _CUPS_OUTPUT_PLIST
&& data
.outfile
)) && data
.test_count
> 1)
704 * Show a summary report if there were multiple tests...
707 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
);
710 cupsFileClose(data
.outfile
);
721 * 'add_stringf()' - Add a formatted string to an array.
725 add_stringf(cups_array_t
*a
, /* I - Array */
726 const char *s
, /* I - Printf-style format string */
727 ...) /* I - Additional args as needed */
729 char buffer
[10240]; /* Format buffer */
730 va_list ap
; /* Argument pointer */
734 * Don't bother is the array is NULL...
741 * Format the message...
745 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
749 * Add it to the array...
752 cupsArrayAdd(a
, buffer
);
757 * 'compare_uris()' - Compare two URIs...
760 static int /* O - Result of comparison */
761 compare_uris(const char *a
, /* I - First URI */
762 const char *b
) /* I - Second URI */
764 char ascheme
[32], /* Components of first URI */
769 char bscheme
[32], /* Components of second URI */
774 char *ptr
; /* Pointer into string */
775 int result
; /* Result of comparison */
779 * Separate the URIs into their components...
782 if (httpSeparateURI(HTTP_URI_CODING_ALL
, a
, ascheme
, sizeof(ascheme
), auserpass
, sizeof(auserpass
), ahost
, sizeof(ahost
), &aport
, aresource
, sizeof(aresource
)) < HTTP_URI_STATUS_OK
)
785 if (httpSeparateURI(HTTP_URI_CODING_ALL
, b
, bscheme
, sizeof(bscheme
), buserpass
, sizeof(buserpass
), bhost
, sizeof(bhost
), &bport
, bresource
, sizeof(bresource
)) < HTTP_URI_STATUS_OK
)
789 * Strip trailing dots from the host components, if present...
792 if ((ptr
= ahost
+ strlen(ahost
) - 1) > ahost
&& *ptr
== '.')
795 if ((ptr
= bhost
+ strlen(bhost
) - 1) > bhost
&& *ptr
== '.')
799 * Compare each component...
802 if ((result
= _cups_strcasecmp(ascheme
, bscheme
)) != 0)
805 if ((result
= strcmp(auserpass
, buserpass
)) != 0)
808 if ((result
= _cups_strcasecmp(ahost
, bhost
)) != 0)
812 return (aport
- bport
);
814 if (!_cups_strcasecmp(ascheme
, "mailto") || !_cups_strcasecmp(ascheme
, "urn"))
815 return (_cups_strcasecmp(aresource
, bresource
));
817 return (strcmp(aresource
, bresource
));
822 * 'do_test()' - Do a single test from the test file.
825 static int /* O - 1 on success, 0 on failure */
826 do_test(_ipp_file_t
*f
, /* I - IPP data file */
827 _ipp_vars_t
*vars
, /* I - IPP variables */
828 _cups_testdata_t
*data
) /* I - Test data */
831 int i
, /* Looping var */
832 status_ok
, /* Did we get a matching status? */
833 repeat_count
= 0, /* Repeat count */
834 repeat_test
; /* Repeat the test? */
835 _cups_expect_t
*expect
; /* Current expected attribute */
836 ipp_t
*request
, /* IPP request */
837 *response
; /* IPP response */
838 size_t length
; /* Length of IPP request */
839 http_status_t status
; /* HTTP status */
840 cups_array_t
*a
; /* Duplicate attribute array */
841 ipp_tag_t group
; /* Current group */
842 ipp_attribute_t
*attrptr
, /* Attribute pointer */
843 *found
; /* Found attribute */
844 char temp
[1024]; /* Temporary string */
845 cups_file_t
*reqfile
; /* File to send */
846 ssize_t bytes
; /* Bytes read/written */
847 char buffer
[131072]; /* Copy buffer */
848 size_t widths
[200]; /* Width of columns */
849 const char *error
; /* Current error */
856 * Take over control of the attributes in the request...
863 * Submit the IPP request...
868 ippSetVersion(request
, data
->version
/ 10, data
->version
% 10);
869 ippSetRequestId(request
, data
->request_id
);
871 if (data
->output
== _CUPS_OUTPUT_PLIST
)
873 cupsFilePuts(data
->outfile
, "<dict>\n");
874 cupsFilePuts(data
->outfile
, "<key>Name</key>\n");
875 print_xml_string(data
->outfile
, "string", data
->name
);
876 if (data
->file_id
[0])
878 cupsFilePuts(data
->outfile
, "<key>FileId</key>\n");
879 print_xml_string(data
->outfile
, "string", data
->file_id
);
881 if (data
->test_id
[0])
883 cupsFilePuts(data
->outfile
, "<key>TestId</key>\n");
884 print_xml_string(data
->outfile
, "string", data
->test_id
);
886 cupsFilePuts(data
->outfile
, "<key>Version</key>\n");
887 cupsFilePrintf(data
->outfile
, "<string>%d.%d</string>\n", data
->version
/ 10, data
->version
% 10);
888 cupsFilePuts(data
->outfile
, "<key>Operation</key>\n");
889 print_xml_string(data
->outfile
, "string", ippOpString(ippGetOperation(request
)));
890 cupsFilePuts(data
->outfile
, "<key>RequestId</key>\n");
891 cupsFilePrintf(data
->outfile
, "<integer>%d</integer>\n", data
->request_id
);
892 cupsFilePuts(data
->outfile
, "<key>RequestAttributes</key>\n");
893 cupsFilePuts(data
->outfile
, "<array>\n");
894 if (ippFirstAttribute(request
))
896 cupsFilePuts(data
->outfile
, "<dict>\n");
897 for (attrptr
= ippFirstAttribute(request
), group
= ippGetGroupTag(attrptr
); attrptr
; attrptr
= ippNextAttribute(request
))
898 print_attr(data
->outfile
, data
->output
, attrptr
, &group
);
899 cupsFilePuts(data
->outfile
, "</dict>\n");
901 cupsFilePuts(data
->outfile
, "</array>\n");
904 if (data
->output
== _CUPS_OUTPUT_TEST
|| (data
->output
== _CUPS_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
908 cupsFilePrintf(cupsFileStdout(), " %s:\n", ippOpString(ippGetOperation(request
)));
910 for (attrptr
= ippFirstAttribute(request
); attrptr
; attrptr
= ippNextAttribute(request
))
911 print_attr(cupsFileStdout(), _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
914 cupsFilePrintf(cupsFileStdout(), " %-68.68s [", data
->name
);
917 if ((data
->skip_previous
&& !data
->prev_pass
) || data
->skip_test
)
925 if (data
->output
== _CUPS_OUTPUT_PLIST
)
927 cupsFilePuts(data
->outfile
, "<key>Successful</key>\n");
928 cupsFilePuts(data
->outfile
, "<true />\n");
929 cupsFilePuts(data
->outfile
, "<key>Skipped</key>\n");
930 cupsFilePuts(data
->outfile
, "<true />\n");
931 cupsFilePuts(data
->outfile
, "<key>StatusCode</key>\n");
932 print_xml_string(data
->outfile
, "string", "skip");
933 cupsFilePuts(data
->outfile
, "<key>ResponseAttributes</key>\n");
934 cupsFilePuts(data
->outfile
, "<dict />\n");
937 if (data
->output
== _CUPS_OUTPUT_TEST
|| (data
->output
== _CUPS_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
938 cupsFilePuts(cupsFileStdout(), "SKIP]\n");
943 vars
->password_tries
= 0;
950 data
->delay
= data
->repeat_interval
;
953 status
= HTTP_STATUS_OK
;
955 if (data
->transfer
== _CUPS_TRANSFER_CHUNKED
|| (data
->transfer
== _CUPS_TRANSFER_AUTO
&& data
->file
[0]))
958 * Send request using chunking - a 0 length means "chunk".
966 * Send request using content length...
969 length
= ippLength(request
);
971 if (data
->file
[0] && (reqfile
= cupsFileOpen(data
->file
, "r")) != NULL
)
974 * Read the file to get the uncompressed file size...
977 while ((bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
978 length
+= (size_t)bytes
;
980 cupsFileClose(reqfile
);
985 * Send the request...
992 if (status
!= HTTP_STATUS_ERROR
)
994 while (!response
&& !Cancel
&& data
->prev_pass
)
996 status
= cupsSendRequest(data
->http
, request
, data
->resource
, length
);
999 if (data
->compression
[0])
1000 httpSetField(data
->http
, HTTP_FIELD_CONTENT_ENCODING
, data
->compression
);
1001 #endif /* HAVE_LIBZ */
1003 if (!Cancel
&& status
== HTTP_STATUS_CONTINUE
&& ippGetState(request
) == IPP_DATA
&& data
->file
[0])
1005 if ((reqfile
= cupsFileOpen(data
->file
, "r")) != NULL
)
1007 while (!Cancel
&& (bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
1009 if ((status
= cupsWriteRequestData(data
->http
, buffer
, (size_t)bytes
)) != HTTP_STATUS_CONTINUE
)
1013 cupsFileClose(reqfile
);
1017 snprintf(buffer
, sizeof(buffer
), "%s: %s", data
->file
, strerror(errno
));
1018 _cupsSetError(IPP_INTERNAL_ERROR
, buffer
, 0);
1020 status
= HTTP_STATUS_ERROR
;
1025 * Get the server's response...
1028 if (!Cancel
&& status
!= HTTP_STATUS_ERROR
)
1030 response
= cupsGetResponse(data
->http
, data
->resource
);
1031 status
= httpGetStatus(data
->http
);
1034 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& httpError(data
->http
) != EINVAL
&&
1036 httpError(data
->http
) != WSAETIMEDOUT
)
1038 httpError(data
->http
) != ETIMEDOUT
)
1041 if (httpReconnect2(data
->http
, 30000, NULL
))
1042 data
->prev_pass
= 0;
1044 else if (status
== HTTP_STATUS_ERROR
|| status
== HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED
)
1046 data
->prev_pass
= 0;
1049 else if (status
!= HTTP_STATUS_OK
)
1051 httpFlush(data
->http
);
1053 if (status
== HTTP_STATUS_UNAUTHORIZED
)
1061 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& httpError(data
->http
) != EINVAL
&&
1063 httpError(data
->http
) != WSAETIMEDOUT
)
1065 httpError(data
->http
) != ETIMEDOUT
)
1068 if (httpReconnect2(data
->http
, 30000, NULL
))
1069 data
->prev_pass
= 0;
1071 else if (status
== HTTP_STATUS_ERROR
)
1074 httpReconnect2(data
->http
, 30000, NULL
);
1076 data
->prev_pass
= 0;
1078 else if (status
!= HTTP_STATUS_OK
)
1080 httpFlush(data
->http
);
1081 data
->prev_pass
= 0;
1085 * Check results of request...
1088 cupsArrayClear(data
->errors
);
1090 if (httpGetVersion(data
->http
) != HTTP_1_1
)
1092 int version
= httpGetVersion(data
->http
);
1094 add_stringf(data
->errors
, "Bad HTTP version (%d.%d)", version
/ 100, version
% 100);
1097 if (data
->validate_headers
)
1099 const char *header
; /* HTTP header value */
1101 if ((header
= httpGetField(data
->http
, HTTP_FIELD_CONTENT_TYPE
)) == NULL
|| _cups_strcasecmp(header
, "application/ipp"))
1102 add_stringf(data
->errors
, "Bad HTTP Content-Type in response (%s)", header
&& *header
? header
: "<missing>");
1104 if ((header
= httpGetField(data
->http
, HTTP_FIELD_DATE
)) != NULL
&& *header
&& httpGetDateTime(header
) == 0)
1105 add_stringf(data
->errors
, "Bad HTTP Date in response (%s)", header
);
1111 * No response, log error...
1114 add_stringf(data
->errors
, "IPP request failed with status %s (%s)", ippErrorString(cupsLastError()), cupsLastErrorString());
1119 * Collect common attribute values...
1122 if ((attrptr
= ippFindAttribute(response
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
1124 snprintf(temp
, sizeof(temp
), "%d", ippGetInteger(attrptr
, 0));
1125 _ippVarsSet(vars
, "job-id", temp
);
1128 if ((attrptr
= ippFindAttribute(response
, "job-uri", IPP_TAG_URI
)) != NULL
)
1129 _ippVarsSet(vars
, "job-uri", ippGetString(attrptr
, 0, NULL
));
1131 if ((attrptr
= ippFindAttribute(response
, "notify-subscription-id", IPP_TAG_INTEGER
)) != NULL
)
1133 snprintf(temp
, sizeof(temp
), "%d", ippGetInteger(attrptr
, 0));
1134 _ippVarsSet(vars
, "notify-subscription-id", temp
);
1138 * Check response, validating groups and attributes and logging errors
1142 if (ippGetState(response
) != IPP_DATA
)
1143 add_stringf(data
->errors
, "Missing end-of-attributes-tag in response (RFC 2910 section 3.5.1)");
1147 int major
, minor
; /* IPP version */
1149 major
= ippGetVersion(response
, &minor
);
1151 if (major
!= (data
->version
/ 10) || minor
!= (data
->version
% 10))
1152 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);
1155 if (ippGetRequestId(response
) != data
->request_id
)
1156 add_stringf(data
->errors
, "Bad request ID %d in response - expected %d (RFC 2911 section 3.1.1)", ippGetRequestId(response
), data
->request_id
);
1158 attrptr
= ippFirstAttribute(response
);
1161 add_stringf(data
->errors
, "Missing first attribute \"attributes-charset (charset)\" in group operation-attributes-tag (RFC 2911 section 3.1.4).");
1165 if (!ippGetName(attrptr
) || ippGetValueTag(attrptr
) != IPP_TAG_CHARSET
|| ippGetGroupTag(attrptr
) != IPP_TAG_OPERATION
|| ippGetCount(attrptr
) != 1 ||strcmp(ippGetName(attrptr
), "attributes-charset"))
1166 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
)));
1168 attrptr
= ippNextAttribute(response
);
1170 add_stringf(data
->errors
, "Missing second attribute \"attributes-natural-language (naturalLanguage)\" in group operation-attributes-tag (RFC 2911 section 3.1.4).");
1171 else if (!ippGetName(attrptr
) || ippGetValueTag(attrptr
) != IPP_TAG_LANGUAGE
|| ippGetGroupTag(attrptr
) != IPP_TAG_OPERATION
|| ippGetCount(attrptr
) != 1 || strcmp(ippGetName(attrptr
), "attributes-natural-language"))
1172 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
)));
1175 if ((attrptr
= ippFindAttribute(response
, "status-message", IPP_TAG_ZERO
)) != NULL
)
1177 const char *status_message
= ippGetString(attrptr
, 0, NULL
);
1180 if (ippGetValueTag(attrptr
) != IPP_TAG_TEXT
)
1181 add_stringf(data
->errors
, "status-message (text(255)) has wrong value tag %s (RFC 2911 section 3.1.6.2).", ippTagString(ippGetValueTag(attrptr
)));
1182 if (ippGetGroupTag(attrptr
) != IPP_TAG_OPERATION
)
1183 add_stringf(data
->errors
, "status-message (text(255)) has wrong group tag %s (RFC 2911 section 3.1.6.2).", ippTagString(ippGetGroupTag(attrptr
)));
1184 if (ippGetCount(attrptr
) != 1)
1185 add_stringf(data
->errors
, "status-message (text(255)) has %d values (RFC 2911 section 3.1.6.2).", ippGetCount(attrptr
));
1186 if (status_message
&& strlen(status_message
) > 255)
1187 add_stringf(data
->errors
, "status-message (text(255)) has bad length %d (RFC 2911 section 3.1.6.2).", (int)strlen(status_message
));
1190 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
1191 IPP_TAG_ZERO
)) != NULL
)
1193 const char *detailed_status_message
= ippGetString(attrptr
, 0, NULL
);
1196 if (ippGetValueTag(attrptr
) != IPP_TAG_TEXT
)
1197 add_stringf(data
->errors
,
1198 "detailed-status-message (text(MAX)) has wrong "
1199 "value tag %s (RFC 2911 section 3.1.6.3).",
1200 ippTagString(ippGetValueTag(attrptr
)));
1201 if (ippGetGroupTag(attrptr
) != IPP_TAG_OPERATION
)
1202 add_stringf(data
->errors
,
1203 "detailed-status-message (text(MAX)) has wrong "
1204 "group tag %s (RFC 2911 section 3.1.6.3).",
1205 ippTagString(ippGetGroupTag(attrptr
)));
1206 if (ippGetCount(attrptr
) != 1)
1207 add_stringf(data
->errors
,
1208 "detailed-status-message (text(MAX)) has %d values"
1209 " (RFC 2911 section 3.1.6.3).",
1210 ippGetCount(attrptr
));
1211 if (detailed_status_message
&& strlen(detailed_status_message
) > 1023)
1212 add_stringf(data
->errors
,
1213 "detailed-status-message (text(MAX)) has bad "
1214 "length %d (RFC 2911 section 3.1.6.3).",
1215 (int)strlen(detailed_status_message
));
1218 a
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
1220 for (attrptr
= ippFirstAttribute(response
), group
= ippGetGroupTag(attrptr
);
1222 attrptr
= ippNextAttribute(response
))
1224 if (ippGetGroupTag(attrptr
) != group
)
1226 int out_of_order
= 0; /* Are attribute groups out-of-order? */
1229 switch (ippGetGroupTag(attrptr
))
1234 case IPP_TAG_OPERATION
:
1238 case IPP_TAG_UNSUPPORTED_GROUP
:
1239 if (group
!= IPP_TAG_OPERATION
)
1244 case IPP_TAG_PRINTER
:
1245 if (group
!= IPP_TAG_OPERATION
&& group
!= IPP_TAG_UNSUPPORTED_GROUP
)
1249 case IPP_TAG_SUBSCRIPTION
:
1250 if (group
> ippGetGroupTag(attrptr
) && group
!= IPP_TAG_DOCUMENT
)
1255 if (group
> ippGetGroupTag(attrptr
))
1261 add_stringf(data
->errors
, "Attribute groups out of order (%s < %s)",
1262 ippTagString(ippGetGroupTag(attrptr
)),
1263 ippTagString(group
));
1265 if (ippGetGroupTag(attrptr
) != IPP_TAG_ZERO
)
1266 group
= ippGetGroupTag(attrptr
);
1269 validate_attr(data
, data
->errors
, attrptr
);
1271 if (ippGetName(attrptr
))
1273 if (cupsArrayFind(a
, (void *)ippGetName(attrptr
)))
1274 add_stringf(data
->errors
, "Duplicate \"%s\" attribute in %s group",
1275 ippGetName(attrptr
), ippTagString(group
));
1277 cupsArrayAdd(a
, (void *)ippGetName(attrptr
));
1284 * Now check the test-defined expected status-code and attribute
1288 for (i
= 0, status_ok
= 0; i
< data
->num_statuses
; i
++)
1290 if (data
->statuses
[i
].if_defined
&&
1291 !_ippVarsGet(vars
, data
->statuses
[i
].if_defined
))
1294 if (data
->statuses
[i
].if_not_defined
&&
1295 _ippVarsGet(vars
, data
->statuses
[i
].if_not_defined
))
1298 if (ippGetStatusCode(response
) == data
->statuses
[i
].status
)
1302 if (data
->statuses
[i
].repeat_match
&& repeat_count
< data
->statuses
[i
].repeat_limit
)
1305 if (data
->statuses
[i
].define_match
)
1306 _ippVarsSet(vars
, data
->statuses
[i
].define_match
, "1");
1310 if (data
->statuses
[i
].repeat_no_match
&& repeat_count
< data
->statuses
[i
].repeat_limit
)
1313 if (data
->statuses
[i
].define_no_match
)
1315 _ippVarsSet(vars
, data
->statuses
[i
].define_no_match
, "1");
1321 if (!status_ok
&& data
->num_statuses
> 0)
1323 for (i
= 0; i
< data
->num_statuses
; i
++)
1325 if (data
->statuses
[i
].if_defined
&&
1326 !_ippVarsGet(vars
, data
->statuses
[i
].if_defined
))
1329 if (data
->statuses
[i
].if_not_defined
&&
1330 _ippVarsGet(vars
, data
->statuses
[i
].if_not_defined
))
1333 if (!data
->statuses
[i
].repeat_match
|| repeat_count
>= data
->statuses
[i
].repeat_limit
)
1334 add_stringf(data
->errors
, "EXPECTED: STATUS %s (got %s)",
1335 ippErrorString(data
->statuses
[i
].status
),
1336 ippErrorString(cupsLastError()));
1339 if ((attrptr
= ippFindAttribute(response
, "status-message",
1340 IPP_TAG_TEXT
)) != NULL
)
1341 add_stringf(data
->errors
, "status-message=\"%s\"", ippGetString(attrptr
, 0, NULL
));
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
, ippGetValueTag(found
))) ||
1360 (found
&& expect
->in_group
&&
1361 ippGetGroupTag(found
) != 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
, ippGetValueTag(found
)))
1374 add_stringf(data
->errors
, "EXPECTED: %s OF-TYPE %s (got %s)",
1375 expect
->name
, expect
->of_type
,
1376 ippTagString(ippGetValueTag(found
)));
1378 if (expect
->in_group
&& ippGetGroupTag(found
) != expect
->in_group
)
1379 add_stringf(data
->errors
, "EXPECTED: %s IN-GROUP %s (got %s).",
1380 expect
->name
, ippTagString(expect
->in_group
),
1381 ippTagString(ippGetGroupTag(found
)));
1385 if (expect
->repeat_no_match
&& repeat_count
< expect
->repeat_limit
)
1391 ippAttributeString(found
, buffer
, sizeof(buffer
));
1393 if (found
&& expect
->with_value_from
&& !with_value_from(NULL
, ippFindAttribute(response
, expect
->with_value_from
, IPP_TAG_ZERO
), found
, buffer
, sizeof(buffer
)))
1395 if (expect
->define_no_match
)
1396 _ippVarsSet(vars
, expect
->define_no_match
, "1");
1397 else if (!expect
->define_match
&& !expect
->define_value
&& ((!expect
->repeat_match
&& !expect
->repeat_no_match
) || repeat_count
>= expect
->repeat_limit
))
1399 add_stringf(data
->errors
, "EXPECTED: %s WITH-VALUES-FROM %s", expect
->name
, expect
->with_value_from
);
1401 with_value_from(data
->errors
, ippFindAttribute(response
, expect
->with_value_from
, IPP_TAG_ZERO
), found
, buffer
, sizeof(buffer
));
1404 if (expect
->repeat_no_match
&& repeat_count
< expect
->repeat_limit
)
1409 else if (found
&& !with_value(data
, NULL
, expect
->with_value
, expect
->with_flags
, found
, buffer
, sizeof(buffer
)))
1411 if (expect
->define_no_match
)
1412 _ippVarsSet(vars
, expect
->define_no_match
, "1");
1413 else if (!expect
->define_match
&& !expect
->define_value
&&
1414 !expect
->repeat_match
&& (!expect
->repeat_no_match
|| repeat_count
>= expect
->repeat_limit
))
1416 if (expect
->with_flags
& _CUPS_WITH_REGEX
)
1417 add_stringf(data
->errors
, "EXPECTED: %s %s /%s/", expect
->name
, with_flags_string(expect
->with_flags
), expect
->with_value
);
1419 add_stringf(data
->errors
, "EXPECTED: %s %s \"%s\"", expect
->name
, with_flags_string(expect
->with_flags
), expect
->with_value
);
1421 with_value(data
, data
->errors
, expect
->with_value
, expect
->with_flags
, found
, buffer
, sizeof(buffer
));
1424 if (expect
->repeat_no_match
&&
1425 repeat_count
< expect
->repeat_limit
)
1431 if (found
&& expect
->count
> 0 && ippGetCount(found
) != expect
->count
)
1433 if (expect
->define_no_match
)
1434 _ippVarsSet(vars
, expect
->define_no_match
, "1");
1435 else if (!expect
->define_match
&& !expect
->define_value
)
1437 add_stringf(data
->errors
, "EXPECTED: %s COUNT %d (got %d)", expect
->name
,
1438 expect
->count
, ippGetCount(found
));
1441 if (expect
->repeat_no_match
&&
1442 repeat_count
< expect
->repeat_limit
)
1448 if (found
&& expect
->same_count_as
)
1450 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
1453 if (!attrptr
|| ippGetCount(attrptr
) != ippGetCount(found
))
1455 if (expect
->define_no_match
)
1456 _ippVarsSet(vars
, expect
->define_no_match
, "1");
1457 else if (!expect
->define_match
&& !expect
->define_value
)
1460 add_stringf(data
->errors
,
1461 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
1462 "(not returned)", expect
->name
,
1463 ippGetCount(found
), expect
->same_count_as
);
1464 else if (ippGetCount(attrptr
) != ippGetCount(found
))
1465 add_stringf(data
->errors
,
1466 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
1467 "(%d values)", expect
->name
, ippGetCount(found
),
1468 expect
->same_count_as
, ippGetCount(attrptr
));
1471 if (expect
->repeat_no_match
&&
1472 repeat_count
< expect
->repeat_limit
)
1479 if (found
&& expect
->define_match
)
1480 _ippVarsSet(vars
, expect
->define_match
, "1");
1482 if (found
&& expect
->define_value
)
1484 if (!expect
->with_value
)
1486 int last
= ippGetCount(found
) - 1;
1487 /* Last element in attribute */
1489 switch (ippGetValueTag(found
))
1492 case IPP_TAG_INTEGER
:
1493 snprintf(buffer
, sizeof(buffer
), "%d", ippGetInteger(found
, last
));
1496 case IPP_TAG_BOOLEAN
:
1497 if (ippGetBoolean(found
, last
))
1498 strlcpy(buffer
, "true", sizeof(buffer
));
1500 strlcpy(buffer
, "false", sizeof(buffer
));
1503 case IPP_TAG_RESOLUTION
:
1505 int xres
, /* Horizontal resolution */
1506 yres
; /* Vertical resolution */
1507 ipp_res_t units
; /* Resolution units */
1509 xres
= ippGetResolution(found
, last
, &yres
, &units
);
1512 snprintf(buffer
, sizeof(buffer
), "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
1514 snprintf(buffer
, sizeof(buffer
), "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
1518 case IPP_TAG_CHARSET
:
1519 case IPP_TAG_KEYWORD
:
1520 case IPP_TAG_LANGUAGE
:
1521 case IPP_TAG_MIMETYPE
:
1523 case IPP_TAG_NAMELANG
:
1525 case IPP_TAG_TEXTLANG
:
1527 case IPP_TAG_URISCHEME
:
1528 strlcpy(buffer
, ippGetString(found
, last
, NULL
), sizeof(buffer
));
1532 ippAttributeString(found
, buffer
, sizeof(buffer
));
1537 _ippVarsSet(vars
, expect
->define_value
, buffer
);
1540 if (found
&& expect
->repeat_match
&&
1541 repeat_count
< expect
->repeat_limit
)
1544 while (expect
->expect_all
&& (found
= ippFindNextAttribute(response
, expect
->name
, IPP_TAG_ZERO
)) != NULL
);
1549 * If we are going to repeat this test, display intermediate results...
1554 if (data
->output
== _CUPS_OUTPUT_TEST
|| (data
->output
== _CUPS_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
1556 cupsFilePrintf(cupsFileStdout(), "%04d]\n", repeat_count
);
1558 if (data
->num_displayed
> 0)
1560 for (attrptr
= ippFirstAttribute(response
); attrptr
; attrptr
= ippNextAttribute(response
))
1562 const char *attrname
= ippGetName(attrptr
);
1565 for (i
= 0; i
< data
->num_displayed
; i
++)
1567 if (!strcmp(data
->displayed
[i
], attrname
))
1569 print_attr(cupsFileStdout(), _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
1578 if (data
->output
== _CUPS_OUTPUT_TEST
|| (data
->output
== _CUPS_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
1580 cupsFilePrintf(cupsFileStdout(), " %-68.68s [", data
->name
);
1583 ippDelete(response
);
1587 while (repeat_test
);
1593 if (cupsArrayCount(data
->errors
) > 0)
1594 data
->prev_pass
= data
->pass
= 0;
1596 if (data
->prev_pass
)
1597 data
->pass_count
++;
1599 data
->fail_count
++;
1601 if (data
->output
== _CUPS_OUTPUT_PLIST
)
1603 cupsFilePuts(data
->outfile
, "<key>Successful</key>\n");
1604 cupsFilePuts(data
->outfile
, data
->prev_pass
? "<true />\n" : "<false />\n");
1605 cupsFilePuts(data
->outfile
, "<key>StatusCode</key>\n");
1606 print_xml_string(data
->outfile
, "string", ippErrorString(cupsLastError()));
1607 cupsFilePuts(data
->outfile
, "<key>ResponseAttributes</key>\n");
1608 cupsFilePuts(data
->outfile
, "<array>\n");
1609 cupsFilePuts(data
->outfile
, "<dict>\n");
1610 for (attrptr
= ippFirstAttribute(response
), group
= ippGetGroupTag(attrptr
);
1612 attrptr
= ippNextAttribute(response
))
1613 print_attr(data
->outfile
, data
->output
, attrptr
, &group
);
1614 cupsFilePuts(data
->outfile
, "</dict>\n");
1615 cupsFilePuts(data
->outfile
, "</array>\n");
1617 else if (data
->output
== _CUPS_OUTPUT_IPPSERVER
&& response
)
1619 for (attrptr
= ippFirstAttribute(response
); attrptr
; attrptr
= ippNextAttribute(response
))
1621 if (!ippGetName(attrptr
) || ippGetGroupTag(attrptr
) != IPP_TAG_PRINTER
)
1624 print_ippserver_attr(data
, attrptr
, 0);
1628 if (data
->output
== _CUPS_OUTPUT_TEST
|| (data
->output
== _CUPS_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
1630 cupsFilePuts(cupsFileStdout(), data
->prev_pass
? "PASS]\n" : "FAIL]\n");
1632 if (!data
->prev_pass
|| (data
->verbosity
&& response
))
1634 cupsFilePrintf(cupsFileStdout(), " RECEIVED: %lu bytes in response\n", (unsigned long)ippLength(response
));
1635 cupsFilePrintf(cupsFileStdout(), " status-code = %s (%s)\n", ippErrorString(cupsLastError()), cupsLastErrorString());
1637 if (data
->verbosity
&& response
)
1639 for (attrptr
= ippFirstAttribute(response
); attrptr
; attrptr
= ippNextAttribute(response
))
1640 print_attr(cupsFileStdout(), _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
1644 else if (!data
->prev_pass
&& data
->output
!= _CUPS_OUTPUT_QUIET
)
1645 fprintf(stderr
, "%s\n", cupsLastErrorString());
1647 if (data
->prev_pass
&& data
->output
>= _CUPS_OUTPUT_LIST
&& !data
->verbosity
&& data
->num_displayed
> 0)
1649 size_t width
; /* Length of value */
1651 for (i
= 0; i
< data
->num_displayed
; i
++)
1653 widths
[i
] = strlen(data
->displayed
[i
]);
1655 for (attrptr
= ippFindAttribute(response
, data
->displayed
[i
], IPP_TAG_ZERO
);
1657 attrptr
= ippFindNextAttribute(response
, data
->displayed
[i
], IPP_TAG_ZERO
))
1659 width
= ippAttributeString(attrptr
, NULL
, 0);
1660 if (width
> widths
[i
])
1665 if (data
->output
== _CUPS_OUTPUT_CSV
)
1666 print_csv(data
, NULL
, NULL
, data
->num_displayed
, data
->displayed
, widths
);
1668 print_line(data
, NULL
, NULL
, data
->num_displayed
, data
->displayed
, widths
);
1670 attrptr
= ippFirstAttribute(response
);
1674 while (attrptr
&& ippGetGroupTag(attrptr
) <= IPP_TAG_OPERATION
)
1675 attrptr
= ippNextAttribute(response
);
1679 if (data
->output
== _CUPS_OUTPUT_CSV
)
1680 print_csv(data
, response
, attrptr
, data
->num_displayed
, data
->displayed
, widths
);
1682 print_line(data
, response
, attrptr
, data
->num_displayed
, data
->displayed
, widths
);
1684 while (attrptr
&& ippGetGroupTag(attrptr
) > IPP_TAG_OPERATION
)
1685 attrptr
= ippNextAttribute(response
);
1689 else if (!data
->prev_pass
)
1691 if (data
->output
== _CUPS_OUTPUT_PLIST
)
1693 cupsFilePuts(data
->outfile
, "<key>Errors</key>\n");
1694 cupsFilePuts(data
->outfile
, "<array>\n");
1696 for (error
= (char *)cupsArrayFirst(data
->errors
);
1698 error
= (char *)cupsArrayNext(data
->errors
))
1699 print_xml_string(data
->outfile
, "string", error
);
1701 cupsFilePuts(data
->outfile
, "</array>\n");
1704 if (data
->output
== _CUPS_OUTPUT_TEST
|| (data
->output
== _CUPS_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
1706 for (error
= (char *)cupsArrayFirst(data
->errors
);
1708 error
= (char *)cupsArrayNext(data
->errors
))
1709 cupsFilePrintf(cupsFileStdout(), " %s\n", error
);
1713 if (data
->num_displayed
> 0 && !data
->verbosity
&& response
&& (data
->output
== _CUPS_OUTPUT_TEST
|| (data
->output
== _CUPS_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout())))
1715 for (attrptr
= ippFirstAttribute(response
); attrptr
; attrptr
= ippNextAttribute(response
))
1717 if (ippGetName(attrptr
))
1719 for (i
= 0; i
< data
->num_displayed
; i
++)
1721 if (!strcmp(data
->displayed
[i
], ippGetName(attrptr
)))
1723 print_attr(data
->outfile
, data
->output
, attrptr
, NULL
);
1733 if (data
->output
== _CUPS_OUTPUT_PLIST
)
1734 cupsFilePuts(data
->outfile
, "</dict>\n");
1736 ippDelete(response
);
1739 for (i
= 0; i
< data
->num_statuses
; i
++)
1741 if (data
->statuses
[i
].if_defined
)
1742 free(data
->statuses
[i
].if_defined
);
1743 if (data
->statuses
[i
].if_not_defined
)
1744 free(data
->statuses
[i
].if_not_defined
);
1745 if (data
->statuses
[i
].define_match
)
1746 free(data
->statuses
[i
].define_match
);
1747 if (data
->statuses
[i
].define_no_match
)
1748 free(data
->statuses
[i
].define_no_match
);
1750 data
->num_statuses
= 0;
1752 for (i
= data
->num_expects
, expect
= data
->expects
; i
> 0; i
--, expect
++)
1755 if (expect
->of_type
)
1756 free(expect
->of_type
);
1757 if (expect
->same_count_as
)
1758 free(expect
->same_count_as
);
1759 if (expect
->if_defined
)
1760 free(expect
->if_defined
);
1761 if (expect
->if_not_defined
)
1762 free(expect
->if_not_defined
);
1763 if (expect
->with_value
)
1764 free(expect
->with_value
);
1765 if (expect
->define_match
)
1766 free(expect
->define_match
);
1767 if (expect
->define_no_match
)
1768 free(expect
->define_no_match
);
1769 if (expect
->define_value
)
1770 free(expect
->define_value
);
1772 data
->num_expects
= 0;
1774 for (i
= 0; i
< data
->num_displayed
; i
++)
1775 free(data
->displayed
[i
]);
1776 data
->num_displayed
= 0;
1778 return (data
->ignore_errors
|| data
->prev_pass
);
1783 * 'do_tests()' - Do tests as specified in the test file.
1786 static int /* O - 1 on success, 0 on failure */
1787 do_tests(const char *testfile
, /* I - Test file to use */
1788 _ipp_vars_t
*vars
, /* I - Variables */
1789 _cups_testdata_t
*data
) /* I - Test data */
1791 http_encryption_t encryption
; /* Encryption mode */
1795 * Connect to the printer/server...
1798 if (!_cups_strcasecmp(vars
->scheme
, "https") || !_cups_strcasecmp(vars
->scheme
, "ipps"))
1799 encryption
= HTTP_ENCRYPTION_ALWAYS
;
1801 encryption
= data
->encryption
;
1803 if ((data
->http
= httpConnect2(vars
->host
, vars
->port
, NULL
, data
->family
, encryption
, 1, 30000, NULL
)) == NULL
)
1805 print_fatal_error(data
, "Unable to connect to \"%s\" on port %d - %s", vars
->host
, vars
->port
, cupsLastErrorString());
1810 httpSetDefaultField(data
->http
, HTTP_FIELD_ACCEPT_ENCODING
, "deflate, gzip, identity");
1812 httpSetDefaultField(data
->http
, HTTP_FIELD_ACCEPT_ENCODING
, "identity");
1813 #endif /* HAVE_LIBZ */
1815 if (data
->timeout
> 0.0)
1816 httpSetTimeout(data
->http
, data
->timeout
, timeout_cb
, NULL
);
1822 _ippFileParse(vars
, testfile
, (_ipp_ftoken_cb_t
)token_cb
, (_ipp_ferror_cb_t
)error_cb
, (void *)data
);
1825 * Close connection and return...
1828 httpClose(data
->http
);
1831 return (data
->pass
);
1836 * 'error_cb()' - Print/add an error message.
1839 static int /* O - 1 to continue, 0 to stop */
1840 error_cb(_ipp_file_t
*f
, /* I - IPP file data */
1841 _cups_testdata_t
*data
, /* I - Test data */
1842 const char *error
) /* I - Error message */
1846 print_fatal_error(data
, "%s", error
);
1853 * 'expect_matches()' - Return true if the tag matches the specification.
1856 static int /* O - 1 if matches, 0 otherwise */
1858 _cups_expect_t
*expect
, /* I - Expected attribute */
1859 ipp_tag_t value_tag
) /* I - Value tag for attribute */
1861 int match
; /* Match? */
1862 char *of_type
, /* Type name to match */
1863 *next
, /* Next name to match */
1864 sep
; /* Separator character */
1868 * If we don't expect a particular type, return immediately...
1871 if (!expect
->of_type
)
1875 * Parse the "of_type" value since the string can contain multiple attribute
1876 * types separated by "," or "|"...
1879 for (of_type
= expect
->of_type
, match
= 0; !match
&& *of_type
; of_type
= next
)
1882 * Find the next separator, and set it (temporarily) to nul if present.
1885 for (next
= of_type
; *next
&& *next
!= '|' && *next
!= ','; next
++);
1887 if ((sep
= *next
) != '\0')
1891 * Support some meta-types to make it easier to write the test file.
1894 if (!strcmp(of_type
, "text"))
1895 match
= value_tag
== IPP_TAG_TEXTLANG
|| value_tag
== IPP_TAG_TEXT
;
1896 else if (!strcmp(of_type
, "name"))
1897 match
= value_tag
== IPP_TAG_NAMELANG
|| value_tag
== IPP_TAG_NAME
;
1898 else if (!strcmp(of_type
, "collection"))
1899 match
= value_tag
== IPP_TAG_BEGIN_COLLECTION
;
1901 match
= value_tag
== ippTagValue(of_type
);
1904 * Restore the separator if we have one...
1916 * 'get_filename()' - Get a filename based on the current test file.
1919 static char * /* O - Filename */
1920 get_filename(const char *testfile
, /* I - Current test file */
1921 char *dst
, /* I - Destination filename */
1922 const char *src
, /* I - Source filename */
1923 size_t dstsize
) /* I - Size of destination buffer */
1925 char *dstptr
; /* Pointer into destination */
1926 _cups_globals_t
*cg
= _cupsGlobals();
1930 if (*src
== '<' && src
[strlen(src
) - 1] == '>')
1933 * Map <filename> to CUPS_DATADIR/ipptool/filename...
1936 snprintf(dst
, dstsize
, "%s/ipptool/%s", cg
->cups_datadir
, src
+ 1);
1937 dstptr
= dst
+ strlen(dst
) - 1;
1941 else if (!access(src
, R_OK
) || *src
== '/'
1943 || (isalpha(*src
& 255) && src
[1] == ':')
1948 * Use the path as-is...
1951 strlcpy(dst
, src
, dstsize
);
1956 * Make path relative to testfile...
1959 strlcpy(dst
, testfile
, dstsize
);
1960 if ((dstptr
= strrchr(dst
, '/')) != NULL
)
1963 dstptr
= dst
; /* Should never happen */
1965 strlcpy(dstptr
, src
, dstsize
- (size_t)(dstptr
- dst
));
1973 * 'get_string()' - Get a pointer to a string value or the portion of interest.
1976 static const char * /* O - Pointer to string */
1977 get_string(ipp_attribute_t
*attr
, /* I - IPP attribute */
1978 int element
, /* I - Element to fetch */
1979 int flags
, /* I - Value ("with") flags */
1980 char *buffer
, /* I - Temporary buffer */
1981 size_t bufsize
) /* I - Size of temporary buffer */
1983 const char *value
; /* Value */
1984 char *ptr
, /* Pointer into value */
1985 scheme
[256], /* URI scheme */
1986 userpass
[256], /* Username/password */
1987 hostname
[256], /* Hostname */
1988 resource
[1024]; /* Resource */
1989 int port
; /* Port number */
1992 value
= ippGetString(attr
, element
, NULL
);
1994 if (flags
& _CUPS_WITH_HOSTNAME
)
1996 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), buffer
, (int)bufsize
, &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
1999 ptr
= buffer
+ strlen(buffer
) - 1;
2000 if (ptr
>= buffer
&& *ptr
== '.')
2001 *ptr
= '\0'; /* Drop trailing "." */
2005 else if (flags
& _CUPS_WITH_RESOURCE
)
2007 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, buffer
, (int)bufsize
) < HTTP_URI_STATUS_OK
)
2012 else if (flags
& _CUPS_WITH_SCHEME
)
2014 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, buffer
, (int)bufsize
, userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
2019 else if (ippGetValueTag(attr
) == IPP_TAG_URI
&& (!strncmp(value
, "ipp://", 6) || !strncmp(value
, "http://", 7) || !strncmp(value
, "ipps://", 7) || !strncmp(value
, "https://", 8)))
2021 http_uri_status_t status
= httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
));
2023 if (status
< HTTP_URI_STATUS_OK
)
2034 * Normalize URI with no trailing dot...
2037 if ((ptr
= hostname
+ strlen(hostname
) - 1) >= hostname
&& *ptr
== '.')
2040 httpAssembleURI(HTTP_URI_CODING_ALL
, buffer
, (int)bufsize
, scheme
, userpass
, hostname
, port
, resource
);
2051 * 'init_data()' - Initialize test data.
2055 init_data(_cups_testdata_t
*data
) /* I - Data */
2057 memset(data
, 0, sizeof(_cups_testdata_t
));
2059 data
->output
= _CUPS_OUTPUT_LIST
;
2060 data
->outfile
= cupsFileStdout();
2061 data
->family
= AF_UNSPEC
;
2062 data
->def_transfer
= _CUPS_TRANSFER_AUTO
;
2063 data
->def_version
= 11;
2064 data
->errors
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
, (cups_afree_func_t
)free
);
2066 data
->prev_pass
= 1;
2067 data
->request_id
= (CUPS_RAND() % 1000) * 137 + 1;
2068 data
->show_header
= 1;
2073 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
2077 static char * /* O - ISO 8601 date/time string */
2078 iso_date(const ipp_uchar_t
*date
) /* I - IPP (RFC 1903) date/time value */
2080 time_t utctime
; /* UTC time since 1970 */
2081 struct tm
*utcdate
; /* UTC date/time */
2082 static char buffer
[255]; /* String buffer */
2085 utctime
= ippDateToTime(date
);
2086 utcdate
= gmtime(&utctime
);
2088 snprintf(buffer
, sizeof(buffer
), "%04d-%02d-%02dT%02d:%02d:%02dZ",
2089 utcdate
->tm_year
+ 1900, utcdate
->tm_mon
+ 1, utcdate
->tm_mday
,
2090 utcdate
->tm_hour
, utcdate
->tm_min
, utcdate
->tm_sec
);
2097 * 'pause_message()' - Display the message and pause until the user presses a key.
2101 pause_message(const char *message
) /* I - Message */
2104 HANDLE tty
; /* Console handle */
2105 DWORD mode
; /* Console mode */
2106 char key
; /* Key press */
2107 DWORD bytes
; /* Bytes read for key press */
2111 * Disable input echo and set raw input...
2114 if ((tty
= GetStdHandle(STD_INPUT_HANDLE
)) == INVALID_HANDLE_VALUE
)
2117 if (!GetConsoleMode(tty
, &mode
))
2120 if (!SetConsoleMode(tty
, 0))
2124 int tty
; /* /dev/tty - never read from stdin */
2125 struct termios original
, /* Original input mode */
2126 noecho
; /* No echo input mode */
2127 char key
; /* Current key press */
2131 * Disable input echo and set raw input...
2134 if ((tty
= open("/dev/tty", O_RDONLY
)) < 0)
2137 if (tcgetattr(tty
, &original
))
2144 noecho
.c_lflag
&= (tcflag_t
)~(ICANON
| ECHO
| ECHOE
| ISIG
);
2146 if (tcsetattr(tty
, TCSAFLUSH
, &noecho
))
2154 * Display the prompt...
2157 cupsFilePrintf(cupsFileStdout(), "%s\n---- PRESS ANY KEY ----", message
);
2164 ReadFile(tty
, &key
, 1, &bytes
, NULL
);
2170 SetConsoleMode(tty
, mode
);
2183 tcsetattr(tty
, TCSAFLUSH
, &original
);
2188 * Erase the "press any key" prompt...
2191 cupsFilePuts(cupsFileStdout(), "\r \r");
2196 * 'print_attr()' - Print an attribute on the screen.
2200 print_attr(cups_file_t
*outfile
, /* I - Output file */
2201 int output
, /* I - Output format */
2202 ipp_attribute_t
*attr
, /* I - Attribute to print */
2203 ipp_tag_t
*group
) /* IO - Current group */
2205 int i
, /* Looping var */
2206 count
; /* Number of values */
2207 ipp_attribute_t
*colattr
; /* Collection attribute */
2210 if (output
== _CUPS_OUTPUT_PLIST
)
2212 if (!ippGetName(attr
) || (group
&& *group
!= ippGetGroupTag(attr
)))
2214 if (ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
2216 cupsFilePuts(outfile
, "</dict>\n");
2217 cupsFilePuts(outfile
, "<dict>\n");
2221 *group
= ippGetGroupTag(attr
);
2224 if (!ippGetName(attr
))
2227 print_xml_string(outfile
, "key", ippGetName(attr
));
2228 if ((count
= ippGetCount(attr
)) > 1)
2229 cupsFilePuts(outfile
, "<array>\n");
2231 switch (ippGetValueTag(attr
))
2233 case IPP_TAG_INTEGER
:
2235 for (i
= 0; i
< count
; i
++)
2236 cupsFilePrintf(outfile
, "<integer>%d</integer>\n", ippGetInteger(attr
, i
));
2239 case IPP_TAG_BOOLEAN
:
2240 for (i
= 0; i
< count
; i
++)
2241 cupsFilePuts(outfile
, ippGetBoolean(attr
, i
) ? "<true />\n" : "<false />\n");
2244 case IPP_TAG_RANGE
:
2245 for (i
= 0; i
< count
; i
++)
2247 int lower
, upper
; /* Lower and upper ranges */
2249 lower
= ippGetRange(attr
, i
, &upper
);
2250 cupsFilePrintf(outfile
, "<dict><key>lower</key><integer>%d</integer><key>upper</key><integer>%d</integer></dict>\n", lower
, upper
);
2254 case IPP_TAG_RESOLUTION
:
2255 for (i
= 0; i
< count
; i
++)
2257 int xres
, yres
; /* Resolution values */
2258 ipp_res_t units
; /* Resolution units */
2260 xres
= ippGetResolution(attr
, i
, &yres
, &units
);
2261 cupsFilePrintf(outfile
, "<dict><key>xres</key><integer>%d</integer><key>yres</key><integer>%d</integer><key>units</key><string>%s</string></dict>\n", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
2266 for (i
= 0; i
< count
; i
++)
2267 cupsFilePrintf(outfile
, "<date>%s</date>\n", iso_date(ippGetDate(attr
, i
)));
2270 case IPP_TAG_STRING
:
2271 for (i
= 0; i
< count
; i
++)
2273 int datalen
; /* Length of data */
2274 void *data
= ippGetOctetString(attr
, i
, &datalen
);
2276 char buffer
[IPP_MAX_LENGTH
* 5 / 4 + 1];
2277 /* Base64 output buffer */
2279 cupsFilePrintf(outfile
, "<data>%s</data>\n", httpEncode64_2(buffer
, sizeof(buffer
), data
, datalen
));
2285 case IPP_TAG_KEYWORD
:
2287 case IPP_TAG_URISCHEME
:
2288 case IPP_TAG_CHARSET
:
2289 case IPP_TAG_LANGUAGE
:
2290 case IPP_TAG_MIMETYPE
:
2291 for (i
= 0; i
< count
; i
++)
2292 print_xml_string(outfile
, "string", ippGetString(attr
, i
, NULL
));
2295 case IPP_TAG_TEXTLANG
:
2296 case IPP_TAG_NAMELANG
:
2297 for (i
= 0; i
< count
; i
++)
2299 const char *s
, /* String */
2300 *lang
; /* Language */
2302 s
= ippGetString(attr
, i
, &lang
);
2303 cupsFilePuts(outfile
, "<dict><key>language</key><string>");
2304 print_xml_string(outfile
, NULL
, lang
);
2305 cupsFilePuts(outfile
, "</string><key>string</key><string>");
2306 print_xml_string(outfile
, NULL
, s
);
2307 cupsFilePuts(outfile
, "</string></dict>\n");
2311 case IPP_TAG_BEGIN_COLLECTION
:
2312 for (i
= 0; i
< count
; i
++)
2314 ipp_t
*col
= ippGetCollection(attr
, i
);
2315 /* Collection value */
2317 cupsFilePuts(outfile
, "<dict>\n");
2318 for (colattr
= ippFirstAttribute(col
); colattr
; colattr
= ippNextAttribute(col
))
2319 print_attr(outfile
, output
, colattr
, NULL
);
2320 cupsFilePuts(outfile
, "</dict>\n");
2325 cupsFilePrintf(outfile
, "<string><<%s>></string>\n", ippTagString(ippGetValueTag(attr
)));
2330 cupsFilePuts(outfile
, "</array>\n");
2334 char buffer
[131072]; /* Value buffer */
2336 if (output
== _CUPS_OUTPUT_TEST
)
2338 if (!ippGetName(attr
))
2340 cupsFilePuts(outfile
, " -- separator --\n");
2344 cupsFilePrintf(outfile
, " %s (%s%s) = ", ippGetName(attr
), ippGetCount(attr
) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr
)));
2347 ippAttributeString(attr
, buffer
, sizeof(buffer
));
2348 cupsFilePrintf(outfile
, "%s\n", buffer
);
2354 * 'print_csv()' - Print a line of CSV text.
2359 _cups_testdata_t
*data
, /* I - Test data */
2360 ipp_t
*ipp
, /* I - Response message */
2361 ipp_attribute_t
*attr
, /* I - First attribute for line */
2362 int num_displayed
, /* I - Number of attributes to display */
2363 char **displayed
, /* I - Attributes to display */
2364 size_t *widths
) /* I - Column widths */
2366 int i
; /* Looping var */
2367 size_t maxlength
; /* Max length of all columns */
2368 char *buffer
, /* String buffer */
2369 *bufptr
; /* Pointer into buffer */
2370 ipp_attribute_t
*current
; /* Current attribute */
2374 * Get the maximum string length we have to show and allocate...
2377 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
2378 if (widths
[i
] > maxlength
)
2379 maxlength
= widths
[i
];
2383 if ((buffer
= malloc(maxlength
)) == NULL
)
2387 * Loop through the attributes to display...
2392 for (i
= 0; i
< num_displayed
; i
++)
2395 cupsFilePutChar(data
->outfile
, ',');
2399 for (current
= attr
; current
; current
= ippNextAttribute(ipp
))
2401 if (!ippGetName(current
))
2403 else if (!strcmp(ippGetName(current
), displayed
[i
]))
2405 ippAttributeString(current
, buffer
, maxlength
);
2410 if (strchr(buffer
, ',') != NULL
|| strchr(buffer
, '\"') != NULL
||
2411 strchr(buffer
, '\\') != NULL
)
2413 cupsFilePutChar(cupsFileStdout(), '\"');
2414 for (bufptr
= buffer
; *bufptr
; bufptr
++)
2416 if (*bufptr
== '\\' || *bufptr
== '\"')
2417 cupsFilePutChar(cupsFileStdout(), '\\');
2418 cupsFilePutChar(cupsFileStdout(), *bufptr
);
2420 cupsFilePutChar(cupsFileStdout(), '\"');
2423 cupsFilePuts(data
->outfile
, buffer
);
2425 cupsFilePutChar(cupsFileStdout(), '\n');
2429 for (i
= 0; i
< num_displayed
; i
++)
2432 cupsFilePutChar(cupsFileStdout(), ',');
2434 cupsFilePuts(data
->outfile
, displayed
[i
]);
2436 cupsFilePutChar(cupsFileStdout(), '\n');
2444 * 'print_fatal_error()' - Print a fatal error message.
2449 _cups_testdata_t
*data
, /* I - Test data */
2450 const char *s
, /* I - Printf-style format string */
2451 ...) /* I - Additional arguments as needed */
2453 char buffer
[10240]; /* Format buffer */
2454 va_list ap
; /* Pointer to arguments */
2458 * Format the error message...
2462 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
2469 if (data
->output
== _CUPS_OUTPUT_PLIST
)
2471 print_xml_header(data
);
2472 print_xml_trailer(data
, 0, buffer
);
2475 _cupsLangPrintf(stderr
, "ipptool: %s", buffer
);
2480 * 'print_ippserver_attr()' - Print a attribute suitable for use by ippserver.
2484 print_ippserver_attr(
2485 _cups_testdata_t
*data
, /* I - Test data */
2486 ipp_attribute_t
*attr
, /* I - Attribute to print */
2487 int indent
) /* I - Indentation level */
2489 int i
, /* Looping var */
2490 count
= ippGetCount(attr
);
2491 /* Number of values */
2492 ipp_attribute_t
*colattr
; /* Collection attribute */
2496 cupsFilePrintf(data
->outfile
, "ATTR %s %s", ippTagString(ippGetValueTag(attr
)), ippGetName(attr
));
2498 cupsFilePrintf(data
->outfile
, "%*sMEMBER %s %s", indent
, "", ippTagString(ippGetValueTag(attr
)), ippGetName(attr
));
2500 switch (ippGetValueTag(attr
))
2502 case IPP_TAG_INTEGER
:
2504 for (i
= 0; i
< count
; i
++)
2505 cupsFilePrintf(data
->outfile
, "%s%d", i
? "," : " ", ippGetInteger(attr
, i
));
2508 case IPP_TAG_BOOLEAN
:
2509 cupsFilePuts(data
->outfile
, ippGetBoolean(attr
, 0) ? " true" : " false");
2511 for (i
= 1; i
< count
; i
++)
2512 cupsFilePuts(data
->outfile
, ippGetBoolean(attr
, 1) ? ",true" : ",false");
2515 case IPP_TAG_RANGE
:
2516 for (i
= 0; i
< count
; i
++)
2518 int upper
, lower
= ippGetRange(attr
, i
, &upper
);
2520 cupsFilePrintf(data
->outfile
, "%s%d-%d", i
? "," : " ", lower
, upper
);
2524 case IPP_TAG_RESOLUTION
:
2525 for (i
= 0; i
< count
; i
++)
2528 int yres
, xres
= ippGetResolution(attr
, i
, &yres
, &units
);
2530 cupsFilePrintf(data
->outfile
, "%s%dx%d%s", i
? "," : " ", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
2535 for (i
= 0; i
< count
; i
++)
2536 cupsFilePrintf(data
->outfile
, "%s%s", i
? "," : " ", iso_date(ippGetDate(attr
, i
)));
2539 case IPP_TAG_STRING
:
2540 for (i
= 0; i
< count
; i
++)
2543 const char *s
= (const char *)ippGetOctetString(attr
, i
, &len
);
2545 cupsFilePuts(data
->outfile
, i
? "," : " ");
2546 print_ippserver_string(data
, s
, (size_t)len
);
2551 case IPP_TAG_TEXTLANG
:
2553 case IPP_TAG_NAMELANG
:
2554 case IPP_TAG_KEYWORD
:
2556 case IPP_TAG_URISCHEME
:
2557 case IPP_TAG_CHARSET
:
2558 case IPP_TAG_LANGUAGE
:
2559 case IPP_TAG_MIMETYPE
:
2560 for (i
= 0; i
< count
; i
++)
2562 const char *s
= ippGetString(attr
, i
, NULL
);
2564 cupsFilePuts(data
->outfile
, i
? "," : " ");
2565 print_ippserver_string(data
, s
, strlen(s
));
2569 case IPP_TAG_BEGIN_COLLECTION
:
2570 for (i
= 0; i
< count
; i
++)
2572 ipp_t
*col
= ippGetCollection(attr
, i
);
2574 cupsFilePuts(data
->outfile
, i
? ",{\n" : " {\n");
2575 for (colattr
= ippFirstAttribute(col
); colattr
; colattr
= ippNextAttribute(col
))
2576 print_ippserver_attr(data
, colattr
, indent
+ 4);
2577 cupsFilePrintf(data
->outfile
, "%*s}", indent
, "");
2582 cupsFilePuts(data
->outfile
, " \"\"");
2586 cupsFilePuts(data
->outfile
, "\n");
2591 * 'print_ippserver_string()' - Print a string suitable for use by ippserver.
2595 print_ippserver_string(
2596 _cups_testdata_t
*data
, /* I - Test data */
2597 const char *s
, /* I - String to print */
2598 size_t len
) /* I - Length of string */
2600 cupsFilePutChar(data
->outfile
, '\"');
2604 cupsFilePutChar(data
->outfile
, '\\');
2605 cupsFilePutChar(data
->outfile
, *s
);
2610 cupsFilePutChar(data
->outfile
, '\"');
2615 * 'print_line()' - Print a line of formatted or CSV text.
2620 _cups_testdata_t
*data
, /* I - Test data */
2621 ipp_t
*ipp
, /* I - Response message */
2622 ipp_attribute_t
*attr
, /* I - First attribute for line */
2623 int num_displayed
, /* I - Number of attributes to display */
2624 char **displayed
, /* I - Attributes to display */
2625 size_t *widths
) /* I - Column widths */
2627 int i
; /* Looping var */
2628 size_t maxlength
; /* Max length of all columns */
2629 char *buffer
; /* String buffer */
2630 ipp_attribute_t
*current
; /* Current attribute */
2634 * Get the maximum string length we have to show and allocate...
2637 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
2638 if (widths
[i
] > maxlength
)
2639 maxlength
= widths
[i
];
2643 if ((buffer
= malloc(maxlength
)) == NULL
)
2647 * Loop through the attributes to display...
2652 for (i
= 0; i
< num_displayed
; i
++)
2655 cupsFilePutChar(cupsFileStdout(), ' ');
2659 for (current
= attr
; current
; current
= ippNextAttribute(ipp
))
2661 if (!ippGetName(current
))
2663 else if (!strcmp(ippGetName(current
), displayed
[i
]))
2665 ippAttributeString(current
, buffer
, maxlength
);
2670 cupsFilePrintf(data
->outfile
, "%*s", (int)-widths
[i
], buffer
);
2672 cupsFilePutChar(cupsFileStdout(), '\n');
2676 for (i
= 0; i
< num_displayed
; i
++)
2679 cupsFilePutChar(cupsFileStdout(), ' ');
2681 cupsFilePrintf(data
->outfile
, "%*s", (int)-widths
[i
], displayed
[i
]);
2683 cupsFilePutChar(cupsFileStdout(), '\n');
2685 for (i
= 0; i
< num_displayed
; i
++)
2688 cupsFilePutChar(cupsFileStdout(), ' ');
2690 memset(buffer
, '-', widths
[i
]);
2691 buffer
[widths
[i
]] = '\0';
2692 cupsFilePuts(data
->outfile
, buffer
);
2694 cupsFilePutChar(cupsFileStdout(), '\n');
2702 * 'print_xml_header()' - Print a standard XML plist header.
2706 print_xml_header(_cups_testdata_t
*data
)/* I - Test data */
2708 if (!data
->xml_header
)
2710 cupsFilePuts(data
->outfile
, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
2711 cupsFilePuts(data
->outfile
, "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n");
2712 cupsFilePuts(data
->outfile
, "<plist version=\"1.0\">\n");
2713 cupsFilePuts(data
->outfile
, "<dict>\n");
2714 cupsFilePuts(data
->outfile
, "<key>ipptoolVersion</key>\n");
2715 cupsFilePuts(data
->outfile
, "<string>" CUPS_SVERSION
"</string>\n");
2716 cupsFilePuts(data
->outfile
, "<key>Transfer</key>\n");
2717 cupsFilePrintf(data
->outfile
, "<string>%s</string>\n", data
->transfer
== _CUPS_TRANSFER_AUTO
? "auto" : data
->transfer
== _CUPS_TRANSFER_CHUNKED
? "chunked" : "length");
2718 cupsFilePuts(data
->outfile
, "<key>Tests</key>\n");
2719 cupsFilePuts(data
->outfile
, "<array>\n");
2721 data
->xml_header
= 1;
2727 * 'print_xml_string()' - Print an XML string with escaping.
2731 print_xml_string(cups_file_t
*outfile
, /* I - Test data */
2732 const char *element
, /* I - Element name or NULL */
2733 const char *s
) /* I - String to print */
2736 cupsFilePrintf(outfile
, "<%s>", element
);
2741 cupsFilePuts(outfile
, "&");
2743 cupsFilePuts(outfile
, "<");
2745 cupsFilePuts(outfile
, ">");
2746 else if ((*s
& 0xe0) == 0xc0)
2749 * Validate UTF-8 two-byte sequence...
2752 if ((s
[1] & 0xc0) != 0x80)
2754 cupsFilePutChar(outfile
, '?');
2759 cupsFilePutChar(outfile
, *s
++);
2760 cupsFilePutChar(outfile
, *s
);
2763 else if ((*s
& 0xf0) == 0xe0)
2766 * Validate UTF-8 three-byte sequence...
2769 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80)
2771 cupsFilePutChar(outfile
, '?');
2776 cupsFilePutChar(outfile
, *s
++);
2777 cupsFilePutChar(outfile
, *s
++);
2778 cupsFilePutChar(outfile
, *s
);
2781 else if ((*s
& 0xf8) == 0xf0)
2784 * Validate UTF-8 four-byte sequence...
2787 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80 ||
2788 (s
[3] & 0xc0) != 0x80)
2790 cupsFilePutChar(outfile
, '?');
2795 cupsFilePutChar(outfile
, *s
++);
2796 cupsFilePutChar(outfile
, *s
++);
2797 cupsFilePutChar(outfile
, *s
++);
2798 cupsFilePutChar(outfile
, *s
);
2801 else if ((*s
& 0x80) || (*s
< ' ' && !isspace(*s
& 255)))
2804 * Invalid control character...
2807 cupsFilePutChar(outfile
, '?');
2810 cupsFilePutChar(outfile
, *s
);
2816 cupsFilePrintf(outfile
, "</%s>\n", element
);
2821 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
2826 _cups_testdata_t
*data
, /* I - Test data */
2827 int success
, /* I - 1 on success, 0 on failure */
2828 const char *message
) /* I - Error message or NULL */
2830 if (data
->xml_header
)
2832 cupsFilePuts(data
->outfile
, "</array>\n");
2833 cupsFilePuts(data
->outfile
, "<key>Successful</key>\n");
2834 cupsFilePuts(data
->outfile
, success
? "<true />\n" : "<false />\n");
2837 cupsFilePuts(data
->outfile
, "<key>ErrorMessage</key>\n");
2838 print_xml_string(data
->outfile
, "string", message
);
2840 cupsFilePuts(data
->outfile
, "</dict>\n");
2841 cupsFilePuts(data
->outfile
, "</plist>\n");
2843 data
->xml_header
= 0;
2850 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
2854 sigterm_handler(int sig
) /* I - Signal number (unused) */
2860 signal(SIGINT
, SIG_DFL
);
2861 signal(SIGTERM
, SIG_DFL
);
2867 * 'timeout_cb()' - Handle HTTP timeouts.
2870 static int /* O - 1 to continue, 0 to cancel */
2871 timeout_cb(http_t
*http
, /* I - Connection to server */
2872 void *user_data
) /* I - User data (unused) */
2874 int buffered
= 0; /* Bytes buffered but not yet sent */
2880 * If the socket still have data waiting to be sent to the printer (as can
2881 * happen if the printer runs out of paper), continue to wait until the output
2882 * buffer is empty...
2885 #ifdef SO_NWRITE /* macOS and some versions of Linux */
2886 socklen_t len
= sizeof(buffered
); /* Size of return value */
2888 if (getsockopt(httpGetFd(http
), SOL_SOCKET
, SO_NWRITE
, &buffered
, &len
))
2891 #elif defined(SIOCOUTQ) /* Others except Windows */
2892 if (ioctl(httpGetFd(http
), SIOCOUTQ
, &buffered
))
2895 #else /* Windows (not possible) */
2897 #endif /* SO_NWRITE */
2899 return (buffered
> 0);
2904 * 'token_cb()' - Parse test file-specific tokens and run tests.
2907 static int /* O - 1 to continue, 0 to stop */
2908 token_cb(_ipp_file_t
*f
, /* I - IPP file data */
2909 _ipp_vars_t
*vars
, /* I - IPP variables */
2910 _cups_testdata_t
*data
, /* I - Test data */
2911 const char *token
) /* I - Current token */
2913 char name
[1024], /* Name string */
2914 temp
[1024], /* Temporary string */
2915 value
[1024], /* Value string */
2916 *ptr
; /* Pointer into value */
2922 * Initialize state as needed (nothing for now...)
2930 * Parse until we see a close brace...
2933 if (_cups_strcasecmp(token
, "COUNT") &&
2934 _cups_strcasecmp(token
, "DEFINE-MATCH") &&
2935 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
2936 _cups_strcasecmp(token
, "DEFINE-VALUE") &&
2937 _cups_strcasecmp(token
, "IF-DEFINED") &&
2938 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
2939 _cups_strcasecmp(token
, "IN-GROUP") &&
2940 _cups_strcasecmp(token
, "OF-TYPE") &&
2941 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
2942 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
2943 _cups_strcasecmp(token
, "REPEAT-NO-MATCH") &&
2944 _cups_strcasecmp(token
, "SAME-COUNT-AS") &&
2945 _cups_strcasecmp(token
, "WITH-ALL-VALUES") &&
2946 _cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") &&
2947 _cups_strcasecmp(token
, "WITH-ALL-RESOURCES") &&
2948 _cups_strcasecmp(token
, "WITH-ALL-SCHEMES") &&
2949 _cups_strcasecmp(token
, "WITH-HOSTNAME") &&
2950 _cups_strcasecmp(token
, "WITH-RESOURCE") &&
2951 _cups_strcasecmp(token
, "WITH-SCHEME") &&
2952 _cups_strcasecmp(token
, "WITH-VALUE") &&
2953 _cups_strcasecmp(token
, "WITH-VALUE-FROM"))
2954 data
->last_expect
= NULL
;
2956 if (_cups_strcasecmp(token
, "DEFINE-MATCH") &&
2957 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
2958 _cups_strcasecmp(token
, "IF-DEFINED") &&
2959 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
2960 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
2961 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
2962 _cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
2963 data
->last_status
= NULL
;
2965 if (!strcmp(token
, "}"))
2967 return (do_test(f
, vars
, data
));
2969 else if (!strcmp(token
, "COMPRESSION"))
2973 * COMPRESSION deflate
2977 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
2979 _ippVarsExpand(vars
, data
->compression
, temp
, sizeof(data
->compression
));
2981 if (strcmp(data
->compression
, "none") && strcmp(data
->compression
, "deflate") &&
2982 strcmp(data
->compression
, "gzip"))
2984 if (strcmp(data
->compression
, "none"))
2985 #endif /* HAVE_LIBZ */
2987 print_fatal_error(data
, "Unsupported COMPRESSION value \"%s\" on line %d of \"%s\".", data
->compression
, f
->linenum
, f
->filename
);
2991 if (!strcmp(data
->compression
, "none"))
2992 data
->compression
[0] = '\0';
2996 print_fatal_error(data
, "Missing COMPRESSION value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3000 else if (!strcmp(token
, "DEFINE"))
3006 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
3008 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3009 _ippVarsSet(vars
, name
, value
);
3013 print_fatal_error(data
, "Missing DEFINE name and/or value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3017 else if (!strcmp(token
, "IGNORE-ERRORS"))
3024 if (_ippFileReadToken(f
, temp
, sizeof(temp
)) && (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
3026 data
->ignore_errors
= !_cups_strcasecmp(temp
, "yes");
3030 print_fatal_error(data
, "Missing IGNORE-ERRORS value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3034 else if (!_cups_strcasecmp(token
, "NAME"))
3040 _ippFileReadToken(f
, temp
, sizeof(temp
));
3041 _ippVarsExpand(vars
, data
->name
, temp
, sizeof(data
->name
));
3043 else if (!_cups_strcasecmp(token
, "PAUSE"))
3046 * Pause with a message...
3049 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3051 pause_message(temp
);
3055 print_fatal_error(data
, "Missing PAUSE message on line %d of \"%s\".", f
->linenum
, f
->filename
);
3059 else if (!strcmp(token
, "REQUEST-ID"))
3066 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3068 if (isdigit(temp
[0] & 255))
3070 data
->request_id
= atoi(temp
);
3072 else if (!_cups_strcasecmp(temp
, "random"))
3074 data
->request_id
= (CUPS_RAND() % 1000) * 137 + 1;
3078 print_fatal_error(data
, "Bad REQUEST-ID value \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
3084 print_fatal_error(data
, "Missing REQUEST-ID value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3088 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
3091 * SKIP-IF-DEFINED variable
3094 if (_ippFileReadToken(f
, name
, sizeof(name
)))
3096 if (_ippVarsGet(vars
, name
))
3097 data
->skip_test
= 1;
3101 print_fatal_error(data
, "Missing SKIP-IF-DEFINED value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3105 else if (!strcmp(token
, "SKIP-IF-MISSING"))
3108 * SKIP-IF-MISSING filename
3111 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3113 char filename
[1024]; /* Filename */
3115 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3116 get_filename(f
->filename
, filename
, temp
, sizeof(filename
));
3118 if (access(filename
, R_OK
))
3119 data
->skip_test
= 1;
3123 print_fatal_error(data
, "Missing SKIP-IF-MISSING filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
3127 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
3130 * SKIP-IF-NOT-DEFINED variable
3133 if (_ippFileReadToken(f
, name
, sizeof(name
)))
3135 if (!_ippVarsGet(vars
, name
))
3136 data
->skip_test
= 1;
3140 print_fatal_error(data
, "Missing SKIP-IF-NOT-DEFINED value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3144 else if (!strcmp(token
, "SKIP-PREVIOUS-ERROR"))
3147 * SKIP-PREVIOUS-ERROR yes
3148 * SKIP-PREVIOUS-ERROR no
3151 if (_ippFileReadToken(f
, temp
, sizeof(temp
)) && (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
3153 data
->skip_previous
= !_cups_strcasecmp(temp
, "yes");
3157 print_fatal_error(data
, "Missing SKIP-PREVIOUS-ERROR value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3161 else if (!strcmp(token
, "TEST-ID"))
3167 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3169 _ippVarsExpand(vars
, data
->test_id
, temp
, sizeof(data
->test_id
));
3173 print_fatal_error(data
, "Missing TEST-ID value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3177 else if (!strcmp(token
, "TRANSFER"))
3185 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3187 if (!strcmp(temp
, "auto"))
3189 data
->transfer
= _CUPS_TRANSFER_AUTO
;
3191 else if (!strcmp(temp
, "chunked"))
3193 data
->transfer
= _CUPS_TRANSFER_CHUNKED
;
3195 else if (!strcmp(temp
, "length"))
3197 data
->transfer
= _CUPS_TRANSFER_LENGTH
;
3201 print_fatal_error(data
, "Bad TRANSFER value \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
3207 print_fatal_error(data
, "Missing TRANSFER value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3211 else if (!_cups_strcasecmp(token
, "VERSION"))
3213 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3215 if (!strcmp(temp
, "0.0"))
3219 else if (!strcmp(temp
, "1.0"))
3223 else if (!strcmp(temp
, "1.1"))
3227 else if (!strcmp(temp
, "2.0"))
3231 else if (!strcmp(temp
, "2.1"))
3235 else if (!strcmp(temp
, "2.2"))
3241 print_fatal_error(data
, "Bad VERSION \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
3247 print_fatal_error(data
, "Missing VERSION number on line %d of \"%s\".", f
->linenum
, f
->filename
);
3251 else if (!_cups_strcasecmp(token
, "RESOURCE"))
3257 if (!_ippFileReadToken(f
, data
->resource
, sizeof(data
->resource
)))
3259 print_fatal_error(data
, "Missing RESOURCE path on line %d of \"%s\".", f
->linenum
, f
->filename
);
3263 else if (!_cups_strcasecmp(token
, "OPERATION"))
3269 ipp_op_t op
; /* Operation code */
3271 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3273 print_fatal_error(data
, "Missing OPERATION code on line %d of \"%s\".", f
->linenum
, f
->filename
);
3277 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3279 if ((op
= ippOpValue(value
)) == (ipp_op_t
)-1 && (op
= (ipp_op_t
)strtol(value
, NULL
, 0)) == 0)
3281 print_fatal_error(data
, "Bad OPERATION code \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
3285 ippSetOperation(f
->attrs
, op
);
3287 else if (!_cups_strcasecmp(token
, "GROUP"))
3290 * Attribute group...
3293 ipp_tag_t group_tag
; /* Group tag */
3295 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3297 print_fatal_error(data
, "Missing GROUP tag on line %d of \"%s\".", f
->linenum
, f
->filename
);
3301 if ((group_tag
= ippTagValue(temp
)) == IPP_TAG_ZERO
|| group_tag
>= IPP_TAG_UNSUPPORTED_VALUE
)
3303 print_fatal_error(data
, "Bad GROUP tag \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
3307 if (group_tag
== f
->group_tag
)
3308 ippAddSeparator(f
->attrs
);
3310 f
->group_tag
= group_tag
;
3312 else if (!_cups_strcasecmp(token
, "DELAY"))
3315 * Delay before operation...
3318 double dval
; /* Delay value */
3320 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3322 print_fatal_error(data
, "Missing DELAY value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3326 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3328 if ((dval
= _cupsStrScand(value
, &ptr
, localeconv())) < 0.0 || (*ptr
&& *ptr
!= ','))
3330 print_fatal_error(data
, "Bad DELAY value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
3334 data
->delay
= (useconds_t
)(1000000.0 * dval
);
3338 if ((dval
= _cupsStrScand(ptr
+ 1, &ptr
, localeconv())) <= 0.0 || *ptr
)
3340 print_fatal_error(data
, "Bad DELAY value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
3344 data
->repeat_interval
= (useconds_t
)(1000000.0 * dval
);
3347 data
->repeat_interval
= data
->delay
;
3349 else if (!_cups_strcasecmp(token
, "FILE"))
3355 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3357 print_fatal_error(data
, "Missing FILE filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
3361 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3362 get_filename(f
->filename
, data
->file
, value
, sizeof(data
->file
));
3364 if (access(data
->file
, R_OK
))
3366 print_fatal_error(data
, "Filename \"%s\" (mapped to \"%s\") on line %d of \"%s\" cannot be read.", value
, data
->file
, f
->linenum
, f
->filename
);
3370 else if (!_cups_strcasecmp(token
, "STATUS"))
3376 if (data
->num_statuses
>= (int)(sizeof(data
->statuses
) / sizeof(data
->statuses
[0])))
3378 print_fatal_error(data
, "Too many STATUS's on line %d of \"%s\".", f
->linenum
, f
->filename
);
3382 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3384 print_fatal_error(data
, "Missing STATUS code on line %d of \"%s\".", f
->linenum
, f
->filename
);
3388 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)
3390 print_fatal_error(data
, "Bad STATUS code \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
3394 data
->last_status
= data
->statuses
+ data
->num_statuses
;
3395 data
->num_statuses
++;
3397 data
->last_status
->define_match
= NULL
;
3398 data
->last_status
->define_no_match
= NULL
;
3399 data
->last_status
->if_defined
= NULL
;
3400 data
->last_status
->if_not_defined
= NULL
;
3401 data
->last_status
->repeat_limit
= 1000;
3402 data
->last_status
->repeat_match
= 0;
3403 data
->last_status
->repeat_no_match
= 0;
3405 else if (!_cups_strcasecmp(token
, "EXPECT") || !_cups_strcasecmp(token
, "EXPECT-ALL"))
3408 * Expected attributes...
3411 int expect_all
= !_cups_strcasecmp(token
, "EXPECT-ALL");
3413 if (data
->num_expects
>= (int)(sizeof(data
->expects
) / sizeof(data
->expects
[0])))
3415 print_fatal_error(data
, "Too many EXPECT's on line %d of \"%s\".", f
->linenum
, f
->filename
);
3419 if (!_ippFileReadToken(f
, name
, sizeof(name
)))
3421 print_fatal_error(data
, "Missing EXPECT name on line %d of \"%s\".", f
->linenum
, f
->filename
);
3425 data
->last_expect
= data
->expects
+ data
->num_expects
;
3426 data
->num_expects
++;
3428 memset(data
->last_expect
, 0, sizeof(_cups_expect_t
));
3429 data
->last_expect
->repeat_limit
= 1000;
3430 data
->last_expect
->expect_all
= expect_all
;
3434 data
->last_expect
->not_expect
= 1;
3435 data
->last_expect
->name
= strdup(name
+ 1);
3437 else if (name
[0] == '?')
3439 data
->last_expect
->optional
= 1;
3440 data
->last_expect
->name
= strdup(name
+ 1);
3443 data
->last_expect
->name
= strdup(name
);
3445 else if (!_cups_strcasecmp(token
, "COUNT"))
3447 int count
; /* Count value */
3449 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3451 print_fatal_error(data
, "Missing COUNT number on line %d of \"%s\".", f
->linenum
, f
->filename
);
3455 if ((count
= atoi(temp
)) <= 0)
3457 print_fatal_error(data
, "Bad COUNT \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
3461 if (data
->last_expect
)
3463 data
->last_expect
->count
= count
;
3467 print_fatal_error(data
, "COUNT without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
3471 else if (!_cups_strcasecmp(token
, "DEFINE-MATCH"))
3473 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3475 print_fatal_error(data
, "Missing DEFINE-MATCH variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
3479 if (data
->last_expect
)
3481 data
->last_expect
->define_match
= strdup(temp
);
3483 else if (data
->last_status
)
3485 data
->last_status
->define_match
= strdup(temp
);
3489 print_fatal_error(data
, "DEFINE-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
3493 else if (!_cups_strcasecmp(token
, "DEFINE-NO-MATCH"))
3495 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3497 print_fatal_error(data
, "Missing DEFINE-NO-MATCH variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
3501 if (data
->last_expect
)
3503 data
->last_expect
->define_no_match
= strdup(temp
);
3505 else if (data
->last_status
)
3507 data
->last_status
->define_no_match
= strdup(temp
);
3511 print_fatal_error(data
, "DEFINE-NO-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
3515 else if (!_cups_strcasecmp(token
, "DEFINE-VALUE"))
3517 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3519 print_fatal_error(data
, "Missing DEFINE-VALUE variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
3523 if (data
->last_expect
)
3525 data
->last_expect
->define_value
= strdup(temp
);
3529 print_fatal_error(data
, "DEFINE-VALUE without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
3533 else if (!_cups_strcasecmp(token
, "OF-TYPE"))
3535 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3537 print_fatal_error(data
, "Missing OF-TYPE value tag(s) on line %d of \"%s\".", f
->linenum
, f
->filename
);
3541 if (data
->last_expect
)
3543 data
->last_expect
->of_type
= strdup(temp
);
3547 print_fatal_error(data
, "OF-TYPE without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
3551 else if (!_cups_strcasecmp(token
, "IN-GROUP"))
3553 ipp_tag_t in_group
; /* IN-GROUP value */
3555 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3557 print_fatal_error(data
, "Missing IN-GROUP group tag on line %d of \"%s\".", f
->linenum
, f
->filename
);
3561 if ((in_group
= ippTagValue(temp
)) == IPP_TAG_ZERO
|| in_group
>= IPP_TAG_UNSUPPORTED_VALUE
)
3563 print_fatal_error(data
, "Bad IN-GROUP group tag \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
3566 else if (data
->last_expect
)
3568 data
->last_expect
->in_group
= in_group
;
3572 print_fatal_error(data
, "IN-GROUP without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
3576 else if (!_cups_strcasecmp(token
, "REPEAT-LIMIT"))
3578 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3580 print_fatal_error(data
, "Missing REPEAT-LIMIT value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3583 else if (atoi(temp
) <= 0)
3585 print_fatal_error(data
, "Bad REPEAT-LIMIT value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3589 if (data
->last_status
)
3591 data
->last_status
->repeat_limit
= atoi(temp
);
3593 else if (data
->last_expect
)
3595 data
->last_expect
->repeat_limit
= atoi(temp
);
3599 print_fatal_error(data
, "REPEAT-LIMIT without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
3603 else if (!_cups_strcasecmp(token
, "REPEAT-MATCH"))
3605 if (data
->last_status
)
3607 data
->last_status
->repeat_match
= 1;
3609 else if (data
->last_expect
)
3611 data
->last_expect
->repeat_match
= 1;
3615 print_fatal_error(data
, "REPEAT-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
3619 else if (!_cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
3621 if (data
->last_status
)
3623 data
->last_status
->repeat_no_match
= 1;
3625 else if (data
->last_expect
)
3627 data
->last_expect
->repeat_no_match
= 1;
3631 print_fatal_error(data
, "REPEAT-NO-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
3635 else if (!_cups_strcasecmp(token
, "SAME-COUNT-AS"))
3637 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3639 print_fatal_error(data
, "Missing SAME-COUNT-AS name on line %d of \"%s\".", f
->linenum
, f
->filename
);
3643 if (data
->last_expect
)
3645 data
->last_expect
->same_count_as
= strdup(temp
);
3649 print_fatal_error(data
, "SAME-COUNT-AS without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
3653 else if (!_cups_strcasecmp(token
, "IF-DEFINED"))
3655 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3657 print_fatal_error(data
, "Missing IF-DEFINED name on line %d of \"%s\".", f
->linenum
, f
->filename
);
3661 if (data
->last_expect
)
3663 data
->last_expect
->if_defined
= strdup(temp
);
3665 else if (data
->last_status
)
3667 data
->last_status
->if_defined
= strdup(temp
);
3671 print_fatal_error(data
, "IF-DEFINED without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
3675 else if (!_cups_strcasecmp(token
, "IF-NOT-DEFINED"))
3677 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3679 print_fatal_error(data
, "Missing IF-NOT-DEFINED name on line %d of \"%s\".", f
->linenum
, f
->filename
);
3683 if (data
->last_expect
)
3685 data
->last_expect
->if_not_defined
= strdup(temp
);
3687 else if (data
->last_status
)
3689 data
->last_status
->if_not_defined
= strdup(temp
);
3693 print_fatal_error(data
, "IF-NOT-DEFINED without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
3697 else if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") ||
3698 !_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
3699 !_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
3700 !_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
3701 !_cups_strcasecmp(token
, "WITH-HOSTNAME") ||
3702 !_cups_strcasecmp(token
, "WITH-RESOURCE") ||
3703 !_cups_strcasecmp(token
, "WITH-SCHEME") ||
3704 !_cups_strcasecmp(token
, "WITH-VALUE"))
3706 off_t lastpos
; /* Last file position */
3708 if (data
->last_expect
)
3710 if (!_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") || !_cups_strcasecmp(token
, "WITH-HOSTNAME"))
3711 data
->last_expect
->with_flags
= _CUPS_WITH_HOSTNAME
;
3712 else if (!_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") || !_cups_strcasecmp(token
, "WITH-RESOURCE"))
3713 data
->last_expect
->with_flags
= _CUPS_WITH_RESOURCE
;
3714 else if (!_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") || !_cups_strcasecmp(token
, "WITH-SCHEME"))
3715 data
->last_expect
->with_flags
= _CUPS_WITH_SCHEME
;
3717 if (!_cups_strncasecmp(token
, "WITH-ALL-", 9))
3718 data
->last_expect
->with_flags
|= _CUPS_WITH_ALL
;
3721 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3723 print_fatal_error(data
, "Missing %s value on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
3728 * Read additional comma-delimited values - needed since legacy test files
3729 * will have unquoted WITH-VALUE values with commas...
3732 ptr
= temp
+ strlen(temp
);
3736 lastpos
= cupsFileTell(f
->fp
);
3739 if (!_ippFileReadToken(f
, ptr
, (sizeof(temp
) - (size_t)(ptr
- temp
))))
3742 if (!strcmp(ptr
, ","))
3750 if (!_ippFileReadToken(f
, ptr
, (sizeof(temp
) - (size_t)(ptr
- temp
))))
3756 * Not another value, stop here...
3759 cupsFileSeek(f
->fp
, lastpos
);
3765 if (data
->last_expect
)
3768 * Expand any variables in the value and then save it.
3771 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3773 ptr
= value
+ strlen(value
) - 1;
3775 if (value
[0] == '/' && ptr
> value
&& *ptr
== '/')
3778 * WITH-VALUE is a POSIX extended regular expression.
3781 data
->last_expect
->with_value
= calloc(1, (size_t)(ptr
- value
));
3782 data
->last_expect
->with_flags
|= _CUPS_WITH_REGEX
;
3784 if (data
->last_expect
->with_value
)
3785 memcpy(data
->last_expect
->with_value
, value
+ 1, (size_t)(ptr
- value
- 1));
3790 * WITH-VALUE is a literal value...
3793 for (ptr
= value
; *ptr
; ptr
++)
3795 if (*ptr
== '\\' && ptr
[1])
3798 * Remove \ from \foo...
3801 _cups_strcpy(ptr
, ptr
+ 1);
3805 data
->last_expect
->with_value
= strdup(value
);
3806 data
->last_expect
->with_flags
|= _CUPS_WITH_LITERAL
;
3811 print_fatal_error(data
, "%s without a preceding EXPECT on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
3815 else if (!_cups_strcasecmp(token
, "WITH-VALUE-FROM"))
3817 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3819 print_fatal_error(data
, "Missing %s value on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
3823 if (data
->last_expect
)
3826 * Expand any variables in the value and then save it.
3829 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3831 data
->last_expect
->with_value_from
= strdup(value
);
3832 data
->last_expect
->with_flags
= _CUPS_WITH_LITERAL
;
3836 print_fatal_error(data
, "%s without a preceding EXPECT on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
3840 else if (!_cups_strcasecmp(token
, "DISPLAY"))
3843 * Display attributes...
3846 if (data
->num_displayed
>= (int)(sizeof(data
->displayed
) / sizeof(data
->displayed
[0])))
3848 print_fatal_error(data
, "Too many DISPLAY's on line %d of \"%s\".", f
->linenum
, f
->filename
);
3852 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3854 print_fatal_error(data
, "Missing DISPLAY name on line %d of \"%s\".", f
->linenum
, f
->filename
);
3858 data
->displayed
[data
->num_displayed
] = strdup(temp
);
3859 data
->num_displayed
++;
3863 print_fatal_error(data
, "Unexpected token %s seen on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
3870 * Scan for the start of a test (open brace)...
3873 if (!strcmp(token
, "{"))
3879 if (data
->show_header
)
3881 if (data
->output
== _CUPS_OUTPUT_PLIST
)
3882 print_xml_header(data
);
3884 if (data
->output
== _CUPS_OUTPUT_TEST
|| (data
->output
== _CUPS_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
3885 cupsFilePrintf(cupsFileStdout(), "\"%s\":\n", f
->filename
);
3887 data
->show_header
= 0;
3890 data
->compression
[0] = '\0';
3892 data
->num_expects
= 0;
3893 data
->last_expect
= NULL
;
3894 data
->file
[0] = '\0';
3895 data
->ignore_errors
= data
->def_ignore_errors
;
3896 strlcpy(data
->name
, f
->filename
, sizeof(data
->name
));
3897 if ((ptr
= strrchr(data
->name
, '.')) != NULL
)
3899 data
->repeat_interval
= 5000000;
3900 data
->request_id
++;
3901 strlcpy(data
->resource
, vars
->resource
, sizeof(data
->resource
));
3902 data
->skip_previous
= 0;
3903 data
->skip_test
= 0;
3904 data
->num_statuses
= 0;
3905 data
->last_status
= NULL
;
3906 data
->test_id
[0] = '\0';
3907 data
->transfer
= data
->def_transfer
;
3908 data
->version
= data
->def_version
;
3910 f
->attrs
= ippNew();
3911 f
->group_tag
= IPP_TAG_ZERO
;
3913 else if (!strcmp(token
, "DEFINE"))
3919 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
3921 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3922 _ippVarsSet(vars
, name
, value
);
3926 print_fatal_error(data
, "Missing DEFINE name and/or value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3930 else if (!strcmp(token
, "DEFINE-DEFAULT"))
3933 * DEFINE-DEFAULT name value
3936 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
3938 if (!_ippVarsGet(vars
, name
))
3940 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3941 _ippVarsSet(vars
, name
, value
);
3946 print_fatal_error(data
, "Missing DEFINE-DEFAULT name and/or value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3950 else if (!strcmp(token
, "FILE-ID"))
3956 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3958 _ippVarsExpand(vars
, data
->file_id
, temp
, sizeof(data
->file_id
));
3962 print_fatal_error(data
, "Missing FILE-ID value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3966 else if (!strcmp(token
, "IGNORE-ERRORS"))
3973 if (_ippFileReadToken(f
, temp
, sizeof(temp
)) && (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
3975 data
->def_ignore_errors
= !_cups_strcasecmp(temp
, "yes");
3979 print_fatal_error(data
, "Missing IGNORE-ERRORS value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3983 else if (!strcmp(token
, "INCLUDE"))
3986 * INCLUDE "filename"
3987 * INCLUDE <filename>
3990 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3993 * Map the filename to and then run the tests...
3996 _cups_testdata_t inc_data
; /* Data for included file */
3997 char filename
[1024]; /* Mapped filename */
3999 memcpy(&inc_data
, data
, sizeof(inc_data
));
4000 inc_data
.http
= NULL
;
4002 inc_data
.prev_pass
= 1;
4003 inc_data
.show_header
= 1;
4005 if (!do_tests(get_filename(f
->filename
, filename
, temp
, sizeof(filename
)), vars
, &inc_data
) && data
->stop_after_include_error
)
4007 data
->pass
= data
->prev_pass
= 0;
4013 print_fatal_error(data
, "Missing INCLUDE filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
4017 data
->show_header
= 1;
4019 else if (!strcmp(token
, "INCLUDE-IF-DEFINED"))
4022 * INCLUDE-IF-DEFINED name "filename"
4023 * INCLUDE-IF-DEFINED name <filename>
4026 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
4029 * Map the filename to and then run the tests...
4032 _cups_testdata_t inc_data
; /* Data for included file */
4033 char filename
[1024]; /* Mapped filename */
4035 memcpy(&inc_data
, data
, sizeof(inc_data
));
4036 inc_data
.http
= NULL
;
4038 inc_data
.prev_pass
= 1;
4039 inc_data
.show_header
= 1;
4041 if (!do_tests(get_filename(f
->filename
, filename
, temp
, sizeof(filename
)), vars
, &inc_data
) && data
->stop_after_include_error
)
4043 data
->pass
= data
->prev_pass
= 0;
4049 print_fatal_error(data
, "Missing INCLUDE-IF-DEFINED name or filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
4053 data
->show_header
= 1;
4055 else if (!strcmp(token
, "INCLUDE-IF-NOT-DEFINED"))
4058 * INCLUDE-IF-NOT-DEFINED name "filename"
4059 * INCLUDE-IF-NOT-DEFINED name <filename>
4062 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
4065 * Map the filename to and then run the tests...
4068 _cups_testdata_t inc_data
; /* Data for included file */
4069 char filename
[1024]; /* Mapped filename */
4071 memcpy(&inc_data
, data
, sizeof(inc_data
));
4072 inc_data
.http
= NULL
;
4074 inc_data
.prev_pass
= 1;
4075 inc_data
.show_header
= 1;
4077 if (!do_tests(get_filename(f
->filename
, filename
, temp
, sizeof(filename
)), vars
, &inc_data
) && data
->stop_after_include_error
)
4079 data
->pass
= data
->prev_pass
= 0;
4085 print_fatal_error(data
, "Missing INCLUDE-IF-NOT-DEFINED name or filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
4089 data
->show_header
= 1;
4091 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
4094 * SKIP-IF-DEFINED variable
4097 if (_ippFileReadToken(f
, name
, sizeof(name
)))
4099 if (_ippVarsGet(vars
, name
))
4100 data
->skip_test
= 1;
4104 print_fatal_error(data
, "Missing SKIP-IF-DEFINED variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
4108 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
4111 * SKIP-IF-NOT-DEFINED variable
4114 if (_ippFileReadToken(f
, name
, sizeof(name
)))
4116 if (!_ippVarsGet(vars
, name
))
4117 data
->skip_test
= 1;
4121 print_fatal_error(data
, "Missing SKIP-IF-NOT-DEFINED variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
4125 else if (!strcmp(token
, "STOP-AFTER-INCLUDE-ERROR"))
4128 * STOP-AFTER-INCLUDE-ERROR yes
4129 * STOP-AFTER-INCLUDE-ERROR no
4132 if (_ippFileReadToken(f
, temp
, sizeof(temp
)) && (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
4134 data
->stop_after_include_error
= !_cups_strcasecmp(temp
, "yes");
4138 print_fatal_error(data
, "Missing STOP-AFTER-INCLUDE-ERROR value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4142 else if (!strcmp(token
, "TRANSFER"))
4150 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
4152 if (!strcmp(temp
, "auto"))
4153 data
->def_transfer
= _CUPS_TRANSFER_AUTO
;
4154 else if (!strcmp(temp
, "chunked"))
4155 data
->def_transfer
= _CUPS_TRANSFER_CHUNKED
;
4156 else if (!strcmp(temp
, "length"))
4157 data
->def_transfer
= _CUPS_TRANSFER_LENGTH
;
4160 print_fatal_error(data
, "Bad TRANSFER value \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
4166 print_fatal_error(data
, "Missing TRANSFER value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4170 else if (!strcmp(token
, "VERSION"))
4172 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
4174 if (!strcmp(temp
, "1.0"))
4175 data
->def_version
= 10;
4176 else if (!strcmp(temp
, "1.1"))
4177 data
->def_version
= 11;
4178 else if (!strcmp(temp
, "2.0"))
4179 data
->def_version
= 20;
4180 else if (!strcmp(temp
, "2.1"))
4181 data
->def_version
= 21;
4182 else if (!strcmp(temp
, "2.2"))
4183 data
->def_version
= 22;
4186 print_fatal_error(data
, "Bad VERSION \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
4192 print_fatal_error(data
, "Missing VERSION number on line %d of \"%s\".", f
->linenum
, f
->filename
);
4198 print_fatal_error(data
, "Unexpected token %s seen on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
4208 * 'usage()' - Show program usage.
4214 _cupsLangPuts(stderr
, _("Usage: ipptool [options] URI filename [ ... filenameN ]"));
4215 _cupsLangPuts(stderr
, _("Options:"));
4216 _cupsLangPuts(stderr
, _(" --help Show help."));
4217 _cupsLangPuts(stderr
, _(" --ippserver filename Produce ippserver attribute file."));
4218 _cupsLangPuts(stderr
, _(" --stop-after-include-error\n"
4219 " Stop tests after a failed INCLUDE."));
4220 _cupsLangPuts(stderr
, _(" --version Show version."));
4221 _cupsLangPuts(stderr
, _(" -4 Connect using IPv4."));
4222 _cupsLangPuts(stderr
, _(" -6 Connect using IPv6."));
4223 _cupsLangPuts(stderr
, _(" -C Send requests using "
4224 "chunking (default)."));
4225 _cupsLangPuts(stderr
, _(" -E Test with encryption using HTTP Upgrade to TLS."));
4226 _cupsLangPuts(stderr
, _(" -I Ignore errors."));
4227 _cupsLangPuts(stderr
, _(" -L Send requests using content-length."));
4228 _cupsLangPuts(stderr
, _(" -P filename.plist Produce XML plist to a file and test report to standard output."));
4229 _cupsLangPuts(stderr
, _(" -S Test with encryption using HTTPS."));
4230 _cupsLangPuts(stderr
, _(" -T seconds Set the receive/send timeout in seconds."));
4231 _cupsLangPuts(stderr
, _(" -V version Set default IPP version."));
4232 _cupsLangPuts(stderr
, _(" -X Produce XML plist instead of plain text."));
4233 _cupsLangPuts(stderr
, _(" -c Produce CSV output."));
4234 _cupsLangPuts(stderr
, _(" -d name=value Set named variable to value."));
4235 _cupsLangPuts(stderr
, _(" -f filename Set default request filename."));
4236 _cupsLangPuts(stderr
, _(" -h Validate HTTP response headers."));
4237 _cupsLangPuts(stderr
, _(" -i seconds Repeat the last file with the given time interval."));
4238 _cupsLangPuts(stderr
, _(" -l Produce plain text output."));
4239 _cupsLangPuts(stderr
, _(" -n count Repeat the last file the given number of times."));
4240 _cupsLangPuts(stderr
, _(" -q Run silently."));
4241 _cupsLangPuts(stderr
, _(" -t Produce a test report."));
4242 _cupsLangPuts(stderr
, _(" -v Be verbose."));
4249 * 'validate_attr()' - Determine whether an attribute is valid.
4252 static int /* O - 1 if valid, 0 otherwise */
4253 validate_attr(_cups_testdata_t
*data
, /* I - Test data */
4254 cups_array_t
*errors
, /* I - Errors array */
4255 ipp_attribute_t
*attr
) /* I - Attribute to validate */
4257 int i
, /* Looping var */
4258 count
; /* Number of values */
4259 char scheme
[64], /* Scheme from URI */
4260 userpass
[256], /* Username/password from URI */
4261 hostname
[256], /* Hostname from URI */
4262 resource
[1024]; /* Resource from URI */
4263 int port
, /* Port number from URI */
4264 uri_status
, /* URI separation status */
4265 valid
= 1; /* Is the attribute valid? */
4266 const char *name
, /* Attribute name */
4267 *ptr
; /* Pointer into string */
4268 ipp_attribute_t
*colattr
; /* Collection attribute */
4269 regex_t re
; /* Regular expression */
4276 if ((name
= ippGetName(attr
)) == NULL
)
4280 * Validate the attribute name.
4283 for (ptr
= name
; *ptr
; ptr
++)
4284 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' && *ptr
!= '_')
4287 if (*ptr
|| ptr
== name
)
4291 add_stringf(data
->errors
,
4292 "\"%s\": Bad attribute name - invalid character "
4293 "(RFC 2911 section 4.1.3).", name
);
4296 if ((ptr
- ippGetName(attr
)) > 255)
4300 add_stringf(data
->errors
,
4301 "\"%s\": Bad attribute name - bad length "
4302 "(RFC 2911 section 4.1.3).", name
);
4305 count
= ippGetCount(attr
);
4307 switch (ippGetValueTag(attr
))
4309 case IPP_TAG_INTEGER
:
4312 case IPP_TAG_BOOLEAN
:
4313 for (i
= 0; i
< count
; i
++)
4315 int b
= ippGetBoolean(attr
, i
);
4318 if (b
!= 0 && b
!= 1)
4322 add_stringf(data
->errors
, "\"%s\": Bad boolen value %d (RFC 2911 section 4.1.11).", name
, b
);
4328 for (i
= 0; i
< count
; i
++)
4330 if (ippGetInteger(attr
, i
) < 1)
4334 add_stringf(data
->errors
, "\"%s\": Bad enum value %d - out of range (RFC 2911 section 4.1.4).", name
, ippGetInteger(attr
, i
));
4339 case IPP_TAG_STRING
:
4340 for (i
= 0; i
< count
; i
++)
4342 int datalen
; /* Length of string */
4344 ippGetOctetString(attr
, i
, &datalen
);
4345 if (datalen
> IPP_MAX_OCTETSTRING
)
4349 add_stringf(data
->errors
, "\"%s\": Bad octetString value - bad length %d (RFC 2911 section 4.1.10).", name
, datalen
);
4355 for (i
= 0; i
< count
; i
++)
4357 const ipp_uchar_t
*date
= ippGetDate(attr
, i
);
4358 /* Current date value */
4360 if (date
[2] < 1 || date
[2] > 12)
4364 add_stringf(data
->errors
,
4365 "\"%s\": Bad dateTime month %u "
4366 "(RFC 2911 section 4.1.14).", name
, date
[2]);
4369 if (date
[3] < 1 || date
[3] > 31)
4373 add_stringf(data
->errors
,
4374 "\"%s\": Bad dateTime day %u "
4375 "(RFC 2911 section 4.1.14).", name
, date
[3]);
4382 add_stringf(data
->errors
,
4383 "\"%s\": Bad dateTime hours %u "
4384 "(RFC 2911 section 4.1.14).", name
, date
[4]);
4391 add_stringf(data
->errors
,
4392 "\"%s\": Bad dateTime minutes %u "
4393 "(RFC 2911 section 4.1.14).", name
, date
[5]);
4400 add_stringf(data
->errors
,
4401 "\"%s\": Bad dateTime seconds %u "
4402 "(RFC 2911 section 4.1.14).", name
, date
[6]);
4409 add_stringf(data
->errors
,
4410 "\"%s\": Bad dateTime deciseconds %u "
4411 "(RFC 2911 section 4.1.14).", name
, date
[7]);
4414 if (date
[8] != '-' && date
[8] != '+')
4418 add_stringf(data
->errors
,
4419 "\"%s\": Bad dateTime UTC sign '%c' "
4420 "(RFC 2911 section 4.1.14).", name
, date
[8]);
4427 add_stringf(data
->errors
,
4428 "\"%s\": Bad dateTime UTC hours %u "
4429 "(RFC 2911 section 4.1.14).", name
, date
[9]);
4436 add_stringf(data
->errors
,
4437 "\"%s\": Bad dateTime UTC minutes %u "
4438 "(RFC 2911 section 4.1.14).", name
, date
[10]);
4443 case IPP_TAG_RESOLUTION
:
4444 for (i
= 0; i
< count
; i
++)
4446 int xres
, yres
; /* Resolution values */
4447 ipp_res_t units
; /* Resolution units */
4449 xres
= ippGetResolution(attr
, i
, &yres
, &units
);
4455 add_stringf(data
->errors
, "\"%s\": Bad resolution value %dx%d%s - cross feed resolution must be positive (RFC 2911 section 4.1.15).", name
, xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : units
== IPP_RES_PER_CM
? "dpcm" : "unknown");
4462 add_stringf(data
->errors
, "\"%s\": Bad resolution value %dx%d%s - feed resolution must be positive (RFC 2911 section 4.1.15).", name
, xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : units
== IPP_RES_PER_CM
? "dpcm" : "unknown");
4465 if (units
!= IPP_RES_PER_INCH
&& units
!= IPP_RES_PER_CM
)
4469 add_stringf(data
->errors
, "\"%s\": Bad resolution value %dx%d%s - bad units value (RFC 2911 section 4.1.15).", name
, xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : units
== IPP_RES_PER_CM
? "dpcm" : "unknown");
4474 case IPP_TAG_RANGE
:
4475 for (i
= 0; i
< count
; i
++)
4477 int lower
, upper
; /* Range values */
4479 lower
= ippGetRange(attr
, i
, &upper
);
4485 add_stringf(data
->errors
, "\"%s\": Bad rangeOfInteger value %d-%d - lower greater than upper (RFC 2911 section 4.1.13).", name
, lower
, upper
);
4490 case IPP_TAG_BEGIN_COLLECTION
:
4491 for (i
= 0; i
< count
; i
++)
4493 ipp_t
*col
= ippGetCollection(attr
, i
);
4494 /* Collection value */
4496 for (colattr
= ippFirstAttribute(col
); colattr
; colattr
= ippNextAttribute(col
))
4498 if (!validate_attr(data
, NULL
, colattr
))
4505 if (colattr
&& errors
)
4507 add_stringf(data
->errors
, "\"%s\": Bad collection value.", name
);
4511 validate_attr(data
, errors
, colattr
);
4512 colattr
= ippNextAttribute(col
);
4519 case IPP_TAG_TEXTLANG
:
4520 for (i
= 0; i
< count
; i
++)
4522 const char *s
= ippGetString(attr
, i
, NULL
);
4525 for (ptr
= s
; *ptr
; ptr
++)
4527 if ((*ptr
& 0xe0) == 0xc0)
4530 if ((*ptr
& 0xc0) != 0x80)
4533 else if ((*ptr
& 0xf0) == 0xe0)
4536 if ((*ptr
& 0xc0) != 0x80)
4539 if ((*ptr
& 0xc0) != 0x80)
4542 else if ((*ptr
& 0xf8) == 0xf0)
4545 if ((*ptr
& 0xc0) != 0x80)
4548 if ((*ptr
& 0xc0) != 0x80)
4551 if ((*ptr
& 0xc0) != 0x80)
4554 else if (*ptr
& 0x80)
4562 add_stringf(data
->errors
, "\"%s\": Bad text value \"%s\" - bad UTF-8 sequence (RFC 2911 section 4.1.1).", name
, s
);
4565 if ((ptr
- s
) > (IPP_MAX_TEXT
- 1))
4569 add_stringf(data
->errors
, "\"%s\": Bad text value \"%s\" - bad length %d (RFC 2911 section 4.1.1).", name
, s
, (int)strlen(s
));
4575 case IPP_TAG_NAMELANG
:
4576 for (i
= 0; i
< count
; i
++)
4578 const char *s
= ippGetString(attr
, i
, NULL
);
4581 for (ptr
= s
; *ptr
; ptr
++)
4583 if ((*ptr
& 0xe0) == 0xc0)
4586 if ((*ptr
& 0xc0) != 0x80)
4589 else if ((*ptr
& 0xf0) == 0xe0)
4592 if ((*ptr
& 0xc0) != 0x80)
4595 if ((*ptr
& 0xc0) != 0x80)
4598 else if ((*ptr
& 0xf8) == 0xf0)
4601 if ((*ptr
& 0xc0) != 0x80)
4604 if ((*ptr
& 0xc0) != 0x80)
4607 if ((*ptr
& 0xc0) != 0x80)
4610 else if (*ptr
& 0x80)
4618 add_stringf(data
->errors
, "\"%s\": Bad name value \"%s\" - bad UTF-8 sequence (RFC 2911 section 4.1.2).", name
, s
);
4621 if ((ptr
- s
) > (IPP_MAX_NAME
- 1))
4625 add_stringf(data
->errors
, "\"%s\": Bad name value \"%s\" - bad length %d (RFC 2911 section 4.1.2).", name
, s
, (int)strlen(s
));
4630 case IPP_TAG_KEYWORD
:
4631 for (i
= 0; i
< count
; i
++)
4633 const char *keyword
= ippGetString(attr
, i
, NULL
);
4636 for (ptr
= keyword
; *ptr
; ptr
++)
4637 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' &&
4641 if (*ptr
|| ptr
== keyword
)
4645 add_stringf(data
->errors
, "\"%s\": Bad keyword value \"%s\" - invalid character (RFC 2911 section 4.1.3).", name
, keyword
);
4648 if ((ptr
- keyword
) > (IPP_MAX_KEYWORD
- 1))
4652 add_stringf(data
->errors
, "\"%s\": Bad keyword value \"%s\" - bad length %d (RFC 2911 section 4.1.3).", name
, keyword
, (int)strlen(keyword
));
4658 for (i
= 0; i
< count
; i
++)
4660 const char *uri
= ippGetString(attr
, i
, NULL
);
4663 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, uri
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
));
4665 if (uri_status
< HTTP_URI_OK
)
4669 add_stringf(data
->errors
, "\"%s\": Bad URI value \"%s\" - %s (RFC 2911 section 4.1.5).", name
, uri
, httpURIStatusString(uri_status
));
4672 if (strlen(uri
) > (IPP_MAX_URI
- 1))
4676 add_stringf(data
->errors
, "\"%s\": Bad URI value \"%s\" - bad length %d (RFC 2911 section 4.1.5).", name
, uri
, (int)strlen(uri
));
4681 case IPP_TAG_URISCHEME
:
4682 for (i
= 0; i
< count
; i
++)
4684 const char *urischeme
= ippGetString(attr
, i
, NULL
);
4685 /* URI scheme value */
4688 if (islower(*ptr
& 255))
4690 for (ptr
++; *ptr
; ptr
++)
4691 if (!islower(*ptr
& 255) && !isdigit(*ptr
& 255) &&
4692 *ptr
!= '+' && *ptr
!= '-' && *ptr
!= '.')
4696 if (*ptr
|| ptr
== urischeme
)
4700 add_stringf(data
->errors
, "\"%s\": Bad uriScheme value \"%s\" - bad characters (RFC 2911 section 4.1.6).", name
, urischeme
);
4703 if ((ptr
- urischeme
) > (IPP_MAX_URISCHEME
- 1))
4707 add_stringf(data
->errors
, "\"%s\": Bad uriScheme value \"%s\" - bad length %d (RFC 2911 section 4.1.6).", name
, urischeme
, (int)strlen(urischeme
));
4712 case IPP_TAG_CHARSET
:
4713 for (i
= 0; i
< count
; i
++)
4715 const char *charset
= ippGetString(attr
, i
, NULL
);
4718 for (ptr
= charset
; *ptr
; ptr
++)
4719 if (!isprint(*ptr
& 255) || isupper(*ptr
& 255) ||
4720 isspace(*ptr
& 255))
4723 if (*ptr
|| ptr
== charset
)
4727 add_stringf(data
->errors
, "\"%s\": Bad charset value \"%s\" - bad characters (RFC 2911 section 4.1.7).", name
, charset
);
4730 if ((ptr
- charset
) > (IPP_MAX_CHARSET
- 1))
4734 add_stringf(data
->errors
, "\"%s\": Bad charset value \"%s\" - bad length %d (RFC 2911 section 4.1.7).", name
, charset
, (int)strlen(charset
));
4739 case IPP_TAG_LANGUAGE
:
4741 * The following regular expression is derived from the ABNF for
4742 * language tags in RFC 4646. All I can say is that this is the
4743 * easiest way to check the values...
4746 if ((i
= regcomp(&re
,
4748 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
4750 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
4751 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
4752 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
4753 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
4754 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
4756 "x(-[a-z0-9]{1,8})+" /* privateuse */
4758 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
4760 REG_NOSUB
| REG_EXTENDED
)) != 0)
4762 char temp
[256]; /* Temporary error string */
4764 regerror(i
, &re
, temp
, sizeof(temp
));
4765 print_fatal_error(data
, "Unable to compile naturalLanguage regular "
4766 "expression: %s.", temp
);
4770 for (i
= 0; i
< count
; i
++)
4772 const char *lang
= ippGetString(attr
, i
, NULL
);
4773 /* Language string */
4775 if (regexec(&re
, lang
, 0, NULL
, 0))
4779 add_stringf(data
->errors
, "\"%s\": Bad naturalLanguage value \"%s\" - bad characters (RFC 2911 section 4.1.8).", name
, lang
);
4782 if (strlen(lang
) > (IPP_MAX_LANGUAGE
- 1))
4786 add_stringf(data
->errors
, "\"%s\": Bad naturalLanguage value \"%s\" - bad length %d (RFC 2911 section 4.1.8).", name
, lang
, (int)strlen(lang
));
4793 case IPP_TAG_MIMETYPE
:
4795 * The following regular expression is derived from the ABNF for
4796 * language tags in RFC 2045 and 4288. All I can say is that this is
4797 * the easiest way to check the values...
4800 if ((i
= regcomp(&re
,
4802 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
4804 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
4805 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
4806 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
4809 REG_NOSUB
| REG_EXTENDED
)) != 0)
4811 char temp
[256]; /* Temporary error string */
4813 regerror(i
, &re
, temp
, sizeof(temp
));
4814 print_fatal_error(data
, "Unable to compile mimeMediaType regular "
4815 "expression: %s.", temp
);
4819 for (i
= 0; i
< count
; i
++)
4821 const char *mimetype
= ippGetString(attr
, i
, NULL
);
4822 /* Mime media type string */
4824 if (regexec(&re
, mimetype
, 0, NULL
, 0))
4828 add_stringf(data
->errors
, "\"%s\": Bad mimeMediaType value \"%s\" - bad characters (RFC 2911 section 4.1.9).", name
, mimetype
);
4831 if (strlen(mimetype
) > (IPP_MAX_MIMETYPE
- 1))
4835 add_stringf(data
->errors
, "\"%s\": Bad mimeMediaType value \"%s\" - bad length %d (RFC 2911 section 4.1.9).", name
, mimetype
, (int)strlen(mimetype
));
4851 * 'with_flags_string()' - Return the "WITH-xxx" predicate that corresponds to
4855 static const char * /* O - WITH-xxx string */
4856 with_flags_string(int flags
) /* I - WITH flags */
4858 if (flags
& _CUPS_WITH_ALL
)
4860 if (flags
& _CUPS_WITH_HOSTNAME
)
4861 return ("WITH-ALL-HOSTNAMES");
4862 else if (flags
& _CUPS_WITH_RESOURCE
)
4863 return ("WITH-ALL-RESOURCES");
4864 else if (flags
& _CUPS_WITH_SCHEME
)
4865 return ("WITH-ALL-SCHEMES");
4867 return ("WITH-ALL-VALUES");
4869 else if (flags
& _CUPS_WITH_HOSTNAME
)
4870 return ("WITH-HOSTNAME");
4871 else if (flags
& _CUPS_WITH_RESOURCE
)
4872 return ("WITH-RESOURCE");
4873 else if (flags
& _CUPS_WITH_SCHEME
)
4874 return ("WITH-SCHEME");
4876 return ("WITH-VALUE");
4881 * 'with_value()' - Test a WITH-VALUE predicate.
4884 static int /* O - 1 on match, 0 on non-match */
4885 with_value(_cups_testdata_t
*data
, /* I - Test data */
4886 cups_array_t
*errors
, /* I - Errors array */
4887 char *value
, /* I - Value string */
4888 int flags
, /* I - Flags for match */
4889 ipp_attribute_t
*attr
, /* I - Attribute to compare */
4890 char *matchbuf
, /* I - Buffer to hold matching value */
4891 size_t matchlen
) /* I - Length of match buffer */
4893 int i
, /* Looping var */
4894 count
, /* Number of values */
4896 char temp
[1024], /* Temporary value string */
4897 *valptr
; /* Pointer into value */
4898 const char *name
; /* Attribute name */
4902 match
= (flags
& _CUPS_WITH_ALL
) ? 1 : 0;
4905 * NULL matches everything.
4908 if (!value
|| !*value
)
4912 * Compare the value string to the attribute value.
4915 name
= ippGetName(attr
);
4916 count
= ippGetCount(attr
);
4918 switch (ippGetValueTag(attr
))
4920 case IPP_TAG_INTEGER
:
4922 for (i
= 0; i
< ippGetCount(attr
); i
++)
4924 char op
, /* Comparison operator */
4925 *nextptr
; /* Next pointer */
4926 int intvalue
, /* Integer value */
4927 attrvalue
= ippGetInteger(attr
, i
),
4928 /* Attribute value */
4929 valmatch
= 0; /* Does the current value match? */
4933 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
4934 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
4935 *valptr
== '=' || *valptr
== '>')
4938 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
4940 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
4948 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
4949 if (nextptr
== valptr
)
4953 if ((op
== '=' && attrvalue
== intvalue
) ||
4954 (op
== '<' && attrvalue
< intvalue
) ||
4955 (op
== '>' && attrvalue
> intvalue
))
4958 snprintf(matchbuf
, matchlen
, "%d", attrvalue
);
4965 if (flags
& _CUPS_WITH_ALL
)
4980 if (!match
&& errors
)
4982 for (i
= 0; i
< ippGetCount(attr
); i
++)
4983 add_stringf(data
->errors
, "GOT: %s=%d", name
, ippGetInteger(attr
, i
));
4987 case IPP_TAG_RANGE
:
4988 for (i
= 0; i
< ippGetCount(attr
); i
++)
4990 char op
, /* Comparison operator */
4991 *nextptr
; /* Next pointer */
4992 int intvalue
, /* Integer value */
4993 lower
, /* Lower range */
4994 upper
, /* Upper range */
4995 valmatch
= 0; /* Does the current value match? */
4997 lower
= ippGetRange(attr
, i
, &upper
);
5000 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5001 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5002 *valptr
== '=' || *valptr
== '>')
5005 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5007 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5015 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
5016 if (nextptr
== valptr
)
5020 if ((op
== '=' && (lower
== intvalue
|| upper
== intvalue
)) ||
5021 (op
== '<' && upper
< intvalue
) ||
5022 (op
== '>' && upper
> intvalue
))
5025 snprintf(matchbuf
, matchlen
, "%d-%d", lower
, upper
);
5032 if (flags
& _CUPS_WITH_ALL
)
5047 if (!match
&& errors
)
5049 for (i
= 0; i
< ippGetCount(attr
); i
++)
5051 int lower
, upper
; /* Range values */
5053 lower
= ippGetRange(attr
, i
, &upper
);
5054 add_stringf(data
->errors
, "GOT: %s=%d-%d", name
, lower
, upper
);
5059 case IPP_TAG_BOOLEAN
:
5060 for (i
= 0; i
< ippGetCount(attr
); i
++)
5062 if ((!strcmp(value
, "true")) == ippGetBoolean(attr
, i
))
5065 strlcpy(matchbuf
, value
, matchlen
);
5067 if (!(flags
& _CUPS_WITH_ALL
))
5073 else if (flags
& _CUPS_WITH_ALL
)
5080 if (!match
&& errors
)
5082 for (i
= 0; i
< ippGetCount(attr
); i
++)
5083 add_stringf(data
->errors
, "GOT: %s=%s", name
, ippGetBoolean(attr
, i
) ? "true" : "false");
5087 case IPP_TAG_RESOLUTION
:
5088 for (i
= 0; i
< ippGetCount(attr
); i
++)
5090 int xres
, yres
; /* Resolution values */
5091 ipp_res_t units
; /* Resolution units */
5093 xres
= ippGetResolution(attr
, i
, &yres
, &units
);
5095 snprintf(temp
, sizeof(temp
), "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5097 snprintf(temp
, sizeof(temp
), "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5099 if (!strcmp(value
, temp
))
5102 strlcpy(matchbuf
, value
, matchlen
);
5104 if (!(flags
& _CUPS_WITH_ALL
))
5110 else if (flags
& _CUPS_WITH_ALL
)
5117 if (!match
&& errors
)
5119 for (i
= 0; i
< ippGetCount(attr
); i
++)
5121 int xres
, yres
; /* Resolution values */
5122 ipp_res_t units
; /* Resolution units */
5124 xres
= ippGetResolution(attr
, i
, &yres
, &units
);
5126 snprintf(temp
, sizeof(temp
), "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5128 snprintf(temp
, sizeof(temp
), "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5130 if (strcmp(value
, temp
))
5131 add_stringf(data
->errors
, "GOT: %s=%s", name
, temp
);
5136 case IPP_TAG_NOVALUE
:
5137 case IPP_TAG_UNKNOWN
:
5140 case IPP_TAG_CHARSET
:
5141 case IPP_TAG_KEYWORD
:
5142 case IPP_TAG_LANGUAGE
:
5143 case IPP_TAG_MIMETYPE
:
5145 case IPP_TAG_NAMELANG
:
5147 case IPP_TAG_TEXTLANG
:
5149 case IPP_TAG_URISCHEME
:
5150 if (flags
& _CUPS_WITH_REGEX
)
5153 * Value is an extended, case-sensitive POSIX regular expression...
5156 regex_t re
; /* Regular expression */
5158 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
5160 regerror(i
, &re
, temp
, sizeof(temp
));
5162 print_fatal_error(data
, "Unable to compile WITH-VALUE regular expression \"%s\" - %s", value
, temp
);
5167 * See if ALL of the values match the given regular expression.
5170 for (i
= 0; i
< ippGetCount(attr
); i
++)
5172 if (!regexec(&re
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5176 strlcpy(matchbuf
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)), matchlen
);
5178 if (!(flags
& _CUPS_WITH_ALL
))
5184 else if (flags
& _CUPS_WITH_ALL
)
5193 else if (ippGetValueTag(attr
) == IPP_TAG_URI
&& !(flags
& (_CUPS_WITH_SCHEME
| _CUPS_WITH_HOSTNAME
| _CUPS_WITH_RESOURCE
)))
5196 * Value is a literal URI string, see if the value(s) match...
5199 for (i
= 0; i
< ippGetCount(attr
); i
++)
5201 if (!compare_uris(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
))))
5204 strlcpy(matchbuf
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)), matchlen
);
5206 if (!(flags
& _CUPS_WITH_ALL
))
5212 else if (flags
& _CUPS_WITH_ALL
)
5222 * Value is a literal string, see if the value(s) match...
5225 for (i
= 0; i
< ippGetCount(attr
); i
++)
5229 switch (ippGetValueTag(attr
))
5233 * Some URI components are case-sensitive, some not...
5236 if (flags
& (_CUPS_WITH_SCHEME
| _CUPS_WITH_HOSTNAME
))
5237 result
= _cups_strcasecmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
5239 result
= strcmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
5242 case IPP_TAG_MIMETYPE
:
5244 case IPP_TAG_NAMELANG
:
5246 case IPP_TAG_TEXTLANG
:
5248 * mimeMediaType, nameWithoutLanguage, nameWithLanguage,
5249 * textWithoutLanguage, and textWithLanguage are defined to
5250 * be case-insensitive strings...
5253 result
= _cups_strcasecmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
5258 * Other string syntaxes are defined as lowercased so we use
5259 * case-sensitive comparisons to catch problems...
5262 result
= strcmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
5269 strlcpy(matchbuf
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)), matchlen
);
5271 if (!(flags
& _CUPS_WITH_ALL
))
5277 else if (flags
& _CUPS_WITH_ALL
)
5285 if (!match
&& errors
)
5287 for (i
= 0; i
< ippGetCount(attr
); i
++)
5288 add_stringf(data
->errors
, "GOT: %s=\"%s\"", name
, ippGetString(attr
, i
, NULL
));
5301 * 'with_value_from()' - Test a WITH-VALUE-FROM predicate.
5304 static int /* O - 1 on match, 0 on non-match */
5306 cups_array_t
*errors
, /* I - Errors array */
5307 ipp_attribute_t
*fromattr
, /* I - "From" attribute */
5308 ipp_attribute_t
*attr
, /* I - Attribute to compare */
5309 char *matchbuf
, /* I - Buffer to hold matching value */
5310 size_t matchlen
) /* I - Length of match buffer */
5312 int i
, j
, /* Looping vars */
5313 count
= ippGetCount(attr
), /* Number of attribute values */
5314 match
= 1; /* Match? */
5320 * Compare the from value(s) to the attribute value(s)...
5323 switch (ippGetValueTag(attr
))
5325 case IPP_TAG_INTEGER
:
5326 if (ippGetValueTag(fromattr
) != IPP_TAG_INTEGER
&& ippGetValueTag(fromattr
) != IPP_TAG_RANGE
)
5327 goto wrong_value_tag
;
5329 for (i
= 0; i
< count
; i
++)
5331 int value
= ippGetInteger(attr
, i
);
5332 /* Current integer value */
5334 if (ippContainsInteger(fromattr
, value
))
5337 snprintf(matchbuf
, matchlen
, "%d", value
);
5341 add_stringf(errors
, "GOT: %s=%d", ippGetName(attr
), value
);
5348 if (ippGetValueTag(fromattr
) != IPP_TAG_ENUM
)
5349 goto wrong_value_tag
;
5351 for (i
= 0; i
< count
; i
++)
5353 int value
= ippGetInteger(attr
, i
);
5354 /* Current integer value */
5356 if (ippContainsInteger(fromattr
, value
))
5359 snprintf(matchbuf
, matchlen
, "%d", value
);
5363 add_stringf(errors
, "GOT: %s=%d", ippGetName(attr
), value
);
5369 case IPP_TAG_RESOLUTION
:
5370 if (ippGetValueTag(fromattr
) != IPP_TAG_RESOLUTION
)
5371 goto wrong_value_tag
;
5373 for (i
= 0; i
< count
; i
++)
5377 int fromcount
= ippGetCount(fromattr
);
5378 int fromxres
, fromyres
;
5379 ipp_res_t fromunits
;
5381 xres
= ippGetResolution(attr
, i
, &yres
, &units
);
5383 for (j
= 0; j
< fromcount
; j
++)
5385 fromxres
= ippGetResolution(fromattr
, j
, &fromyres
, &fromunits
);
5386 if (fromxres
== xres
&& fromyres
== yres
&& fromunits
== units
)
5395 snprintf(matchbuf
, matchlen
, "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5397 snprintf(matchbuf
, matchlen
, "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5403 add_stringf(errors
, "GOT: %s=%d%s", ippGetName(attr
), xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5405 add_stringf(errors
, "GOT: %s=%dx%d%s", ippGetName(attr
), xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5412 case IPP_TAG_NOVALUE
:
5413 case IPP_TAG_UNKNOWN
:
5416 case IPP_TAG_CHARSET
:
5417 case IPP_TAG_KEYWORD
:
5418 case IPP_TAG_LANGUAGE
:
5419 case IPP_TAG_MIMETYPE
:
5421 case IPP_TAG_NAMELANG
:
5423 case IPP_TAG_TEXTLANG
:
5424 case IPP_TAG_URISCHEME
:
5425 for (i
= 0; i
< count
; i
++)
5427 const char *value
= ippGetString(attr
, i
, NULL
);
5428 /* Current string value */
5430 if (ippContainsString(fromattr
, value
))
5433 strlcpy(matchbuf
, value
, matchlen
);
5437 add_stringf(errors
, "GOT: %s='%s'", ippGetName(attr
), value
);
5444 for (i
= 0; i
< count
; i
++)
5446 const char *value
= ippGetString(attr
, i
, NULL
);
5447 /* Current string value */
5448 int fromcount
= ippGetCount(fromattr
);
5450 for (j
= 0; j
< fromcount
; j
++)
5452 if (!compare_uris(value
, ippGetString(fromattr
, j
, NULL
)))
5455 strlcpy(matchbuf
, value
, matchlen
);
5462 add_stringf(errors
, "GOT: %s='%s'", ippGetName(attr
), value
);
5475 /* value tag mismatch between fromattr and attr */
5478 add_stringf(errors
, "GOT: %s OF-TYPE %s", ippGetName(attr
), ippTagString(ippGetValueTag(attr
)));