2 * ipptool command for CUPS.
4 * Copyright 2007-2017 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products.
7 * These coded instructions, statements, and computer programs are the
8 * property of Apple Inc. and are protected by Federal copyright
9 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
10 * which should have been included with this file. If this file is
11 * missing or damaged, see the license at "http://www.cups.org/".
13 * This file is subject to the Apple OS-Developed Software exception.
17 * Include necessary headers...
20 #include <cups/cups-private.h>
21 #include <cups/file-private.h>
35 #endif /* !O_BINARY */
42 typedef enum _cups_transfer_e
/**** How to send request data ****/
44 _CUPS_TRANSFER_AUTO
, /* Chunk for files, length for static */
45 _CUPS_TRANSFER_CHUNKED
, /* Chunk always */
46 _CUPS_TRANSFER_LENGTH
/* Length always */
49 typedef enum _cups_output_e
/**** Output mode ****/
51 _CUPS_OUTPUT_QUIET
, /* No output */
52 _CUPS_OUTPUT_TEST
, /* Traditional CUPS test output */
53 _CUPS_OUTPUT_PLIST
, /* XML plist test output */
54 _CUPS_OUTPUT_LIST
, /* Tabular list output */
55 _CUPS_OUTPUT_CSV
/* Comma-separated values output */
58 typedef enum _cups_with_e
/**** WITH flags ****/
60 _CUPS_WITH_LITERAL
= 0, /* Match string is a literal value */
61 _CUPS_WITH_ALL
= 1, /* Must match all values */
62 _CUPS_WITH_REGEX
= 2, /* Match string is a regular expression */
63 _CUPS_WITH_HOSTNAME
= 4, /* Match string is a URI hostname */
64 _CUPS_WITH_RESOURCE
= 8, /* Match string is a URI resource */
65 _CUPS_WITH_SCHEME
= 16 /* Match string is a URI scheme */
68 typedef struct _cups_expect_s
/**** Expected attribute info ****/
70 int optional
, /* Optional attribute? */
71 not_expect
, /* Don't expect attribute? */
72 expect_all
; /* Expect all attributes to match/not match */
73 char *name
, /* Attribute name */
74 *of_type
, /* Type name */
75 *same_count_as
, /* Parallel attribute name */
76 *if_defined
, /* Only required if variable defined */
77 *if_not_defined
, /* Only required if variable is not defined */
78 *with_value
, /* Attribute must include this value */
79 *with_value_from
, /* Attribute must have one of the values in this attribute */
80 *define_match
, /* Variable to define on match */
81 *define_no_match
, /* Variable to define on no-match */
82 *define_value
; /* Variable to define with value */
83 int repeat_limit
, /* Maximum number of times to repeat */
84 repeat_match
, /* Repeat test on match */
85 repeat_no_match
, /* Repeat test on no match */
86 with_flags
, /* WITH flags */
87 count
; /* Expected count if > 0 */
88 ipp_tag_t in_group
; /* IN-GROUP value */
91 typedef struct _cups_status_s
/**** Status info ****/
93 ipp_status_t status
; /* Expected status code */
94 char *if_defined
, /* Only if variable is defined */
95 *if_not_defined
, /* Only if variable is not defined */
96 *define_match
, /* Variable to define on match */
97 *define_no_match
, /* Variable to define on no-match */
98 *define_value
; /* Variable to define with value */
99 int repeat_limit
, /* Maximum number of times to repeat */
100 repeat_match
, /* Repeat the test when it does not match */
101 repeat_no_match
; /* Repeat the test when it matches */
104 typedef struct _cups_var_s
/**** Variable ****/
106 char *name
, /* Name of variable */
107 *value
; /* Value of variable */
110 typedef struct _cups_vars_s
/**** Set of variables ****/
112 char *uri
, /* URI for printer */
113 *filename
, /* Filename */
114 scheme
[64], /* Scheme from URI */
115 userpass
[256], /* Username/password from URI */
116 hostname
[256], /* Hostname from URI */
117 resource
[1024]; /* Resource path from URI */
118 int port
; /* Port number from URI */
119 http_encryption_t encryption
; /* Encryption for connection? */
120 double timeout
; /* Timeout for connection */
121 int family
; /* Address family */
122 cups_array_t
*vars
; /* Array of variables */
130 static _cups_transfer_t Transfer
= _CUPS_TRANSFER_AUTO
;
131 /* How to transfer requests */
132 static _cups_output_t Output
= _CUPS_OUTPUT_LIST
;
134 static int Cancel
= 0, /* Cancel test? */
135 IgnoreErrors
= 0, /* Ignore errors? */
136 StopAfterIncludeError
= 0,
137 /* Stop after include errors? */
138 ValidateHeaders
= 0, /* Validate HTTP headers in response? */
139 Verbosity
= 0, /* Show all attributes? */
140 Version
= 11, /* Default IPP version */
141 XMLHeader
= 0, /* 1 if header is written */
142 TestCount
= 0, /* Number of tests run */
143 PassCount
= 0, /* Number of passing tests */
144 FailCount
= 0, /* Number of failing tests */
145 SkipCount
= 0; /* Number of skipped tests */
146 static char *Username
= NULL
, /* Username from URI */
147 *Password
= NULL
; /* Password from URI */
148 static int PasswordTries
= 0; /* Number of tries with password */
155 static void add_stringf(cups_array_t
*a
, const char *s
, ...) __attribute__ ((__format__ (__printf__
, 2, 3)));
156 static int compare_uris(const char *a
, const char *b
);
157 static int compare_vars(_cups_var_t
*a
, _cups_var_t
*b
);
158 static int do_tests(cups_file_t
*outfile
, _cups_vars_t
*vars
, const char *testfile
);
159 static void expand_variables(_cups_vars_t
*vars
, char *dst
, const char *src
, size_t dstsize
) __attribute__((nonnull(1,2,3)));
160 static int expect_matches(_cups_expect_t
*expect
, ipp_tag_t value_tag
);
161 static ipp_t
*get_collection(cups_file_t
*outfile
, _cups_vars_t
*vars
, cups_file_t
*fp
, int *linenum
);
162 static char *get_filename(const char *testfile
, char *dst
, const char *src
, size_t dstsize
);
163 static const char *get_string(ipp_attribute_t
*attr
, int element
, int flags
, char *buffer
, size_t bufsize
);
164 static char *get_token(cups_file_t
*fp
, char *buf
, int buflen
, int *linenum
);
165 static char *get_variable(_cups_vars_t
*vars
, const char *name
);
166 static char *iso_date(ipp_uchar_t
*date
);
167 static const char *password_cb(const char *prompt
);
168 static void pause_message(const char *message
);
169 static void print_attr(cups_file_t
*outfile
, int format
, ipp_attribute_t
*attr
, ipp_tag_t
*group
);
170 static void print_csv(cups_file_t
*outfile
, ipp_attribute_t
*attr
, int num_displayed
, char **displayed
, size_t *widths
);
171 static void print_fatal_error(cups_file_t
*outfile
, const char *s
, ...) __attribute__ ((__format__ (__printf__
, 2, 3)));
172 static void print_line(cups_file_t
*outfile
, ipp_attribute_t
*attr
, int num_displayed
, char **displayed
, size_t *widths
);
173 static void print_xml_header(cups_file_t
*outfile
);
174 static void print_xml_string(cups_file_t
*outfile
, const char *element
, const char *s
);
175 static void print_xml_trailer(cups_file_t
*outfile
, int success
, const char *message
);
176 static void set_variable(cups_file_t
*outfile
, _cups_vars_t
*vars
, const char *name
, const char *value
);
178 static void sigterm_handler(int sig
);
180 static int timeout_cb(http_t
*http
, void *user_data
);
181 static void usage(void) __attribute__((noreturn
));
182 static int validate_attr(cups_file_t
*outfile
, cups_array_t
*errors
, ipp_attribute_t
*attr
);
183 static const char *with_flags_string(int flags
);
184 static int with_value(cups_file_t
*outfile
, cups_array_t
*errors
, char *value
, int flags
, ipp_attribute_t
*attr
, char *matchbuf
, size_t matchlen
);
185 static int with_value_from(cups_array_t
*errors
, ipp_attribute_t
*fromattr
, ipp_attribute_t
*attr
, char *matchbuf
, size_t matchlen
);
189 * 'main()' - Parse options and do tests.
192 int /* O - Exit status */
193 main(int argc
, /* I - Number of command-line args */
194 char *argv
[]) /* I - Command-line arguments */
196 int i
; /* Looping var */
197 int status
; /* Status of tests... */
198 cups_file_t
*outfile
= cupsFileStdout();
200 char *opt
, /* Current option */
201 name
[1024], /* Name/value buffer */
202 *value
, /* Pointer to value */
203 filename
[1024], /* Real filename */
204 testname
[1024], /* Real test filename */
205 uri
[1024]; /* Copy of printer URI */
206 const char *ext
, /* Extension on filename */
207 *testfile
; /* Test file to use */
208 int interval
, /* Test interval in microseconds */
209 repeat
; /* Repeat count */
210 _cups_vars_t vars
; /* Variables */
211 http_uri_status_t uri_status
; /* URI separation status */
212 _cups_globals_t
*cg
= _cupsGlobals();
218 * Catch SIGINT and SIGTERM...
221 signal(SIGINT
, sigterm_handler
);
222 signal(SIGTERM
, sigterm_handler
);
226 * Initialize the locale and variables...
229 _cupsSetLocale(argv
);
231 memset(&vars
, 0, sizeof(vars
));
232 vars
.family
= AF_UNSPEC
;
233 vars
.vars
= cupsArrayNew((cups_array_func_t
)compare_vars
, NULL
);
238 * ipptool URI testfile
246 for (i
= 1; i
< argc
; i
++)
248 if (!strcmp(argv
[i
], "--help"))
252 else if (!strcmp(argv
[i
], "--stop-after-include-error"))
254 StopAfterIncludeError
= 1;
256 else if (!strcmp(argv
[i
], "--version"))
261 else if (argv
[i
][0] == '-')
263 for (opt
= argv
[i
] + 1; *opt
; opt
++)
267 case '4' : /* Connect using IPv4 only */
268 vars
.family
= AF_INET
;
272 case '6' : /* Connect using IPv6 only */
273 vars
.family
= AF_INET6
;
275 #endif /* AF_INET6 */
277 case 'C' : /* Enable HTTP chunking */
278 Transfer
= _CUPS_TRANSFER_CHUNKED
;
281 case 'E' : /* Encrypt with TLS */
283 vars
.encryption
= HTTP_ENCRYPT_REQUIRED
;
285 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
287 #endif /* HAVE_SSL */
290 case 'I' : /* Ignore errors */
294 case 'L' : /* Disable HTTP chunking */
295 Transfer
= _CUPS_TRANSFER_LENGTH
;
298 case 'P' : /* Output to plist file */
303 _cupsLangPrintf(stderr
, _("%s: Missing filename for \"-P\"."), "ipptool");
307 if (outfile
!= cupsFileStdout())
310 if ((outfile
= cupsFileOpen(argv
[i
], "w")) == NULL
)
312 _cupsLangPrintf(stderr
, _("%s: Unable to open \"%s\": %s"), "ipptool", argv
[i
], strerror(errno
));
316 Output
= _CUPS_OUTPUT_PLIST
;
318 if (interval
|| repeat
)
320 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
325 case 'S' : /* Encrypt with SSL */
327 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
329 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
331 #endif /* HAVE_SSL */
334 case 'T' : /* Set timeout */
339 _cupsLangPrintf(stderr
,
340 _("%s: Missing timeout for \"-T\"."),
345 vars
.timeout
= _cupsStrScand(argv
[i
], NULL
, localeconv());
348 case 'V' : /* Set IPP version */
353 _cupsLangPrintf(stderr
,
354 _("%s: Missing version for \"-V\"."),
359 if (!strcmp(argv
[i
], "1.0"))
361 else if (!strcmp(argv
[i
], "1.1"))
363 else if (!strcmp(argv
[i
], "2.0"))
365 else if (!strcmp(argv
[i
], "2.1"))
367 else if (!strcmp(argv
[i
], "2.2"))
371 _cupsLangPrintf(stderr
,
372 _("%s: Bad version %s for \"-V\"."),
378 case 'X' : /* Produce XML output */
379 Output
= _CUPS_OUTPUT_PLIST
;
381 if (interval
|| repeat
)
383 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
388 case 'c' : /* CSV output */
389 Output
= _CUPS_OUTPUT_CSV
;
392 case 'd' : /* Define a variable */
397 _cupsLangPuts(stderr
,
398 _("ipptool: Missing name=value for \"-d\"."));
402 strlcpy(name
, argv
[i
], sizeof(name
));
403 if ((value
= strchr(name
, '=')) != NULL
)
406 value
= name
+ strlen(name
);
408 set_variable(outfile
, &vars
, name
, value
);
411 case 'f' : /* Set the default test filename */
416 _cupsLangPuts(stderr
,
417 _("ipptool: Missing filename for \"-f\"."));
424 vars
.filename
= NULL
;
427 if (access(argv
[i
], 0))
433 snprintf(filename
, sizeof(filename
), "%s.gz", argv
[i
]);
434 if (access(filename
, 0) && filename
[0] != '/'
436 && (!isalpha(filename
[0] & 255) || filename
[1] != ':')
440 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s",
441 cg
->cups_datadir
, argv
[i
]);
442 if (access(filename
, 0))
444 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s.gz",
445 cg
->cups_datadir
, argv
[i
]);
446 if (access(filename
, 0))
447 vars
.filename
= strdup(argv
[i
]);
449 vars
.filename
= strdup(filename
);
452 vars
.filename
= strdup(filename
);
455 vars
.filename
= strdup(filename
);
458 vars
.filename
= strdup(argv
[i
]);
460 if ((ext
= strrchr(vars
.filename
, '.')) != NULL
)
463 * Guess the MIME media type based on the extension...
466 if (!_cups_strcasecmp(ext
, ".gif"))
467 set_variable(outfile
, &vars
, "filetype", "image/gif");
468 else if (!_cups_strcasecmp(ext
, ".htm") ||
469 !_cups_strcasecmp(ext
, ".htm.gz") ||
470 !_cups_strcasecmp(ext
, ".html") ||
471 !_cups_strcasecmp(ext
, ".html.gz"))
472 set_variable(outfile
, &vars
, "filetype", "text/html");
473 else if (!_cups_strcasecmp(ext
, ".jpg") ||
474 !_cups_strcasecmp(ext
, ".jpeg"))
475 set_variable(outfile
, &vars
, "filetype", "image/jpeg");
476 else if (!_cups_strcasecmp(ext
, ".pcl") ||
477 !_cups_strcasecmp(ext
, ".pcl.gz"))
478 set_variable(outfile
, &vars
, "filetype", "application/vnd.hp-PCL");
479 else if (!_cups_strcasecmp(ext
, ".pdf"))
480 set_variable(outfile
, &vars
, "filetype", "application/pdf");
481 else if (!_cups_strcasecmp(ext
, ".png"))
482 set_variable(outfile
, &vars
, "filetype", "image/png");
483 else if (!_cups_strcasecmp(ext
, ".ps") ||
484 !_cups_strcasecmp(ext
, ".ps.gz"))
485 set_variable(outfile
, &vars
, "filetype", "application/postscript");
486 else if (!_cups_strcasecmp(ext
, ".pwg") ||
487 !_cups_strcasecmp(ext
, ".pwg.gz") ||
488 !_cups_strcasecmp(ext
, ".ras") ||
489 !_cups_strcasecmp(ext
, ".ras.gz"))
490 set_variable(outfile
, &vars
, "filetype", "image/pwg-raster");
491 else if (!_cups_strcasecmp(ext
, ".tif") ||
492 !_cups_strcasecmp(ext
, ".tiff"))
493 set_variable(outfile
, &vars
, "filetype", "image/tiff");
494 else if (!_cups_strcasecmp(ext
, ".txt") ||
495 !_cups_strcasecmp(ext
, ".txt.gz"))
496 set_variable(outfile
, &vars
, "filetype", "text/plain");
497 else if (!_cups_strcasecmp(ext
, ".urf") ||
498 !_cups_strcasecmp(ext
, ".urf.gz"))
499 set_variable(outfile
, &vars
, "filetype", "image/urf");
500 else if (!_cups_strcasecmp(ext
, ".xps"))
501 set_variable(outfile
, &vars
, "filetype", "application/openxps");
503 set_variable(outfile
, &vars
, "filetype", "application/octet-stream");
508 * Use the "auto-type" MIME media type...
511 set_variable(outfile
, &vars
, "filetype", "application/octet-stream");
515 case 'h' : /* Validate response headers */
519 case 'i' : /* Test every N seconds */
524 _cupsLangPuts(stderr
,
525 _("ipptool: Missing seconds for \"-i\"."));
530 interval
= (int)(_cupsStrScand(argv
[i
], NULL
, localeconv()) *
534 _cupsLangPuts(stderr
,
535 _("ipptool: Invalid seconds for \"-i\"."));
540 if (Output
== _CUPS_OUTPUT_PLIST
&& interval
)
542 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
547 case 'l' : /* List as a table */
548 Output
= _CUPS_OUTPUT_LIST
;
551 case 'n' : /* Repeat count */
556 _cupsLangPuts(stderr
,
557 _("ipptool: Missing count for \"-n\"."));
561 repeat
= atoi(argv
[i
]);
563 if (Output
== _CUPS_OUTPUT_PLIST
&& repeat
)
565 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
570 case 'q' : /* Be quiet */
571 Output
= _CUPS_OUTPUT_QUIET
;
574 case 't' : /* CUPS test output */
575 Output
= _CUPS_OUTPUT_TEST
;
578 case 'v' : /* Be verbose */
583 _cupsLangPrintf(stderr
, _("ipptool: Unknown option \"-%c\"."),
589 else if (!strncmp(argv
[i
], "ipp://", 6) || !strncmp(argv
[i
], "http://", 7)
591 || !strncmp(argv
[i
], "ipps://", 7)
592 || !strncmp(argv
[i
], "https://", 8)
593 #endif /* HAVE_SSL */
602 _cupsLangPuts(stderr
, _("ipptool: May only specify a single URI."));
607 if (!strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8))
608 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
609 #endif /* HAVE_SSL */
612 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, vars
.uri
,
613 vars
.scheme
, sizeof(vars
.scheme
),
614 vars
.userpass
, sizeof(vars
.userpass
),
615 vars
.hostname
, sizeof(vars
.hostname
),
617 vars
.resource
, sizeof(vars
.resource
));
619 if (uri_status
!= HTTP_URI_OK
)
621 _cupsLangPrintf(stderr
, _("ipptool: Bad URI - %s."), httpURIStatusString(uri_status
));
625 if (vars
.userpass
[0])
627 if ((Password
= strchr(vars
.userpass
, ':')) != NULL
)
630 Username
= vars
.userpass
;
631 cupsSetPasswordCB(password_cb
);
632 set_variable(outfile
, &vars
, "uriuser", vars
.userpass
);
635 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), vars
.scheme
, NULL
,
636 vars
.hostname
, vars
.port
, vars
.resource
);
647 _cupsLangPuts(stderr
, _("ipptool: URI required before test file."));
648 _cupsLangPuts(stderr
, argv
[i
]);
652 if (access(argv
[i
], 0) && argv
[i
][0] != '/'
654 && (!isalpha(argv
[i
][0] & 255) || argv
[i
][1] != ':')
658 snprintf(testname
, sizeof(testname
), "%s/ipptool/%s", cg
->cups_datadir
,
660 if (access(testname
, 0))
668 if (!do_tests(outfile
, &vars
, testfile
))
673 if (!vars
.uri
|| !testfile
)
677 * Loop if the interval is set...
680 if (Output
== _CUPS_OUTPUT_PLIST
)
681 print_xml_trailer(outfile
, !status
, NULL
);
682 else if (interval
> 0 && repeat
> 0)
686 usleep((useconds_t
)interval
);
687 do_tests(outfile
, &vars
, testfile
);
691 else if (interval
> 0)
695 usleep((useconds_t
)interval
);
696 do_tests(outfile
, &vars
, testfile
);
700 if ((Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
)) && TestCount
> 1)
703 * Show a summary report if there were multiple tests...
706 cupsFilePrintf(cupsFileStdout(), "\nSummary: %d tests, %d passed, %d failed, %d skipped\nScore: %d%%\n", TestCount
, PassCount
, FailCount
, SkipCount
, 100 * (PassCount
+ SkipCount
) / TestCount
);
709 cupsFileClose(outfile
);
720 * 'add_stringf()' - Add a formatted string to an array.
724 add_stringf(cups_array_t
*a
, /* I - Array */
725 const char *s
, /* I - Printf-style format string */
726 ...) /* I - Additional args as needed */
728 char buffer
[10240]; /* Format buffer */
729 va_list ap
; /* Argument pointer */
733 * Don't bother is the array is NULL...
740 * Format the message...
744 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
748 * Add it to the array...
751 cupsArrayAdd(a
, buffer
);
756 * 'compare_uris()' - Compare two URIs...
759 static int /* O - Result of comparison */
760 compare_uris(const char *a
, /* I - First URI */
761 const char *b
) /* I - Second URI */
763 char ascheme
[32], /* Components of first URI */
768 char bscheme
[32], /* Components of second URI */
773 char *ptr
; /* Pointer into string */
774 int result
; /* Result of comparison */
778 * Separate the URIs into their components...
781 if (httpSeparateURI(HTTP_URI_CODING_ALL
, a
, ascheme
, sizeof(ascheme
), auserpass
, sizeof(auserpass
), ahost
, sizeof(ahost
), &aport
, aresource
, sizeof(aresource
)) < HTTP_URI_STATUS_OK
)
784 if (httpSeparateURI(HTTP_URI_CODING_ALL
, b
, bscheme
, sizeof(bscheme
), buserpass
, sizeof(buserpass
), bhost
, sizeof(bhost
), &bport
, bresource
, sizeof(bresource
)) < HTTP_URI_STATUS_OK
)
788 * Strip trailing dots from the host components, if present...
791 if ((ptr
= ahost
+ strlen(ahost
) - 1) > ahost
&& *ptr
== '.')
794 if ((ptr
= bhost
+ strlen(bhost
) - 1) > bhost
&& *ptr
== '.')
798 * Compare each component...
801 if ((result
= _cups_strcasecmp(ascheme
, bscheme
)) != 0)
804 if ((result
= strcmp(auserpass
, buserpass
)) != 0)
807 if ((result
= _cups_strcasecmp(ahost
, bhost
)) != 0)
811 return (aport
- bport
);
813 if (!_cups_strcasecmp(ascheme
, "mailto") || !_cups_strcasecmp(ascheme
, "urn"))
814 return (_cups_strcasecmp(aresource
, bresource
));
816 return (strcmp(aresource
, bresource
));
821 * 'compare_vars()' - Compare two variables.
824 static int /* O - Result of comparison */
825 compare_vars(_cups_var_t
*a
, /* I - First variable */
826 _cups_var_t
*b
) /* I - Second variable */
828 return (_cups_strcasecmp(a
->name
, b
->name
));
833 * 'do_tests()' - Do tests as specified in the test file.
836 static int /* 1 = success, 0 = failure */
837 do_tests(cups_file_t
*outfile
, /* I - Output file */
838 _cups_vars_t
*vars
, /* I - Variables */
839 const char *testfile
) /* I - Test file to use */
841 int i
, /* Looping var */
842 linenum
, /* Current line number */
843 pass
, /* Did we pass the test? */
844 prev_pass
= 1, /* Did we pass the previous test? */
845 request_id
, /* Current request ID */
846 show_header
= 1, /* Show the test header? */
847 ignore_errors
, /* Ignore test failures? */
848 skip_previous
= 0, /* Skip on previous test failure? */
849 repeat_count
, /* Repeat count */
850 repeat_test
; /* Repeat a test? */
851 useconds_t delay
, /* Initial delay */
852 repeat_interval
; /* Repeat interval (delay) */
853 http_t
*http
= NULL
; /* HTTP connection to server */
854 cups_file_t
*fp
= NULL
; /* Test file */
855 char resource
[512], /* Resource for request */
856 token
[1024], /* Token from file */
857 *tokenptr
, /* Pointer into token */
858 temp
[1024], /* Temporary string */
859 buffer
[131072], /* Copy buffer */
860 compression
[16]; /* COMPRESSION value */
861 ipp_t
*request
= NULL
, /* IPP request */
862 *response
= NULL
; /* IPP response */
863 size_t length
; /* Length of IPP request */
864 http_status_t status
; /* HTTP status */
865 cups_file_t
*reqfile
; /* File to send */
866 ssize_t bytes
; /* Bytes read/written */
867 char attr
[128]; /* Attribute name */
868 ipp_op_t op
; /* Operation */
869 ipp_tag_t group
; /* Current group */
870 ipp_tag_t value
; /* Current value type */
871 ipp_attribute_t
*attrptr
, /* Attribute pointer */
872 *found
, /* Found attribute */
873 *lastcol
= NULL
; /* Last collection attribute */
874 char name
[1024], /* Name of test */
875 file_id
[1024], /* File identifier */
876 test_id
[1024]; /* Test identifier */
877 char filename
[1024]; /* Filename */
878 _cups_transfer_t transfer
; /* To chunk or not to chunk */
879 int version
, /* IPP version number to use */
880 skip_test
; /* Skip this test? */
881 int num_statuses
= 0; /* Number of valid status codes */
882 _cups_status_t statuses
[100], /* Valid status codes */
883 *last_status
; /* Last STATUS (for predicates) */
884 int status_ok
, /* Did we get a matching status? */
885 num_expects
= 0; /* Number of expected attributes */
886 _cups_expect_t expects
[200], /* Expected attributes */
887 *expect
, /* Current expected attribute */
888 *last_expect
; /* Last EXPECT (for predicates) */
889 int num_displayed
= 0; /* Number of displayed attributes */
890 char *displayed
[200]; /* Displayed attributes */
891 size_t widths
[200]; /* Width of columns */
892 cups_array_t
*a
, /* Duplicate attribute array */
893 *errors
= NULL
; /* Errors array */
894 const char *error
; /* Current error */
898 * Open the test file...
901 if ((fp
= cupsFileOpen(testfile
, "r")) == NULL
)
903 print_fatal_error(outfile
, "Unable to open test file %s - %s", testfile
,
910 * Connect to the server...
913 if ((http
= httpConnect2(vars
->hostname
, vars
->port
, NULL
, vars
->family
,
914 vars
->encryption
, 1, 30000, NULL
)) == NULL
)
916 print_fatal_error(outfile
, "Unable to connect to %s on port %d - %s", vars
->hostname
,
917 vars
->port
, cupsLastErrorString());
923 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
,
924 "deflate, gzip, identity");
926 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
, "identity");
927 #endif /* HAVE_LIBZ */
929 if (vars
->timeout
> 0.0)
930 httpSetTimeout(http
, vars
->timeout
, timeout_cb
, NULL
);
936 CUPS_SRAND((unsigned)time(NULL
));
938 errors
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
,
939 (cups_afree_func_t
)free
);
943 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
945 while (!Cancel
&& get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
948 * Expect an open brace...
951 if (!strcmp(token
, "DEFINE"))
957 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
958 get_token(fp
, temp
, sizeof(temp
), &linenum
))
960 expand_variables(vars
, token
, temp
, sizeof(token
));
961 set_variable(outfile
, vars
, attr
, token
);
965 print_fatal_error(outfile
, "Missing DEFINE name and/or value on line %d.",
973 else if (!strcmp(token
, "DEFINE-DEFAULT"))
976 * DEFINE-DEFAULT name value
979 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
980 get_token(fp
, temp
, sizeof(temp
), &linenum
))
982 expand_variables(vars
, token
, temp
, sizeof(token
));
983 if (!get_variable(vars
, attr
))
984 set_variable(outfile
, vars
, attr
, token
);
988 print_fatal_error(outfile
, "Missing DEFINE-DEFAULT name and/or value on line "
996 else if (!strcmp(token
, "FILE-ID"))
1002 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1004 expand_variables(vars
, file_id
, temp
, sizeof(file_id
));
1008 print_fatal_error(outfile
, "Missing FILE-ID value on line %d.", linenum
);
1015 else if (!strcmp(token
, "IGNORE-ERRORS"))
1022 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1023 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1025 IgnoreErrors
= !_cups_strcasecmp(temp
, "yes");
1029 print_fatal_error(outfile
, "Missing IGNORE-ERRORS value on line %d.", linenum
);
1036 else if (!strcmp(token
, "INCLUDE"))
1039 * INCLUDE "filename"
1040 * INCLUDE <filename>
1043 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1046 * Map the filename to and then run the tests...
1049 if (!do_tests(outfile
, vars
, get_filename(testfile
, filename
, temp
, sizeof(filename
))))
1053 if (StopAfterIncludeError
)
1059 print_fatal_error(outfile
, "Missing INCLUDE filename on line %d.", linenum
);
1067 else if (!strcmp(token
, "INCLUDE-IF-DEFINED"))
1070 * INCLUDE-IF-DEFINED name "filename"
1071 * INCLUDE-IF-DEFINED name <filename>
1074 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1075 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1078 * Map the filename to and then run the tests...
1081 if (get_variable(vars
, attr
) &&
1082 !do_tests(outfile
, vars
, get_filename(testfile
, filename
, temp
, sizeof(filename
))))
1086 if (StopAfterIncludeError
)
1092 print_fatal_error(outfile
, "Missing INCLUDE-IF-DEFINED name or filename on line "
1101 else if (!strcmp(token
, "INCLUDE-IF-NOT-DEFINED"))
1104 * INCLUDE-IF-NOT-DEFINED name "filename"
1105 * INCLUDE-IF-NOT-DEFINED name <filename>
1108 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1109 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1112 * Map the filename to and then run the tests...
1115 if (!get_variable(vars
, attr
) &&
1116 !do_tests(outfile
, vars
, get_filename(testfile
, filename
, temp
, sizeof(filename
))))
1120 if (StopAfterIncludeError
)
1126 print_fatal_error(outfile
, "Missing INCLUDE-IF-NOT-DEFINED name or filename on "
1127 "line %d.", linenum
);
1135 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1138 * SKIP-IF-DEFINED variable
1141 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1143 if (get_variable(vars
, temp
))
1148 print_fatal_error(outfile
, "Missing SKIP-IF-DEFINED variable on line %d.",
1154 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1157 * SKIP-IF-NOT-DEFINED variable
1160 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1162 if (!get_variable(vars
, temp
))
1167 print_fatal_error(outfile
, "Missing SKIP-IF-NOT-DEFINED variable on line %d.",
1173 else if (!strcmp(token
, "STOP-AFTER-INCLUDE-ERROR"))
1176 * STOP-AFTER-INCLUDE-ERROR yes
1177 * STOP-AFTER-INCLUDE-ERROR no
1180 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1181 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1183 StopAfterIncludeError
= !_cups_strcasecmp(temp
, "yes");
1187 print_fatal_error(outfile
, "Missing STOP-AFTER-INCLUDE-ERROR value on line %d.",
1195 else if (!strcmp(token
, "TRANSFER"))
1203 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1205 if (!strcmp(temp
, "auto"))
1206 Transfer
= _CUPS_TRANSFER_AUTO
;
1207 else if (!strcmp(temp
, "chunked"))
1208 Transfer
= _CUPS_TRANSFER_CHUNKED
;
1209 else if (!strcmp(temp
, "length"))
1210 Transfer
= _CUPS_TRANSFER_LENGTH
;
1213 print_fatal_error(outfile
, "Bad TRANSFER value \"%s\" on line %d.", temp
,
1221 print_fatal_error(outfile
, "Missing TRANSFER value on line %d.", linenum
);
1228 else if (!strcmp(token
, "VERSION"))
1230 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1232 if (!strcmp(temp
, "1.0"))
1234 else if (!strcmp(temp
, "1.1"))
1236 else if (!strcmp(temp
, "2.0"))
1238 else if (!strcmp(temp
, "2.1"))
1240 else if (!strcmp(temp
, "2.2"))
1244 print_fatal_error(outfile
, "Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1251 print_fatal_error(outfile
, "Missing VERSION number on line %d.", linenum
);
1258 else if (strcmp(token
, "{"))
1260 print_fatal_error(outfile
, "Unexpected token %s seen on line %d.", token
, linenum
);
1266 * Initialize things...
1271 if (Output
== _CUPS_OUTPUT_PLIST
)
1272 print_xml_header(outfile
);
1273 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= cupsFileStdout()))
1274 cupsFilePrintf(cupsFileStdout(), "\"%s\":\n", testfile
);
1279 strlcpy(resource
, vars
->resource
, sizeof(resource
));
1284 group
= IPP_TAG_ZERO
;
1285 ignore_errors
= IgnoreErrors
;
1293 transfer
= Transfer
;
1294 compression
[0] = '\0';
1297 repeat_interval
= 5000000;
1299 strlcpy(name
, testfile
, sizeof(name
));
1300 if (strrchr(name
, '.') != NULL
)
1301 *strrchr(name
, '.') = '\0';
1304 * Parse until we see a close brace...
1307 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
1309 if (_cups_strcasecmp(token
, "COUNT") &&
1310 _cups_strcasecmp(token
, "DEFINE-MATCH") &&
1311 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1312 _cups_strcasecmp(token
, "DEFINE-VALUE") &&
1313 _cups_strcasecmp(token
, "IF-DEFINED") &&
1314 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1315 _cups_strcasecmp(token
, "IN-GROUP") &&
1316 _cups_strcasecmp(token
, "OF-TYPE") &&
1317 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1318 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1319 _cups_strcasecmp(token
, "REPEAT-NO-MATCH") &&
1320 _cups_strcasecmp(token
, "SAME-COUNT-AS") &&
1321 _cups_strcasecmp(token
, "WITH-ALL-VALUES") &&
1322 _cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") &&
1323 _cups_strcasecmp(token
, "WITH-ALL-RESOURCES") &&
1324 _cups_strcasecmp(token
, "WITH-ALL-SCHEMES") &&
1325 _cups_strcasecmp(token
, "WITH-HOSTNAME") &&
1326 _cups_strcasecmp(token
, "WITH-RESOURCE") &&
1327 _cups_strcasecmp(token
, "WITH-SCHEME") &&
1328 _cups_strcasecmp(token
, "WITH-VALUE") &&
1329 _cups_strcasecmp(token
, "WITH-VALUE-FROM"))
1332 if (_cups_strcasecmp(token
, "DEFINE-MATCH") &&
1333 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1334 _cups_strcasecmp(token
, "IF-DEFINED") &&
1335 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1336 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1337 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1338 _cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
1341 if (!strcmp(token
, "}"))
1343 else if (!strcmp(token
, "{") && lastcol
)
1346 * Another collection value
1349 ipp_t
*col
= get_collection(outfile
, vars
, fp
, &linenum
);
1350 /* Collection value */
1354 ippSetCollection(request
, &lastcol
, ippGetCount(lastcol
), col
);
1362 else if (!strcmp(token
, "COMPRESSION"))
1366 * COMPRESSION deflate
1370 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1372 expand_variables(vars
, compression
, temp
, sizeof(compression
));
1374 if (strcmp(compression
, "none") && strcmp(compression
, "deflate") &&
1375 strcmp(compression
, "gzip"))
1377 if (strcmp(compression
, "none"))
1378 #endif /* HAVE_LIBZ */
1380 print_fatal_error(outfile
, "Unsupported COMPRESSION value '%s' on line %d.",
1381 compression
, linenum
);
1386 if (!strcmp(compression
, "none"))
1387 compression
[0] = '\0';
1391 print_fatal_error(outfile
, "Missing COMPRESSION value on line %d.", linenum
);
1396 else if (!strcmp(token
, "DEFINE"))
1402 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1403 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1405 expand_variables(vars
, token
, temp
, sizeof(token
));
1406 set_variable(outfile
, vars
, attr
, token
);
1410 print_fatal_error(outfile
, "Missing DEFINE name and/or value on line %d.",
1416 else if (!strcmp(token
, "IGNORE-ERRORS"))
1423 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1424 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1426 ignore_errors
= !_cups_strcasecmp(temp
, "yes");
1430 print_fatal_error(outfile
, "Missing IGNORE-ERRORS value on line %d.", linenum
);
1437 else if (!_cups_strcasecmp(token
, "NAME"))
1443 get_token(fp
, temp
, sizeof(temp
), &linenum
);
1444 expand_variables(vars
, name
, temp
, sizeof(name
));
1446 else if (!_cups_strcasecmp(token
, "PAUSE"))
1449 * Pause with a message...
1452 get_token(fp
, token
, sizeof(token
), &linenum
);
1453 pause_message(token
);
1455 else if (!strcmp(token
, "REQUEST-ID"))
1462 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1464 if (isdigit(temp
[0] & 255))
1465 request_id
= atoi(temp
);
1466 else if (!_cups_strcasecmp(temp
, "random"))
1467 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
1470 print_fatal_error(outfile
, "Bad REQUEST-ID value \"%s\" on line %d.", temp
,
1478 print_fatal_error(outfile
, "Missing REQUEST-ID value on line %d.", linenum
);
1483 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1486 * SKIP-IF-DEFINED variable
1489 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1491 if (get_variable(vars
, temp
))
1496 print_fatal_error(outfile
, "Missing SKIP-IF-DEFINED value on line %d.",
1502 else if (!strcmp(token
, "SKIP-IF-MISSING"))
1505 * SKIP-IF-MISSING filename
1508 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1510 expand_variables(vars
, token
, temp
, sizeof(token
));
1511 get_filename(testfile
, filename
, token
, sizeof(filename
));
1513 if (access(filename
, R_OK
))
1518 print_fatal_error(outfile
, "Missing SKIP-IF-MISSING filename on line %d.",
1524 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1527 * SKIP-IF-NOT-DEFINED variable
1530 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1532 if (!get_variable(vars
, temp
))
1537 print_fatal_error(outfile
, "Missing SKIP-IF-NOT-DEFINED value on line %d.",
1543 else if (!strcmp(token
, "SKIP-PREVIOUS-ERROR"))
1546 * SKIP-PREVIOUS-ERROR yes
1547 * SKIP-PREVIOUS-ERROR no
1550 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1551 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1553 skip_previous
= !_cups_strcasecmp(temp
, "yes");
1557 print_fatal_error(outfile
, "Missing SKIP-PREVIOUS-ERROR value on line %d.", linenum
);
1564 else if (!strcmp(token
, "TEST-ID"))
1570 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1572 expand_variables(vars
, test_id
, temp
, sizeof(test_id
));
1576 print_fatal_error(outfile
, "Missing TEST-ID value on line %d.", linenum
);
1583 else if (!strcmp(token
, "TRANSFER"))
1591 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1593 if (!strcmp(temp
, "auto"))
1594 transfer
= _CUPS_TRANSFER_AUTO
;
1595 else if (!strcmp(temp
, "chunked"))
1596 transfer
= _CUPS_TRANSFER_CHUNKED
;
1597 else if (!strcmp(temp
, "length"))
1598 transfer
= _CUPS_TRANSFER_LENGTH
;
1601 print_fatal_error(outfile
, "Bad TRANSFER value \"%s\" on line %d.", temp
,
1609 print_fatal_error(outfile
, "Missing TRANSFER value on line %d.", linenum
);
1614 else if (!_cups_strcasecmp(token
, "VERSION"))
1616 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1618 if (!strcmp(temp
, "0.0"))
1620 else if (!strcmp(temp
, "1.0"))
1622 else if (!strcmp(temp
, "1.1"))
1624 else if (!strcmp(temp
, "2.0"))
1626 else if (!strcmp(temp
, "2.1"))
1628 else if (!strcmp(temp
, "2.2"))
1632 print_fatal_error(outfile
, "Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1639 print_fatal_error(outfile
, "Missing VERSION number on line %d.", linenum
);
1644 else if (!_cups_strcasecmp(token
, "RESOURCE"))
1650 if (!get_token(fp
, resource
, sizeof(resource
), &linenum
))
1652 print_fatal_error(outfile
, "Missing RESOURCE path on line %d.", linenum
);
1657 else if (!_cups_strcasecmp(token
, "OPERATION"))
1663 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1665 print_fatal_error(outfile
, "Missing OPERATION code on line %d.", linenum
);
1670 expand_variables(vars
, token
, temp
, sizeof(token
));
1672 if ((op
= ippOpValue(token
)) == (ipp_op_t
)-1 &&
1673 (op
= (ipp_op_t
)strtol(token
, NULL
, 0)) == 0)
1675 print_fatal_error(outfile
, "Bad OPERATION code \"%s\" on line %d.", token
,
1681 else if (!_cups_strcasecmp(token
, "GROUP"))
1684 * Attribute group...
1687 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1689 print_fatal_error(outfile
, "Missing GROUP tag on line %d.", linenum
);
1694 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
|| value
>= IPP_TAG_UNSUPPORTED_VALUE
)
1696 print_fatal_error(outfile
, "Bad GROUP tag \"%s\" on line %d.", token
, linenum
);
1702 ippAddSeparator(request
);
1706 else if (!_cups_strcasecmp(token
, "DELAY"))
1709 * Delay before operation...
1712 double dval
; /* Delay value */
1714 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1716 print_fatal_error(outfile
, "Missing DELAY value on line %d.", linenum
);
1721 expand_variables(vars
, token
, temp
, sizeof(token
));
1723 if ((dval
= _cupsStrScand(token
, &tokenptr
, localeconv())) < 0.0 || (*tokenptr
&& *tokenptr
!= ','))
1725 print_fatal_error(outfile
, "Bad DELAY value \"%s\" on line %d.", token
,
1731 delay
= (useconds_t
)(1000000.0 * dval
);
1733 if (*tokenptr
== ',')
1735 if ((dval
= _cupsStrScand(tokenptr
+ 1, &tokenptr
, localeconv())) <= 0.0 || *tokenptr
)
1737 print_fatal_error(outfile
, "Bad DELAY value \"%s\" on line %d.", token
,
1743 repeat_interval
= (useconds_t
)(1000000.0 * dval
);
1746 repeat_interval
= delay
;
1748 else if (!_cups_strcasecmp(token
, "ATTR"))
1754 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1756 print_fatal_error(outfile
, "Missing ATTR value tag on line %d.", linenum
);
1761 if ((value
= ippTagValue(token
)) < IPP_TAG_UNSUPPORTED_VALUE
)
1763 print_fatal_error(outfile
, "Bad ATTR value tag \"%s\" on line %d.", token
,
1769 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
1771 print_fatal_error(outfile
, "Missing ATTR name on line %d.", linenum
);
1776 if (value
< IPP_TAG_INTEGER
)
1779 * Add out-of-band value - no value string needed...
1784 else if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1786 print_fatal_error(outfile
, "Missing ATTR value on line %d.", linenum
);
1792 expand_variables(vars
, token
, temp
, sizeof(token
));
1800 if (value
< IPP_TAG_INTEGER
)
1803 * Add out-of-band value...
1806 attrptr
= ippAddOutOfBand(request
, group
, value
, attr
);
1810 print_fatal_error(outfile
, "Unsupported ATTR value tag %s for \"%s\" on line %d.", ippTagString(value
), attr
, linenum
);
1816 case IPP_TAG_BOOLEAN
:
1817 if (!_cups_strcasecmp(token
, "true"))
1818 attrptr
= ippAddBoolean(request
, group
, attr
, 1);
1820 attrptr
= ippAddBoolean(request
, group
, attr
, (char)atoi(token
));
1823 case IPP_TAG_INTEGER
:
1825 if (!strchr(token
, ','))
1827 if (isdigit(token
[0] & 255) || token
[0] == '-')
1828 attrptr
= ippAddInteger(request
, group
, value
, attr
, (int)strtol(token
, &tokenptr
, 0));
1832 if ((i
= ippEnumValue(attr
, tokenptr
)) >= 0)
1834 attrptr
= ippAddInteger(request
, group
, value
, attr
, i
);
1835 tokenptr
+= strlen(tokenptr
);
1841 int values
[100], /* Values */
1842 num_values
= 1; /* Number of values */
1844 if (!isdigit(token
[0] & 255) && token
[0] != '-' && value
== IPP_TAG_ENUM
)
1846 char *ptr
; /* Pointer to next terminator */
1848 if ((ptr
= strchr(token
, ',')) != NULL
)
1851 ptr
= token
+ strlen(token
);
1853 if ((i
= ippEnumValue(attr
, token
)) < 0)
1859 i
= (int)strtol(token
, &tokenptr
, 0);
1863 while (tokenptr
&& *tokenptr
&&
1864 num_values
< (int)(sizeof(values
) / sizeof(values
[0])))
1866 if (*tokenptr
== ',')
1869 if (!isdigit(*tokenptr
& 255) && *tokenptr
!= '-')
1871 char *ptr
; /* Pointer to next terminator */
1873 if (value
!= IPP_TAG_ENUM
)
1876 if ((ptr
= strchr(tokenptr
, ',')) != NULL
)
1879 ptr
= tokenptr
+ strlen(tokenptr
);
1881 if ((i
= ippEnumValue(attr
, tokenptr
)) < 0)
1887 i
= (int)strtol(tokenptr
, &tokenptr
, 0);
1889 values
[num_values
++] = i
;
1892 attrptr
= ippAddIntegers(request
, group
, value
, attr
, num_values
, values
);
1895 if ((!token
[0] || !tokenptr
|| *tokenptr
) && !skip_test
)
1897 print_fatal_error(outfile
, "Bad %s value \'%s\' for \"%s\" on line %d.",
1898 ippTagString(value
), token
, attr
, linenum
);
1904 case IPP_TAG_RESOLUTION
:
1906 int xres
, /* X resolution */
1907 yres
; /* Y resolution */
1908 char *ptr
; /* Pointer into value */
1910 xres
= yres
= (int)strtol(token
, (char **)&ptr
, 10);
1911 if (ptr
> token
&& xres
> 0)
1914 yres
= (int)strtol(ptr
+ 1, (char **)&ptr
, 10);
1917 if (ptr
<= token
|| xres
<= 0 || yres
<= 0 || !ptr
||
1918 (_cups_strcasecmp(ptr
, "dpi") &&
1919 _cups_strcasecmp(ptr
, "dpc") &&
1920 _cups_strcasecmp(ptr
, "dpcm") &&
1921 _cups_strcasecmp(ptr
, "other")))
1926 print_fatal_error(outfile
, "Bad resolution value \'%s\' for \"%s\" on line %d.", token
, attr
, linenum
);
1931 if (!_cups_strcasecmp(ptr
, "dpi"))
1932 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
1933 else if (!_cups_strcasecmp(ptr
, "dpc") ||
1934 !_cups_strcasecmp(ptr
, "dpcm"))
1935 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_CM
, xres
, yres
);
1937 attrptr
= ippAddResolution(request
, group
, attr
, (ipp_res_t
)0, xres
, yres
);
1941 case IPP_TAG_RANGE
:
1943 int lowers
[4], /* Lower value */
1944 uppers
[4], /* Upper values */
1945 num_vals
; /* Number of values */
1948 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
1949 lowers
+ 0, uppers
+ 0,
1950 lowers
+ 1, uppers
+ 1,
1951 lowers
+ 2, uppers
+ 2,
1952 lowers
+ 3, uppers
+ 3);
1954 if ((num_vals
& 1) || num_vals
== 0)
1959 print_fatal_error(outfile
, "Bad rangeOfInteger value \'%s\' for \"%s\" on line %d.", token
, attr
, linenum
);
1964 attrptr
= ippAddRanges(request
, group
, attr
, num_vals
/ 2, lowers
,
1969 case IPP_TAG_BEGIN_COLLECTION
:
1970 if (!strcmp(token
, "{"))
1972 ipp_t
*col
= get_collection(outfile
, vars
, fp
, &linenum
);
1973 /* Collection value */
1977 attrptr
= lastcol
= ippAddCollection(request
, group
, attr
, col
);
1990 print_fatal_error(outfile
, "Bad ATTR collection value for \"%s\" on line %d.", attr
, linenum
);
1997 ipp_t
*col
; /* Collection value */
1998 off_t savepos
= cupsFileTell(fp
); /* Save position of file */
1999 int savelinenum
= linenum
; /* Save line number */
2001 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2004 if (strcmp(token
, ","))
2006 cupsFileSeek(fp
, savepos
);
2007 linenum
= savelinenum
;
2011 if (!get_token(fp
, token
, sizeof(token
), &linenum
) || strcmp(token
, "{"))
2013 print_fatal_error(outfile
, "Unexpected \"%s\" on line %d.", token
, linenum
);
2019 if ((col
= get_collection(outfile
, vars
, fp
, &linenum
)) == NULL
)
2022 ippSetCollection(request
, &attrptr
, ippGetCount(attrptr
), col
);
2025 while (!strcmp(token
, "{"));
2028 case IPP_TAG_STRING
:
2029 attrptr
= ippAddOctetString(request
, group
, attr
, token
, (int)strlen(token
));
2032 case IPP_TAG_TEXTLANG
:
2033 case IPP_TAG_NAMELANG
:
2036 case IPP_TAG_KEYWORD
:
2038 case IPP_TAG_URISCHEME
:
2039 case IPP_TAG_CHARSET
:
2040 case IPP_TAG_LANGUAGE
:
2041 case IPP_TAG_MIMETYPE
:
2042 if (!strchr(token
, ','))
2043 attrptr
= ippAddString(request
, group
, value
, attr
, NULL
, token
);
2047 * Multiple string values...
2050 int num_values
; /* Number of values */
2051 char *values
[100], /* Values */
2052 *ptr
; /* Pointer to next value */
2058 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
2060 if (ptr
> token
&& ptr
[-1] == '\\')
2061 _cups_strcpy(ptr
- 1, ptr
);
2065 values
[num_values
] = ptr
;
2070 attrptr
= ippAddStrings(request
, group
, value
, attr
, num_values
,
2071 NULL
, (const char **)values
);
2076 if (!attrptr
&& !skip_test
)
2078 print_fatal_error(outfile
, "Unable to add attribute \"%s\" on line %d.", attr
, linenum
);
2083 else if (!_cups_strcasecmp(token
, "FILE"))
2089 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
2091 print_fatal_error(outfile
, "Missing FILE filename on line %d.", linenum
);
2096 expand_variables(vars
, token
, temp
, sizeof(token
));
2097 get_filename(testfile
, filename
, token
, sizeof(filename
));
2099 if (access(filename
, R_OK
))
2101 print_fatal_error(outfile
, "Filename \"%s\" on line %d cannot be read.",
2103 print_fatal_error(outfile
, "Filename mapped to \"%s\".", filename
);
2108 else if (!_cups_strcasecmp(token
, "STATUS"))
2114 if (num_statuses
>= (int)(sizeof(statuses
) / sizeof(statuses
[0])))
2116 print_fatal_error(outfile
, "Too many STATUS's on line %d.", linenum
);
2121 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2123 print_fatal_error(outfile
, "Missing STATUS code on line %d.", linenum
);
2128 if ((statuses
[num_statuses
].status
= ippErrorValue(token
))
2129 == (ipp_status_t
)-1 &&
2130 (statuses
[num_statuses
].status
= (ipp_status_t
)strtol(token
, NULL
, 0)) == 0)
2132 print_fatal_error(outfile
, "Bad STATUS code \"%s\" on line %d.", token
,
2138 last_status
= statuses
+ num_statuses
;
2141 last_status
->define_match
= NULL
;
2142 last_status
->define_no_match
= NULL
;
2143 last_status
->if_defined
= NULL
;
2144 last_status
->if_not_defined
= NULL
;
2145 last_status
->repeat_limit
= 1000;
2146 last_status
->repeat_match
= 0;
2147 last_status
->repeat_no_match
= 0;
2149 else if (!_cups_strcasecmp(token
, "EXPECT") || !_cups_strcasecmp(token
, "EXPECT-ALL"))
2152 * Expected attributes...
2155 int expect_all
= !_cups_strcasecmp(token
, "EXPECT-ALL");
2157 if (num_expects
>= (int)(sizeof(expects
) / sizeof(expects
[0])))
2159 print_fatal_error(outfile
, "Too many EXPECT's on line %d.", linenum
);
2164 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2166 print_fatal_error(outfile
, "Missing EXPECT name on line %d.", linenum
);
2171 last_expect
= expects
+ num_expects
;
2174 memset(last_expect
, 0, sizeof(_cups_expect_t
));
2175 last_expect
->repeat_limit
= 1000;
2176 last_expect
->expect_all
= expect_all
;
2178 if (token
[0] == '!')
2180 last_expect
->not_expect
= 1;
2181 last_expect
->name
= strdup(token
+ 1);
2183 else if (token
[0] == '?')
2185 last_expect
->optional
= 1;
2186 last_expect
->name
= strdup(token
+ 1);
2189 last_expect
->name
= strdup(token
);
2191 else if (!_cups_strcasecmp(token
, "COUNT"))
2193 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2195 print_fatal_error(outfile
, "Missing COUNT number on line %d.", linenum
);
2200 if ((i
= atoi(token
)) <= 0)
2202 print_fatal_error(outfile
, "Bad COUNT \"%s\" on line %d.", token
, linenum
);
2208 last_expect
->count
= i
;
2211 print_fatal_error(outfile
, "COUNT without a preceding EXPECT on line %d.",
2217 else if (!_cups_strcasecmp(token
, "DEFINE-MATCH"))
2219 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2221 print_fatal_error(outfile
, "Missing DEFINE-MATCH variable on line %d.",
2228 last_expect
->define_match
= strdup(token
);
2229 else if (last_status
)
2230 last_status
->define_match
= strdup(token
);
2233 print_fatal_error(outfile
, "DEFINE-MATCH without a preceding EXPECT or STATUS "
2234 "on line %d.", linenum
);
2239 else if (!_cups_strcasecmp(token
, "DEFINE-NO-MATCH"))
2241 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2243 print_fatal_error(outfile
, "Missing DEFINE-NO-MATCH variable on line %d.",
2250 last_expect
->define_no_match
= strdup(token
);
2251 else if (last_status
)
2252 last_status
->define_no_match
= strdup(token
);
2255 print_fatal_error(outfile
, "DEFINE-NO-MATCH without a preceding EXPECT or "
2256 "STATUS on line %d.", linenum
);
2261 else if (!_cups_strcasecmp(token
, "DEFINE-VALUE"))
2263 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2265 print_fatal_error(outfile
, "Missing DEFINE-VALUE variable on line %d.",
2272 last_expect
->define_value
= strdup(token
);
2275 print_fatal_error(outfile
, "DEFINE-VALUE without a preceding EXPECT on "
2276 "line %d.", linenum
);
2281 else if (!_cups_strcasecmp(token
, "OF-TYPE"))
2283 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2285 print_fatal_error(outfile
, "Missing OF-TYPE value tag(s) on line %d.",
2292 last_expect
->of_type
= strdup(token
);
2295 print_fatal_error(outfile
, "OF-TYPE without a preceding EXPECT on line %d.",
2301 else if (!_cups_strcasecmp(token
, "IN-GROUP"))
2303 ipp_tag_t in_group
; /* IN-GROUP value */
2306 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2308 print_fatal_error(outfile
, "Missing IN-GROUP group tag on line %d.", linenum
);
2313 if ((in_group
= ippTagValue(token
)) == IPP_TAG_ZERO
|| in_group
>= IPP_TAG_UNSUPPORTED_VALUE
)
2315 print_fatal_error(outfile
, "Bad IN-GROUP group tag \"%s\" on line %d.", token
, linenum
);
2319 else if (last_expect
)
2320 last_expect
->in_group
= in_group
;
2323 print_fatal_error(outfile
, "IN-GROUP without a preceding EXPECT on line %d.", linenum
);
2328 else if (!_cups_strcasecmp(token
, "REPEAT-LIMIT"))
2330 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2332 print_fatal_error(outfile
, "Missing REPEAT-LIMIT value on line %d.", linenum
);
2336 else if (atoi(token
) <= 0)
2338 print_fatal_error(outfile
, "Bad REPEAT-LIMIT value on line %d.", linenum
);
2344 last_status
->repeat_limit
= atoi(token
);
2345 else if (last_expect
)
2346 last_expect
->repeat_limit
= atoi(token
);
2349 print_fatal_error(outfile
, "REPEAT-LIMIT without a preceding EXPECT or STATUS on line %d.", linenum
);
2354 else if (!_cups_strcasecmp(token
, "REPEAT-MATCH"))
2357 last_status
->repeat_match
= 1;
2358 else if (last_expect
)
2359 last_expect
->repeat_match
= 1;
2362 print_fatal_error(outfile
, "REPEAT-MATCH without a preceding EXPECT or STATUS on line %d.", linenum
);
2367 else if (!_cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
2370 last_status
->repeat_no_match
= 1;
2371 else if (last_expect
)
2372 last_expect
->repeat_no_match
= 1;
2375 print_fatal_error(outfile
, "REPEAT-NO-MATCH without a preceding EXPECT or STATUS on ine %d.", linenum
);
2380 else if (!_cups_strcasecmp(token
, "SAME-COUNT-AS"))
2382 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2384 print_fatal_error(outfile
, "Missing SAME-COUNT-AS name on line %d.", linenum
);
2390 last_expect
->same_count_as
= strdup(token
);
2393 print_fatal_error(outfile
, "SAME-COUNT-AS without a preceding EXPECT on line "
2399 else if (!_cups_strcasecmp(token
, "IF-DEFINED"))
2401 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2403 print_fatal_error(outfile
, "Missing IF-DEFINED name on line %d.", linenum
);
2409 last_expect
->if_defined
= strdup(token
);
2410 else if (last_status
)
2411 last_status
->if_defined
= strdup(token
);
2414 print_fatal_error(outfile
, "IF-DEFINED without a preceding EXPECT or STATUS on line %d.", linenum
);
2419 else if (!_cups_strcasecmp(token
, "IF-NOT-DEFINED"))
2421 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2423 print_fatal_error(outfile
, "Missing IF-NOT-DEFINED name on line %d.", linenum
);
2429 last_expect
->if_not_defined
= strdup(token
);
2430 else if (last_status
)
2431 last_status
->if_not_defined
= strdup(token
);
2434 print_fatal_error(outfile
, "IF-NOT-DEFINED without a preceding EXPECT or STATUS on line %d.", linenum
);
2439 else if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") ||
2440 !_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
2441 !_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
2442 !_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
2443 !_cups_strcasecmp(token
, "WITH-HOSTNAME") ||
2444 !_cups_strcasecmp(token
, "WITH-RESOURCE") ||
2445 !_cups_strcasecmp(token
, "WITH-SCHEME") ||
2446 !_cups_strcasecmp(token
, "WITH-VALUE"))
2450 if (!_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
2451 !_cups_strcasecmp(token
, "WITH-HOSTNAME"))
2452 last_expect
->with_flags
= _CUPS_WITH_HOSTNAME
;
2453 else if (!_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
2454 !_cups_strcasecmp(token
, "WITH-RESOURCE"))
2455 last_expect
->with_flags
= _CUPS_WITH_RESOURCE
;
2456 else if (!_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
2457 !_cups_strcasecmp(token
, "WITH-SCHEME"))
2458 last_expect
->with_flags
= _CUPS_WITH_SCHEME
;
2460 if (!_cups_strncasecmp(token
, "WITH-ALL-", 9))
2461 last_expect
->with_flags
|= _CUPS_WITH_ALL
;
2464 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
2466 print_fatal_error(outfile
, "Missing %s value on line %d.", token
, linenum
);
2474 * Expand any variables in the value and then save it.
2477 expand_variables(vars
, token
, temp
, sizeof(token
));
2479 tokenptr
= token
+ strlen(token
) - 1;
2481 if (token
[0] == '/' && tokenptr
> token
&& *tokenptr
== '/')
2484 * WITH-VALUE is a POSIX extended regular expression.
2487 last_expect
->with_value
= calloc(1, (size_t)(tokenptr
- token
));
2488 last_expect
->with_flags
|= _CUPS_WITH_REGEX
;
2490 if (last_expect
->with_value
)
2491 memcpy(last_expect
->with_value
, token
+ 1, (size_t)(tokenptr
- token
- 1));
2496 * WITH-VALUE is a literal value...
2499 char *ptr
; /* Pointer into value */
2501 for (ptr
= token
; *ptr
; ptr
++)
2503 if (*ptr
== '\\' && ptr
[1])
2506 * Remove \ from \foo...
2509 _cups_strcpy(ptr
, ptr
+ 1);
2513 last_expect
->with_value
= strdup(token
);
2514 last_expect
->with_flags
|= _CUPS_WITH_LITERAL
;
2519 print_fatal_error(outfile
, "%s without a preceding EXPECT on line %d.", token
, linenum
);
2524 else if (!_cups_strcasecmp(token
, "WITH-VALUE-FROM"))
2526 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
2528 print_fatal_error(outfile
, "Missing %s value on line %d.", token
, linenum
);
2536 * Expand any variables in the value and then save it.
2539 expand_variables(vars
, token
, temp
, sizeof(token
));
2541 last_expect
->with_value_from
= strdup(token
);
2542 last_expect
->with_flags
= _CUPS_WITH_LITERAL
;
2546 print_fatal_error(outfile
, "%s without a preceding EXPECT on line %d.", token
, linenum
);
2551 else if (!_cups_strcasecmp(token
, "DISPLAY"))
2554 * Display attributes...
2557 if (num_displayed
>= (int)(sizeof(displayed
) / sizeof(displayed
[0])))
2559 print_fatal_error(outfile
, "Too many DISPLAY's on line %d", linenum
);
2564 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2566 print_fatal_error(outfile
, "Missing DISPLAY name on line %d.", linenum
);
2571 displayed
[num_displayed
] = strdup(token
);
2576 print_fatal_error(outfile
, "Unexpected token %s seen on line %d.", token
, linenum
);
2583 * Submit the IPP request...
2588 ippSetVersion(request
, version
/ 10, version
% 10);
2589 ippSetOperation(request
, op
);
2590 ippSetRequestId(request
, request_id
);
2592 if (Output
== _CUPS_OUTPUT_PLIST
)
2594 cupsFilePuts(outfile
, "<dict>\n");
2595 cupsFilePuts(outfile
, "<key>Name</key>\n");
2596 print_xml_string(outfile
, "string", name
);
2599 cupsFilePuts(outfile
, "<key>FileId</key>\n");
2600 print_xml_string(outfile
, "string", file_id
);
2604 cupsFilePuts(outfile
, "<key>TestId</key>\n");
2605 print_xml_string(outfile
, "string", test_id
);
2607 cupsFilePuts(outfile
, "<key>Version</key>\n");
2608 cupsFilePrintf(outfile
, "<string>%d.%d</string>\n", version
/ 10, version
% 10);
2609 cupsFilePuts(outfile
, "<key>Operation</key>\n");
2610 print_xml_string(outfile
, "string", ippOpString(op
));
2611 cupsFilePuts(outfile
, "<key>RequestId</key>\n");
2612 cupsFilePrintf(outfile
, "<integer>%d</integer>\n", request_id
);
2613 cupsFilePuts(outfile
, "<key>RequestAttributes</key>\n");
2614 cupsFilePuts(outfile
, "<array>\n");
2617 cupsFilePuts(outfile
, "<dict>\n");
2618 for (attrptr
= request
->attrs
,
2619 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2621 attrptr
= attrptr
->next
)
2622 print_attr(outfile
, Output
, attrptr
, &group
);
2623 cupsFilePuts(outfile
, "</dict>\n");
2625 cupsFilePuts(outfile
, "</array>\n");
2628 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= cupsFileStdout()))
2632 cupsFilePrintf(cupsFileStdout(), " %s:\n", ippOpString(op
));
2634 for (attrptr
= request
->attrs
; attrptr
; attrptr
= attrptr
->next
)
2635 print_attr(cupsFileStdout(), _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
2638 cupsFilePrintf(cupsFileStdout(), " %-68.68s [", name
);
2641 if ((skip_previous
&& !prev_pass
) || skip_test
)
2648 if (Output
== _CUPS_OUTPUT_PLIST
)
2650 cupsFilePuts(outfile
, "<key>Successful</key>\n");
2651 cupsFilePuts(outfile
, "<true />\n");
2652 cupsFilePuts(outfile
, "<key>Skipped</key>\n");
2653 cupsFilePuts(outfile
, "<true />\n");
2654 cupsFilePuts(outfile
, "<key>StatusCode</key>\n");
2655 print_xml_string(outfile
, "string", "skip");
2656 cupsFilePuts(outfile
, "<key>ResponseAttributes</key>\n");
2657 cupsFilePuts(outfile
, "<dict />\n");
2660 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= cupsFileStdout()))
2661 cupsFilePuts(cupsFileStdout(), "SKIP]\n");
2673 delay
= repeat_interval
;
2676 status
= HTTP_STATUS_OK
;
2678 if (transfer
== _CUPS_TRANSFER_CHUNKED
||
2679 (transfer
== _CUPS_TRANSFER_AUTO
&& filename
[0]))
2682 * Send request using chunking - a 0 length means "chunk".
2690 * Send request using content length...
2693 length
= ippLength(request
);
2695 if (filename
[0] && (reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2698 * Read the file to get the uncompressed file size...
2701 while ((bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
2702 length
+= (size_t)bytes
;
2704 cupsFileClose(reqfile
);
2709 * Send the request...
2716 if (status
!= HTTP_STATUS_ERROR
)
2718 while (!response
&& !Cancel
&& prev_pass
)
2720 status
= cupsSendRequest(http
, request
, resource
, length
);
2724 httpSetField(http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
2725 #endif /* HAVE_LIBZ */
2727 if (!Cancel
&& status
== HTTP_STATUS_CONTINUE
&&
2728 request
->state
== IPP_DATA
&& filename
[0])
2730 if ((reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2733 (bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
2734 if ((status
= cupsWriteRequestData(http
, buffer
, (size_t)bytes
)) != HTTP_STATUS_CONTINUE
)
2737 cupsFileClose(reqfile
);
2741 snprintf(buffer
, sizeof(buffer
), "%s: %s", filename
,
2743 _cupsSetError(IPP_INTERNAL_ERROR
, buffer
, 0);
2745 status
= HTTP_STATUS_ERROR
;
2750 * Get the server's response...
2753 if (!Cancel
&& status
!= HTTP_STATUS_ERROR
)
2755 response
= cupsGetResponse(http
, resource
);
2756 status
= httpGetStatus(http
);
2759 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& http
->error
!= EINVAL
&&
2761 http
->error
!= WSAETIMEDOUT
)
2763 http
->error
!= ETIMEDOUT
)
2766 if (httpReconnect2(http
, 30000, NULL
))
2769 else if (status
== HTTP_STATUS_ERROR
|| status
== HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED
)
2774 else if (status
!= HTTP_STATUS_OK
)
2778 if (status
== HTTP_STATUS_UNAUTHORIZED
)
2786 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& http
->error
!= EINVAL
&&
2788 http
->error
!= WSAETIMEDOUT
)
2790 http
->error
!= ETIMEDOUT
)
2793 if (httpReconnect2(http
, 30000, NULL
))
2796 else if (status
== HTTP_STATUS_ERROR
)
2799 httpReconnect2(http
, 30000, NULL
);
2803 else if (status
!= HTTP_STATUS_OK
)
2810 * Check results of request...
2813 cupsArrayClear(errors
);
2815 if (http
->version
!= HTTP_1_1
)
2816 add_stringf(errors
, "Bad HTTP version (%d.%d)", http
->version
/ 100,
2817 http
->version
% 100);
2819 if (ValidateHeaders
)
2821 const char *header
; /* HTTP header value */
2823 if ((header
= httpGetField(http
, HTTP_FIELD_CONTENT_TYPE
)) == NULL
|| _cups_strcasecmp(header
, "application/ipp"))
2824 add_stringf(errors
, "Bad HTTP Content-Type in response (%s)", header
&& *header
? header
: "<missing>");
2826 if ((header
= httpGetField(http
, HTTP_FIELD_DATE
)) != NULL
&& *header
&& httpGetDateTime(header
) == 0)
2827 add_stringf(errors
, "Bad HTTP Date in response (%s)", header
);
2833 * No response, log error...
2836 add_stringf(errors
, "IPP request failed with status %s (%s)",
2837 ippErrorString(cupsLastError()),
2838 cupsLastErrorString());
2843 * Collect common attribute values...
2846 if ((attrptr
= ippFindAttribute(response
, "job-id",
2847 IPP_TAG_INTEGER
)) != NULL
)
2849 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2850 set_variable(outfile
, vars
, "job-id", temp
);
2853 if ((attrptr
= ippFindAttribute(response
, "job-uri",
2854 IPP_TAG_URI
)) != NULL
)
2855 set_variable(outfile
, vars
, "job-uri", attrptr
->values
[0].string
.text
);
2857 if ((attrptr
= ippFindAttribute(response
, "notify-subscription-id",
2858 IPP_TAG_INTEGER
)) != NULL
)
2860 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2861 set_variable(outfile
, vars
, "notify-subscription-id", temp
);
2865 * Check response, validating groups and attributes and logging errors
2869 if (response
->state
!= IPP_DATA
)
2871 "Missing end-of-attributes-tag in response "
2872 "(RFC 2910 section 3.5.1)");
2875 (response
->request
.status
.version
[0] != (version
/ 10) ||
2876 response
->request
.status
.version
[1] != (version
% 10)))
2878 "Bad version %d.%d in response - expected %d.%d "
2879 "(RFC 2911 section 3.1.8).",
2880 response
->request
.status
.version
[0],
2881 response
->request
.status
.version
[1],
2882 version
/ 10, version
% 10);
2884 if (response
->request
.status
.request_id
!= request_id
)
2886 "Bad request ID %d in response - expected %d "
2887 "(RFC 2911 section 3.1.1)",
2888 response
->request
.status
.request_id
, request_id
);
2890 attrptr
= response
->attrs
;
2893 "Missing first attribute \"attributes-charset "
2894 "(charset)\" in group operation-attributes-tag "
2895 "(RFC 2911 section 3.1.4).");
2898 if (!attrptr
->name
||
2899 attrptr
->value_tag
!= IPP_TAG_CHARSET
||
2900 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2901 attrptr
->num_values
!= 1 ||
2902 strcmp(attrptr
->name
, "attributes-charset"))
2904 "Bad first attribute \"%s (%s%s)\" in group %s, "
2905 "expected \"attributes-charset (charset)\" in "
2906 "group operation-attributes-tag (RFC 2911 section "
2908 attrptr
->name
? attrptr
->name
: "(null)",
2909 attrptr
->num_values
> 1 ? "1setOf " : "",
2910 ippTagString(attrptr
->value_tag
),
2911 ippTagString(attrptr
->group_tag
));
2913 attrptr
= attrptr
->next
;
2916 "Missing second attribute \"attributes-natural-"
2917 "language (naturalLanguage)\" in group "
2918 "operation-attributes-tag (RFC 2911 section "
2920 else if (!attrptr
->name
||
2921 attrptr
->value_tag
!= IPP_TAG_LANGUAGE
||
2922 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2923 attrptr
->num_values
!= 1 ||
2924 strcmp(attrptr
->name
, "attributes-natural-language"))
2926 "Bad first attribute \"%s (%s%s)\" in group %s, "
2927 "expected \"attributes-natural-language "
2928 "(naturalLanguage)\" in group "
2929 "operation-attributes-tag (RFC 2911 section "
2931 attrptr
->name
? attrptr
->name
: "(null)",
2932 attrptr
->num_values
> 1 ? "1setOf " : "",
2933 ippTagString(attrptr
->value_tag
),
2934 ippTagString(attrptr
->group_tag
));
2937 if ((attrptr
= ippFindAttribute(response
, "status-message",
2938 IPP_TAG_ZERO
)) != NULL
)
2940 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2942 "status-message (text(255)) has wrong value tag "
2943 "%s (RFC 2911 section 3.1.6.2).",
2944 ippTagString(attrptr
->value_tag
));
2945 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2947 "status-message (text(255)) has wrong group tag "
2948 "%s (RFC 2911 section 3.1.6.2).",
2949 ippTagString(attrptr
->group_tag
));
2950 if (attrptr
->num_values
!= 1)
2952 "status-message (text(255)) has %d values "
2953 "(RFC 2911 section 3.1.6.2).",
2954 attrptr
->num_values
);
2955 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2956 strlen(attrptr
->values
[0].string
.text
) > 255)
2958 "status-message (text(255)) has bad length %d"
2959 " (RFC 2911 section 3.1.6.2).",
2960 (int)strlen(attrptr
->values
[0].string
.text
));
2963 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
2964 IPP_TAG_ZERO
)) != NULL
)
2966 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2968 "detailed-status-message (text(MAX)) has wrong "
2969 "value tag %s (RFC 2911 section 3.1.6.3).",
2970 ippTagString(attrptr
->value_tag
));
2971 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2973 "detailed-status-message (text(MAX)) has wrong "
2974 "group tag %s (RFC 2911 section 3.1.6.3).",
2975 ippTagString(attrptr
->group_tag
));
2976 if (attrptr
->num_values
!= 1)
2978 "detailed-status-message (text(MAX)) has %d values"
2979 " (RFC 2911 section 3.1.6.3).",
2980 attrptr
->num_values
);
2981 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2982 strlen(attrptr
->values
[0].string
.text
) > 1023)
2984 "detailed-status-message (text(MAX)) has bad "
2985 "length %d (RFC 2911 section 3.1.6.3).",
2986 (int)strlen(attrptr
->values
[0].string
.text
));
2989 a
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2991 for (attrptr
= response
->attrs
,
2992 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2994 attrptr
= attrptr
->next
)
2996 if (attrptr
->group_tag
!= group
)
2998 int out_of_order
= 0; /* Are attribute groups out-of-order? */
3001 switch (attrptr
->group_tag
)
3006 case IPP_TAG_OPERATION
:
3010 case IPP_TAG_UNSUPPORTED_GROUP
:
3011 if (group
!= IPP_TAG_OPERATION
)
3016 case IPP_TAG_PRINTER
:
3017 if (group
!= IPP_TAG_OPERATION
&&
3018 group
!= IPP_TAG_UNSUPPORTED_GROUP
)
3022 case IPP_TAG_SUBSCRIPTION
:
3023 if (group
> attrptr
->group_tag
&&
3024 group
!= IPP_TAG_DOCUMENT
)
3029 if (group
> attrptr
->group_tag
)
3035 add_stringf(errors
, "Attribute groups out of order (%s < %s)",
3036 ippTagString(attrptr
->group_tag
),
3037 ippTagString(group
));
3039 if (attrptr
->group_tag
!= IPP_TAG_ZERO
)
3040 group
= attrptr
->group_tag
;
3043 validate_attr(outfile
, errors
, attrptr
);
3047 if (cupsArrayFind(a
, attrptr
->name
) && Output
< _CUPS_OUTPUT_LIST
)
3048 add_stringf(errors
, "Duplicate \"%s\" attribute in %s group",
3049 attrptr
->name
, ippTagString(group
));
3051 cupsArrayAdd(a
, attrptr
->name
);
3058 * Now check the test-defined expected status-code and attribute
3062 for (i
= 0, status_ok
= 0; i
< num_statuses
; i
++)
3064 if (statuses
[i
].if_defined
&&
3065 !get_variable(vars
, statuses
[i
].if_defined
))
3068 if (statuses
[i
].if_not_defined
&&
3069 get_variable(vars
, statuses
[i
].if_not_defined
))
3072 if (ippGetStatusCode(response
) == statuses
[i
].status
)
3076 if (statuses
[i
].repeat_match
&& repeat_count
< statuses
[i
].repeat_limit
)
3079 if (statuses
[i
].define_match
)
3080 set_variable(outfile
, vars
, statuses
[i
].define_match
, "1");
3084 if (statuses
[i
].repeat_no_match
&& repeat_count
< statuses
[i
].repeat_limit
)
3087 if (statuses
[i
].define_no_match
)
3089 set_variable(outfile
, vars
, statuses
[i
].define_no_match
, "1");
3095 if (!status_ok
&& num_statuses
> 0)
3097 for (i
= 0; i
< num_statuses
; i
++)
3099 if (statuses
[i
].if_defined
&&
3100 !get_variable(vars
, statuses
[i
].if_defined
))
3103 if (statuses
[i
].if_not_defined
&&
3104 get_variable(vars
, statuses
[i
].if_not_defined
))
3107 if (!statuses
[i
].repeat_match
|| repeat_count
>= statuses
[i
].repeat_limit
)
3108 add_stringf(errors
, "EXPECTED: STATUS %s (got %s)",
3109 ippErrorString(statuses
[i
].status
),
3110 ippErrorString(cupsLastError()));
3113 if ((attrptr
= ippFindAttribute(response
, "status-message",
3114 IPP_TAG_TEXT
)) != NULL
)
3115 add_stringf(errors
, "status-message=\"%s\"",
3116 attrptr
->values
[0].string
.text
);
3119 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3121 if (expect
->if_defined
&& !get_variable(vars
, expect
->if_defined
))
3124 if (expect
->if_not_defined
&&
3125 get_variable(vars
, expect
->if_not_defined
))
3128 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
3132 if ((found
&& expect
->not_expect
) ||
3133 (!found
&& !(expect
->not_expect
|| expect
->optional
)) ||
3134 (found
&& !expect_matches(expect
, found
->value_tag
)) ||
3135 (found
&& expect
->in_group
&&
3136 found
->group_tag
!= expect
->in_group
))
3138 if (expect
->define_no_match
)
3139 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3140 else if (!expect
->define_match
&& !expect
->define_value
)
3142 if (found
&& expect
->not_expect
)
3143 add_stringf(errors
, "NOT EXPECTED: %s", expect
->name
);
3144 else if (!found
&& !(expect
->not_expect
|| expect
->optional
))
3145 add_stringf(errors
, "EXPECTED: %s", expect
->name
);
3148 if (!expect_matches(expect
, found
->value_tag
))
3149 add_stringf(errors
, "EXPECTED: %s OF-TYPE %s (got %s)",
3150 expect
->name
, expect
->of_type
,
3151 ippTagString(found
->value_tag
));
3153 if (expect
->in_group
&& found
->group_tag
!= expect
->in_group
)
3154 add_stringf(errors
, "EXPECTED: %s IN-GROUP %s (got %s).",
3155 expect
->name
, ippTagString(expect
->in_group
),
3156 ippTagString(found
->group_tag
));
3160 if (expect
->repeat_no_match
&& repeat_count
< expect
->repeat_limit
)
3167 ippAttributeString(found
, buffer
, sizeof(buffer
));
3169 if (found
&& expect
->with_value_from
&& !with_value_from(NULL
, ippFindAttribute(response
, expect
->with_value_from
, IPP_TAG_ZERO
), found
, buffer
, sizeof(buffer
)))
3171 if (expect
->define_no_match
)
3172 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3173 else if (!expect
->define_match
&& !expect
->define_value
&& ((!expect
->repeat_match
&& !expect
->repeat_no_match
) || repeat_count
>= expect
->repeat_limit
))
3175 add_stringf(errors
, "EXPECTED: %s WITH-VALUES-FROM %s", expect
->name
, expect
->with_value_from
);
3177 with_value_from(errors
, ippFindAttribute(response
, expect
->with_value_from
, IPP_TAG_ZERO
), found
, buffer
, sizeof(buffer
));
3180 if (expect
->repeat_no_match
&& repeat_count
< expect
->repeat_limit
)
3185 else if (found
&& !with_value(outfile
, NULL
, expect
->with_value
, expect
->with_flags
, found
, buffer
, sizeof(buffer
)))
3187 if (expect
->define_no_match
)
3188 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3189 else if (!expect
->define_match
&& !expect
->define_value
&&
3190 !expect
->repeat_match
&& (!expect
->repeat_no_match
|| repeat_count
>= expect
->repeat_limit
))
3192 if (expect
->with_flags
& _CUPS_WITH_REGEX
)
3193 add_stringf(errors
, "EXPECTED: %s %s /%s/", expect
->name
, with_flags_string(expect
->with_flags
), expect
->with_value
);
3195 add_stringf(errors
, "EXPECTED: %s %s \"%s\"", expect
->name
, with_flags_string(expect
->with_flags
), expect
->with_value
);
3197 with_value(outfile
, errors
, expect
->with_value
, expect
->with_flags
, found
, buffer
, sizeof(buffer
));
3200 if (expect
->repeat_no_match
&&
3201 repeat_count
< expect
->repeat_limit
)
3207 if (found
&& expect
->count
> 0 &&
3208 found
->num_values
!= expect
->count
)
3210 if (expect
->define_no_match
)
3211 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3212 else if (!expect
->define_match
&& !expect
->define_value
)
3214 add_stringf(errors
, "EXPECTED: %s COUNT %d (got %d)", expect
->name
,
3215 expect
->count
, found
->num_values
);
3218 if (expect
->repeat_no_match
&&
3219 repeat_count
< expect
->repeat_limit
)
3225 if (found
&& expect
->same_count_as
)
3227 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
3230 if (!attrptr
|| attrptr
->num_values
!= found
->num_values
)
3232 if (expect
->define_no_match
)
3233 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3234 else if (!expect
->define_match
&& !expect
->define_value
)
3238 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
3239 "(not returned)", expect
->name
,
3240 found
->num_values
, expect
->same_count_as
);
3241 else if (attrptr
->num_values
!= found
->num_values
)
3243 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
3244 "(%d values)", expect
->name
, found
->num_values
,
3245 expect
->same_count_as
, attrptr
->num_values
);
3248 if (expect
->repeat_no_match
&&
3249 repeat_count
< expect
->repeat_limit
)
3256 if (found
&& expect
->define_match
)
3257 set_variable(outfile
, vars
, expect
->define_match
, "1");
3259 if (found
&& expect
->define_value
)
3261 if (!expect
->with_value
)
3263 int last
= ippGetCount(found
) - 1;
3264 /* Last element in attribute */
3266 switch (ippGetValueTag(found
))
3269 case IPP_TAG_INTEGER
:
3270 snprintf(buffer
, sizeof(buffer
), "%d", ippGetInteger(found
, last
));
3273 case IPP_TAG_BOOLEAN
:
3274 if (ippGetBoolean(found
, last
))
3275 strlcpy(buffer
, "true", sizeof(buffer
));
3277 strlcpy(buffer
, "false", sizeof(buffer
));
3280 case IPP_TAG_RESOLUTION
:
3282 int xres
, /* Horizontal resolution */
3283 yres
; /* Vertical resolution */
3284 ipp_res_t units
; /* Resolution units */
3286 xres
= ippGetResolution(found
, last
, &yres
, &units
);
3289 snprintf(buffer
, sizeof(buffer
), "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
3291 snprintf(buffer
, sizeof(buffer
), "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
3295 case IPP_TAG_CHARSET
:
3296 case IPP_TAG_KEYWORD
:
3297 case IPP_TAG_LANGUAGE
:
3298 case IPP_TAG_MIMETYPE
:
3300 case IPP_TAG_NAMELANG
:
3302 case IPP_TAG_TEXTLANG
:
3304 case IPP_TAG_URISCHEME
:
3305 strlcpy(buffer
, ippGetString(found
, last
, NULL
), sizeof(buffer
));
3309 ippAttributeString(found
, buffer
, sizeof(buffer
));
3314 set_variable(outfile
, vars
, expect
->define_value
, buffer
);
3317 if (found
&& expect
->repeat_match
&&
3318 repeat_count
< expect
->repeat_limit
)
3321 while (expect
->expect_all
&& (found
= ippFindNextAttribute(response
, expect
->name
, IPP_TAG_ZERO
)) != NULL
);
3326 * If we are going to repeat this test, display intermediate results...
3331 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= cupsFileStdout()))
3333 cupsFilePrintf(cupsFileStdout(), "%04d]\n", repeat_count
);
3335 if (num_displayed
> 0)
3337 for (attrptr
= ippFirstAttribute(response
); attrptr
; attrptr
= ippNextAttribute(response
))
3339 const char *attrname
= ippGetName(attrptr
);
3342 for (i
= 0; i
< num_displayed
; i
++)
3344 if (!strcmp(displayed
[i
], attrname
))
3346 print_attr(cupsFileStdout(), _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
3355 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= cupsFileStdout()))
3357 cupsFilePrintf(cupsFileStdout(), " %-68.68s [", name
);
3360 ippDelete(response
);
3364 while (repeat_test
);
3370 if (cupsArrayCount(errors
) > 0)
3371 prev_pass
= pass
= 0;
3378 if (Output
== _CUPS_OUTPUT_PLIST
)
3380 cupsFilePuts(outfile
, "<key>Successful</key>\n");
3381 cupsFilePuts(outfile
, prev_pass
? "<true />\n" : "<false />\n");
3382 cupsFilePuts(outfile
, "<key>StatusCode</key>\n");
3383 print_xml_string(outfile
, "string", ippErrorString(cupsLastError()));
3384 cupsFilePuts(outfile
, "<key>ResponseAttributes</key>\n");
3385 cupsFilePuts(outfile
, "<array>\n");
3386 cupsFilePuts(outfile
, "<dict>\n");
3387 for (attrptr
= response
? response
->attrs
: NULL
,
3388 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
3390 attrptr
= attrptr
->next
)
3391 print_attr(outfile
, Output
, attrptr
, &group
);
3392 cupsFilePuts(outfile
, "</dict>\n");
3393 cupsFilePuts(outfile
, "</array>\n");
3396 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= cupsFileStdout()))
3398 cupsFilePuts(cupsFileStdout(), prev_pass
? "PASS]\n" : "FAIL]\n");
3400 if (!prev_pass
|| (Verbosity
&& response
))
3402 cupsFilePrintf(cupsFileStdout(), " RECEIVED: %lu bytes in response\n", (unsigned long)ippLength(response
));
3403 cupsFilePrintf(cupsFileStdout(), " status-code = %s (%s)\n", ippErrorString(cupsLastError()), cupsLastErrorString());
3405 if (Verbosity
&& response
)
3407 for (attrptr
= response
->attrs
;
3409 attrptr
= attrptr
->next
)
3410 print_attr(cupsFileStdout(), _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
3414 else if (!prev_pass
&& Output
!= _CUPS_OUTPUT_QUIET
)
3415 fprintf(stderr
, "%s\n", cupsLastErrorString());
3417 if (prev_pass
&& Output
>= _CUPS_OUTPUT_LIST
&& !Verbosity
&&
3420 size_t width
; /* Length of value */
3422 for (i
= 0; i
< num_displayed
; i
++)
3424 widths
[i
] = strlen(displayed
[i
]);
3426 for (attrptr
= ippFindAttribute(response
, displayed
[i
], IPP_TAG_ZERO
);
3428 attrptr
= ippFindNextAttribute(response
, displayed
[i
],
3431 width
= ippAttributeString(attrptr
, NULL
, 0);
3432 if (width
> widths
[i
])
3437 if (Output
== _CUPS_OUTPUT_CSV
)
3438 print_csv(outfile
, NULL
, num_displayed
, displayed
, widths
);
3440 print_line(outfile
, NULL
, num_displayed
, displayed
, widths
);
3442 attrptr
= response
->attrs
;
3446 while (attrptr
&& attrptr
->group_tag
<= IPP_TAG_OPERATION
)
3447 attrptr
= attrptr
->next
;
3451 if (Output
== _CUPS_OUTPUT_CSV
)
3452 print_csv(outfile
, attrptr
, num_displayed
, displayed
, widths
);
3454 print_line(outfile
, attrptr
, num_displayed
, displayed
, widths
);
3456 while (attrptr
&& attrptr
->group_tag
> IPP_TAG_OPERATION
)
3457 attrptr
= attrptr
->next
;
3461 else if (!prev_pass
)
3463 if (Output
== _CUPS_OUTPUT_PLIST
)
3465 cupsFilePuts(outfile
, "<key>Errors</key>\n");
3466 cupsFilePuts(outfile
, "<array>\n");
3468 for (error
= (char *)cupsArrayFirst(errors
);
3470 error
= (char *)cupsArrayNext(errors
))
3471 print_xml_string(outfile
, "string", error
);
3473 cupsFilePuts(outfile
, "</array>\n");
3476 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= cupsFileStdout()))
3478 for (error
= (char *)cupsArrayFirst(errors
);
3480 error
= (char *)cupsArrayNext(errors
))
3481 cupsFilePrintf(cupsFileStdout(), " %s\n", error
);
3485 if (num_displayed
> 0 && !Verbosity
&& response
&& (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= cupsFileStdout())))
3487 for (attrptr
= response
->attrs
;
3489 attrptr
= attrptr
->next
)
3493 for (i
= 0; i
< num_displayed
; i
++)
3495 if (!strcmp(displayed
[i
], attrptr
->name
))
3497 print_attr(outfile
, Output
, attrptr
, NULL
);
3507 if (Output
== _CUPS_OUTPUT_PLIST
)
3508 cupsFilePuts(outfile
, "</dict>\n");
3510 ippDelete(response
);
3513 for (i
= 0; i
< num_statuses
; i
++)
3515 if (statuses
[i
].if_defined
)
3516 free(statuses
[i
].if_defined
);
3517 if (statuses
[i
].if_not_defined
)
3518 free(statuses
[i
].if_not_defined
);
3519 if (statuses
[i
].define_match
)
3520 free(statuses
[i
].define_match
);
3521 if (statuses
[i
].define_no_match
)
3522 free(statuses
[i
].define_no_match
);
3526 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3529 if (expect
->of_type
)
3530 free(expect
->of_type
);
3531 if (expect
->same_count_as
)
3532 free(expect
->same_count_as
);
3533 if (expect
->if_defined
)
3534 free(expect
->if_defined
);
3535 if (expect
->if_not_defined
)
3536 free(expect
->if_not_defined
);
3537 if (expect
->with_value
)
3538 free(expect
->with_value
);
3539 if (expect
->define_match
)
3540 free(expect
->define_match
);
3541 if (expect
->define_no_match
)
3542 free(expect
->define_no_match
);
3543 if (expect
->define_value
)
3544 free(expect
->define_value
);
3548 for (i
= 0; i
< num_displayed
; i
++)
3552 if (!ignore_errors
&& !prev_pass
)
3558 cupsArrayDelete(errors
);
3565 ippDelete(response
);
3567 for (i
= 0; i
< num_statuses
; i
++)
3569 if (statuses
[i
].if_defined
)
3570 free(statuses
[i
].if_defined
);
3571 if (statuses
[i
].if_not_defined
)
3572 free(statuses
[i
].if_not_defined
);
3573 if (statuses
[i
].define_match
)
3574 free(statuses
[i
].define_match
);
3575 if (statuses
[i
].define_no_match
)
3576 free(statuses
[i
].define_no_match
);
3579 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3582 if (expect
->of_type
)
3583 free(expect
->of_type
);
3584 if (expect
->same_count_as
)
3585 free(expect
->same_count_as
);
3586 if (expect
->if_defined
)
3587 free(expect
->if_defined
);
3588 if (expect
->if_not_defined
)
3589 free(expect
->if_not_defined
);
3590 if (expect
->with_value
)
3591 free(expect
->with_value
);
3592 if (expect
->define_match
)
3593 free(expect
->define_match
);
3594 if (expect
->define_no_match
)
3595 free(expect
->define_no_match
);
3596 if (expect
->define_value
)
3597 free(expect
->define_value
);
3600 for (i
= 0; i
< num_displayed
; i
++)
3608 * 'expand_variables()' - Expand variables in a string.
3612 expand_variables(_cups_vars_t
*vars
, /* I - Variables */
3613 char *dst
, /* I - Destination string buffer */
3614 const char *src
, /* I - Source string */
3615 size_t dstsize
) /* I - Size of destination buffer */
3617 char *dstptr
, /* Pointer into destination */
3618 *dstend
, /* End of destination */
3619 temp
[256], /* Temporary string */
3620 *tempptr
; /* Pointer into temporary string */
3621 const char *value
; /* Value to substitute */
3625 dstend
= dst
+ dstsize
- 1;
3627 while (*src
&& dstptr
< dstend
)
3632 * Substitute a string/number...
3635 if (!strncmp(src
, "$$", 2))
3640 else if (!strncmp(src
, "$ENV[", 5))
3642 strlcpy(temp
, src
+ 5, sizeof(temp
));
3644 for (tempptr
= temp
; *tempptr
; tempptr
++)
3645 if (*tempptr
== ']')
3651 value
= getenv(temp
);
3652 src
+= tempptr
- temp
+ 5;
3659 strlcpy(temp
, src
, sizeof(temp
));
3660 if ((tempptr
= strchr(temp
, '}')) != NULL
)
3663 tempptr
= temp
+ strlen(temp
);
3667 strlcpy(temp
, src
+ 1, sizeof(temp
));
3669 for (tempptr
= temp
; *tempptr
; tempptr
++)
3670 if (!isalnum(*tempptr
& 255) && *tempptr
!= '-' && *tempptr
!= '_')
3677 if (!strcmp(temp
, "uri"))
3679 else if (!strcmp(temp
, "filename"))
3680 value
= vars
->filename
;
3681 else if (!strcmp(temp
, "scheme") || !strcmp(temp
, "method"))
3682 value
= vars
->scheme
;
3683 else if (!strcmp(temp
, "username"))
3684 value
= vars
->userpass
;
3685 else if (!strcmp(temp
, "hostname"))
3686 value
= vars
->hostname
;
3687 else if (!strcmp(temp
, "port"))
3689 snprintf(temp
, sizeof(temp
), "%d", vars
->port
);
3692 else if (!strcmp(temp
, "resource"))
3693 value
= vars
->resource
;
3694 else if (!strcmp(temp
, "user"))
3697 value
= get_variable(vars
, temp
);
3699 src
+= tempptr
- temp
+ 1;
3704 strlcpy(dstptr
, value
, (size_t)(dstend
- dstptr
+ 1));
3705 dstptr
+= strlen(dstptr
);
3717 * 'expect_matches()' - Return true if the tag matches the specification.
3720 static int /* O - 1 if matches, 0 otherwise */
3722 _cups_expect_t
*expect
, /* I - Expected attribute */
3723 ipp_tag_t value_tag
) /* I - Value tag for attribute */
3725 int match
; /* Match? */
3726 char *of_type
, /* Type name to match */
3727 *next
, /* Next name to match */
3728 sep
; /* Separator character */
3732 * If we don't expect a particular type, return immediately...
3735 if (!expect
->of_type
)
3739 * Parse the "of_type" value since the string can contain multiple attribute
3740 * types separated by "," or "|"...
3743 for (of_type
= expect
->of_type
, match
= 0; !match
&& *of_type
; of_type
= next
)
3746 * Find the next separator, and set it (temporarily) to nul if present.
3749 for (next
= of_type
; *next
&& *next
!= '|' && *next
!= ','; next
++);
3751 if ((sep
= *next
) != '\0')
3755 * Support some meta-types to make it easier to write the test file.
3758 if (!strcmp(of_type
, "text"))
3759 match
= value_tag
== IPP_TAG_TEXTLANG
|| value_tag
== IPP_TAG_TEXT
;
3760 else if (!strcmp(of_type
, "name"))
3761 match
= value_tag
== IPP_TAG_NAMELANG
|| value_tag
== IPP_TAG_NAME
;
3762 else if (!strcmp(of_type
, "collection"))
3763 match
= value_tag
== IPP_TAG_BEGIN_COLLECTION
;
3765 match
= value_tag
== ippTagValue(of_type
);
3768 * Restore the separator if we have one...
3780 * 'get_collection()' - Get a collection value from the current test file.
3783 static ipp_t
* /* O - Collection value */
3784 get_collection(cups_file_t
*outfile
, /* I - Output file */
3785 _cups_vars_t
*vars
, /* I - Variables */
3786 cups_file_t
*fp
, /* I - File to read from */
3787 int *linenum
) /* IO - Line number */
3789 char token
[1024], /* Token from file */
3790 temp
[1024], /* Temporary string */
3791 attr
[128]; /* Attribute name */
3792 ipp_tag_t value
; /* Current value type */
3793 ipp_t
*col
= ippNew(); /* Collection value */
3794 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
3797 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
3799 if (!strcmp(token
, "}"))
3801 else if (!strcmp(token
, "{") && lastcol
)
3804 * Another collection value
3807 ipp_t
*subcol
= get_collection(outfile
, vars
, fp
, linenum
);
3808 /* Collection value */
3811 ippSetCollection(col
, &lastcol
, ippGetCount(lastcol
), subcol
);
3815 else if (!_cups_strcasecmp(token
, "MEMBER"))
3823 if (!get_token(fp
, token
, sizeof(token
), linenum
))
3825 print_fatal_error(outfile
, "Missing MEMBER value tag on line %d.", *linenum
);
3829 if ((value
= ippTagValue(token
)) < IPP_TAG_UNSUPPORTED_VALUE
)
3831 print_fatal_error(outfile
, "Bad MEMBER value tag \"%s\" on line %d.", token
,
3836 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
3838 print_fatal_error(outfile
, "Missing MEMBER name on line %d.", *linenum
);
3842 if (value
< IPP_TAG_INTEGER
)
3845 * Out-of-band member attributes have no value...
3850 else if (!get_token(fp
, temp
, sizeof(temp
), linenum
))
3852 print_fatal_error(outfile
, "Missing MEMBER value on line %d.", *linenum
);
3857 expand_variables(vars
, token
, temp
, sizeof(token
));
3863 if (value
< IPP_TAG_INTEGER
)
3866 * Add out-of-band value...
3869 ippAddOutOfBand(col
, IPP_TAG_ZERO
, value
, attr
);
3873 print_fatal_error(outfile
, "Unsupported MEMBER value tag %s for \"%s\" on line %d.", ippTagString(value
), attr
, *linenum
);
3878 case IPP_TAG_BOOLEAN
:
3879 if (!_cups_strcasecmp(token
, "true"))
3880 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
3882 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, (char)atoi(token
));
3885 case IPP_TAG_INTEGER
:
3887 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
3890 case IPP_TAG_RESOLUTION
:
3892 int xres
, /* X resolution */
3893 yres
; /* Y resolution */
3894 char units
[6]; /* Units */
3896 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
3897 (_cups_strcasecmp(units
, "dpi") &&
3898 _cups_strcasecmp(units
, "dpc") &&
3899 _cups_strcasecmp(units
, "dpcm") &&
3900 _cups_strcasecmp(units
, "other")))
3902 print_fatal_error(outfile
, "Bad resolution value \"%s\" on line %d.",
3907 if (!_cups_strcasecmp(units
, "dpi"))
3908 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
3909 else if (!_cups_strcasecmp(units
, "dpc") ||
3910 !_cups_strcasecmp(units
, "dpcm"))
3911 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_CM
, xres
, yres
);
3913 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, (ipp_res_t
)0, xres
, yres
);
3917 case IPP_TAG_RANGE
:
3919 int lowers
[4], /* Lower value */
3920 uppers
[4], /* Upper values */
3921 num_vals
; /* Number of values */
3924 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
3925 lowers
+ 0, uppers
+ 0,
3926 lowers
+ 1, uppers
+ 1,
3927 lowers
+ 2, uppers
+ 2,
3928 lowers
+ 3, uppers
+ 3);
3930 if ((num_vals
& 1) || num_vals
== 0)
3932 print_fatal_error(outfile
, "Bad rangeOfInteger value \"%s\" on line %d.",
3937 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
3942 case IPP_TAG_BEGIN_COLLECTION
:
3943 if (!strcmp(token
, "{"))
3945 ipp_t
*subcol
= get_collection(outfile
, vars
, fp
, linenum
);
3946 /* Collection value */
3950 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
3958 print_fatal_error(outfile
, "Bad collection value on line %d.", *linenum
);
3963 case IPP_TAG_STRING
:
3964 ippAddOctetString(col
, IPP_TAG_ZERO
, attr
, token
, (int)strlen(token
));
3967 case IPP_TAG_TEXTLANG
:
3968 case IPP_TAG_NAMELANG
:
3971 case IPP_TAG_KEYWORD
:
3973 case IPP_TAG_URISCHEME
:
3974 case IPP_TAG_CHARSET
:
3975 case IPP_TAG_LANGUAGE
:
3976 case IPP_TAG_MIMETYPE
:
3977 if (!strchr(token
, ','))
3978 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
3982 * Multiple string values...
3985 int num_values
; /* Number of values */
3986 char *values
[100], /* Values */
3987 *ptr
; /* Pointer to next value */
3993 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
3995 if (ptr
> token
&& ptr
[-1] == '\\')
3996 _cups_strcpy(ptr
- 1, ptr
);
4000 values
[num_values
] = ptr
;
4005 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
4006 NULL
, (const char **)values
);
4013 print_fatal_error(outfile
, "Unexpected token %s seen on line %d.", token
, *linenum
);
4021 * If we get here there was a parse error; free memory and return.
4033 * 'get_filename()' - Get a filename based on the current test file.
4036 static char * /* O - Filename */
4037 get_filename(const char *testfile
, /* I - Current test file */
4038 char *dst
, /* I - Destination filename */
4039 const char *src
, /* I - Source filename */
4040 size_t dstsize
) /* I - Size of destination buffer */
4042 char *dstptr
; /* Pointer into destination */
4043 _cups_globals_t
*cg
= _cupsGlobals();
4047 if (*src
== '<' && src
[strlen(src
) - 1] == '>')
4050 * Map <filename> to CUPS_DATADIR/ipptool/filename...
4053 snprintf(dst
, dstsize
, "%s/ipptool/%s", cg
->cups_datadir
, src
+ 1);
4054 dstptr
= dst
+ strlen(dst
) - 1;
4058 else if (*src
== '/' || !strchr(testfile
, '/')
4060 || (isalpha(*src
& 255) && src
[1] == ':')
4065 * Use the path as-is...
4068 strlcpy(dst
, src
, dstsize
);
4073 * Make path relative to testfile...
4076 strlcpy(dst
, testfile
, dstsize
);
4077 if ((dstptr
= strrchr(dst
, '/')) != NULL
)
4080 dstptr
= dst
; /* Should never happen */
4082 strlcpy(dstptr
, src
, dstsize
- (size_t)(dstptr
- dst
));
4090 * 'get_string()' - Get a pointer to a string value or the portion of interest.
4093 static const char * /* O - Pointer to string */
4094 get_string(ipp_attribute_t
*attr
, /* I - IPP attribute */
4095 int element
, /* I - Element to fetch */
4096 int flags
, /* I - Value ("with") flags */
4097 char *buffer
, /* I - Temporary buffer */
4098 size_t bufsize
) /* I - Size of temporary buffer */
4100 const char *value
; /* Value */
4101 char *ptr
, /* Pointer into value */
4102 scheme
[256], /* URI scheme */
4103 userpass
[256], /* Username/password */
4104 hostname
[256], /* Hostname */
4105 resource
[1024]; /* Resource */
4106 int port
; /* Port number */
4109 value
= ippGetString(attr
, element
, NULL
);
4111 if (flags
& _CUPS_WITH_HOSTNAME
)
4113 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), buffer
, (int)bufsize
, &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
4116 ptr
= buffer
+ strlen(buffer
) - 1;
4117 if (ptr
>= buffer
&& *ptr
== '.')
4118 *ptr
= '\0'; /* Drop trailing "." */
4122 else if (flags
& _CUPS_WITH_RESOURCE
)
4124 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, buffer
, (int)bufsize
) < HTTP_URI_STATUS_OK
)
4129 else if (flags
& _CUPS_WITH_SCHEME
)
4131 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, buffer
, (int)bufsize
, userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
4136 else if (ippGetValueTag(attr
) == IPP_TAG_URI
&& (!strncmp(value
, "ipp://", 6) || !strncmp(value
, "http://", 7) || !strncmp(value
, "ipps://", 7) || !strncmp(value
, "https://", 8)))
4138 http_uri_status_t status
= httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
));
4140 if (status
< HTTP_URI_STATUS_OK
)
4151 * Normalize URI with no trailing dot...
4154 if ((ptr
= hostname
+ strlen(hostname
) - 1) >= hostname
&& *ptr
== '.')
4157 httpAssembleURI(HTTP_URI_CODING_ALL
, buffer
, (int)bufsize
, scheme
, userpass
, hostname
, port
, resource
);
4168 * 'get_token()' - Get a token from a file.
4171 static char * /* O - Token from file or NULL on EOF */
4172 get_token(cups_file_t
*fp
, /* I - File to read from */
4173 char *buf
, /* I - Buffer to read into */
4174 int buflen
, /* I - Length of buffer */
4175 int *linenum
) /* IO - Current line number */
4177 int ch
, /* Character from file */
4178 quote
; /* Quoting character */
4179 char *bufptr
, /* Pointer into buffer */
4180 *bufend
; /* End of buffer */
4186 * Skip whitespace...
4189 while (isspace(ch
= cupsFileGetChar(fp
)))
4201 else if (ch
== '\'' || ch
== '\"')
4204 * Quoted text or regular expression...
4209 bufend
= buf
+ buflen
- 1;
4211 while ((ch
= cupsFileGetChar(fp
)) != EOF
)
4216 * Escape next character...
4219 if (bufptr
< bufend
)
4220 *bufptr
++ = (char)ch
;
4222 if ((ch
= cupsFileGetChar(fp
)) != EOF
&& bufptr
< bufend
)
4223 *bufptr
++ = (char)ch
;
4225 else if (ch
== quote
)
4227 else if (bufptr
< bufend
)
4228 *bufptr
++ = (char)ch
;
4241 while ((ch
= cupsFileGetChar(fp
)) != EOF
)
4247 else if (ch
== '{' || ch
== '}' || ch
== ',')
4257 * Whitespace delimited text...
4260 cupsFileSeek(fp
, cupsFileTell(fp
) - 1);
4263 bufend
= buf
+ buflen
- 1;
4265 while ((ch
= cupsFileGetChar(fp
)) != EOF
)
4266 if (isspace(ch
) || ch
== '#')
4268 else if (bufptr
< bufend
)
4269 *bufptr
++ = (char)ch
;
4272 cupsFileSeek(fp
, cupsFileTell(fp
) - 1);
4273 else if (ch
== '\n')
4285 * 'get_variable()' - Get the value of a variable.
4288 static char * /* O - Value or NULL */
4289 get_variable(_cups_vars_t
*vars
, /* I - Variables */
4290 const char *name
) /* I - Variable name */
4292 _cups_var_t key
, /* Search key */
4293 *match
; /* Matching variable, if any */
4296 key
.name
= (char *)name
;
4297 match
= cupsArrayFind(vars
->vars
, &key
);
4299 return (match
? match
->value
: NULL
);
4304 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
4308 static char * /* O - ISO 8601 date/time string */
4309 iso_date(ipp_uchar_t
*date
) /* I - IPP (RFC 1903) date/time value */
4311 time_t utctime
; /* UTC time since 1970 */
4312 struct tm
*utcdate
; /* UTC date/time */
4313 static char buffer
[255]; /* String buffer */
4316 utctime
= ippDateToTime(date
);
4317 utcdate
= gmtime(&utctime
);
4319 snprintf(buffer
, sizeof(buffer
), "%04d-%02d-%02dT%02d:%02d:%02dZ",
4320 utcdate
->tm_year
+ 1900, utcdate
->tm_mon
+ 1, utcdate
->tm_mday
,
4321 utcdate
->tm_hour
, utcdate
->tm_min
, utcdate
->tm_sec
);
4328 * 'password_cb()' - Password callback for authenticated tests.
4331 static const char * /* O - Password */
4332 password_cb(const char *prompt
) /* I - Prompt (unused) */
4336 if (PasswordTries
< 3)
4340 cupsSetUser(Username
);
4350 * 'pause_message()' - Display the message and pause until the user presses a key.
4354 pause_message(const char *message
) /* I - Message */
4357 HANDLE tty
; /* Console handle */
4358 DWORD mode
; /* Console mode */
4359 char key
; /* Key press */
4360 DWORD bytes
; /* Bytes read for key press */
4364 * Disable input echo and set raw input...
4367 if ((tty
= GetStdHandle(STD_INPUT_HANDLE
)) == INVALID_HANDLE_VALUE
)
4370 if (!GetConsoleMode(tty
, &mode
))
4373 if (!SetConsoleMode(tty
, 0))
4377 int tty
; /* /dev/tty - never read from stdin */
4378 struct termios original
, /* Original input mode */
4379 noecho
; /* No echo input mode */
4380 char key
; /* Current key press */
4384 * Disable input echo and set raw input...
4387 if ((tty
= open("/dev/tty", O_RDONLY
)) < 0)
4390 if (tcgetattr(tty
, &original
))
4397 noecho
.c_lflag
&= (tcflag_t
)~(ICANON
| ECHO
| ECHOE
| ISIG
);
4399 if (tcsetattr(tty
, TCSAFLUSH
, &noecho
))
4407 * Display the prompt...
4410 cupsFilePrintf(cupsFileStdout(), "%s\n---- PRESS ANY KEY ----", message
);
4417 ReadFile(tty
, &key
, 1, &bytes
, NULL
);
4423 SetConsoleMode(tty
, mode
);
4436 tcsetattr(tty
, TCSAFLUSH
, &original
);
4441 * Erase the "press any key" prompt...
4444 cupsFilePuts(cupsFileStdout(), "\r \r");
4449 * 'print_attr()' - Print an attribute on the screen.
4453 print_attr(cups_file_t
*outfile
, /* I - Output file */
4454 int format
, /* I - Output format */
4455 ipp_attribute_t
*attr
, /* I - Attribute to print */
4456 ipp_tag_t
*group
) /* IO - Current group */
4458 int i
; /* Looping var */
4459 ipp_attribute_t
*colattr
; /* Collection attribute */
4462 if (format
== _CUPS_OUTPUT_PLIST
)
4464 if (!attr
->name
|| (group
&& *group
!= attr
->group_tag
))
4466 if (attr
->group_tag
!= IPP_TAG_ZERO
)
4468 cupsFilePuts(outfile
, "</dict>\n");
4469 cupsFilePuts(outfile
, "<dict>\n");
4473 *group
= attr
->group_tag
;
4479 print_xml_string(outfile
, "key", attr
->name
);
4480 if (attr
->num_values
> 1)
4481 cupsFilePuts(outfile
, "<array>\n");
4483 switch (attr
->value_tag
)
4485 case IPP_TAG_INTEGER
:
4487 for (i
= 0; i
< attr
->num_values
; i
++)
4488 cupsFilePrintf(outfile
, "<integer>%d</integer>\n", attr
->values
[i
].integer
);
4491 case IPP_TAG_BOOLEAN
:
4492 for (i
= 0; i
< attr
->num_values
; i
++)
4493 cupsFilePuts(outfile
, attr
->values
[i
].boolean
? "<true />\n" : "<false />\n");
4496 case IPP_TAG_RANGE
:
4497 for (i
= 0; i
< attr
->num_values
; i
++)
4498 cupsFilePrintf(outfile
, "<dict><key>lower</key><integer>%d</integer>"
4499 "<key>upper</key><integer>%d</integer></dict>\n",
4500 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
4503 case IPP_TAG_RESOLUTION
:
4504 for (i
= 0; i
< attr
->num_values
; i
++)
4505 cupsFilePrintf(outfile
, "<dict><key>xres</key><integer>%d</integer>"
4506 "<key>yres</key><integer>%d</integer>"
4507 "<key>units</key><string>%s</string></dict>\n",
4508 attr
->values
[i
].resolution
.xres
,
4509 attr
->values
[i
].resolution
.yres
,
4510 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
4515 for (i
= 0; i
< attr
->num_values
; i
++)
4516 cupsFilePrintf(outfile
, "<date>%s</date>\n", iso_date(attr
->values
[i
].date
));
4519 case IPP_TAG_STRING
:
4520 for (i
= 0; i
< attr
->num_values
; i
++)
4522 char buffer
[IPP_MAX_LENGTH
* 5 / 4 + 1];
4525 cupsFilePrintf(outfile
, "<data>%s</data>\n",
4526 httpEncode64_2(buffer
, sizeof(buffer
),
4527 attr
->values
[i
].unknown
.data
,
4528 attr
->values
[i
].unknown
.length
));
4534 case IPP_TAG_KEYWORD
:
4536 case IPP_TAG_URISCHEME
:
4537 case IPP_TAG_CHARSET
:
4538 case IPP_TAG_LANGUAGE
:
4539 case IPP_TAG_MIMETYPE
:
4540 for (i
= 0; i
< attr
->num_values
; i
++)
4541 print_xml_string(outfile
, "string", attr
->values
[i
].string
.text
);
4544 case IPP_TAG_TEXTLANG
:
4545 case IPP_TAG_NAMELANG
:
4546 for (i
= 0; i
< attr
->num_values
; i
++)
4548 cupsFilePuts(outfile
, "<dict><key>language</key><string>");
4549 print_xml_string(outfile
, NULL
, attr
->values
[i
].string
.language
);
4550 cupsFilePuts(outfile
, "</string><key>string</key><string>");
4551 print_xml_string(outfile
, NULL
, attr
->values
[i
].string
.text
);
4552 cupsFilePuts(outfile
, "</string></dict>\n");
4556 case IPP_TAG_BEGIN_COLLECTION
:
4557 for (i
= 0; i
< attr
->num_values
; i
++)
4559 cupsFilePuts(outfile
, "<dict>\n");
4560 for (colattr
= attr
->values
[i
].collection
->attrs
;
4562 colattr
= colattr
->next
)
4563 print_attr(outfile
, format
, colattr
, NULL
);
4564 cupsFilePuts(outfile
, "</dict>\n");
4569 cupsFilePrintf(outfile
, "<string><<%s>></string>\n", ippTagString(attr
->value_tag
));
4573 if (attr
->num_values
> 1)
4574 cupsFilePuts(outfile
, "</array>\n");
4578 char buffer
[131072]; /* Value buffer */
4580 if (format
== _CUPS_OUTPUT_TEST
)
4584 cupsFilePuts(outfile
, " -- separator --\n");
4588 cupsFilePrintf(outfile
, " %s (%s%s) = ", attr
->name
, attr
->num_values
> 1 ? "1setOf " : "", ippTagString(attr
->value_tag
));
4591 ippAttributeString(attr
, buffer
, sizeof(buffer
));
4592 cupsFilePrintf(outfile
, "%s\n", buffer
);
4598 * 'print_csv()' - Print a line of CSV text.
4603 cups_file_t
*outfile
, /* I - Output file */
4604 ipp_attribute_t
*attr
, /* I - First attribute for line */
4605 int num_displayed
, /* I - Number of attributes to display */
4606 char **displayed
, /* I - Attributes to display */
4607 size_t *widths
) /* I - Column widths */
4609 int i
; /* Looping var */
4610 size_t maxlength
; /* Max length of all columns */
4611 char *buffer
, /* String buffer */
4612 *bufptr
; /* Pointer into buffer */
4613 ipp_attribute_t
*current
; /* Current attribute */
4617 * Get the maximum string length we have to show and allocate...
4620 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4621 if (widths
[i
] > maxlength
)
4622 maxlength
= widths
[i
];
4626 if ((buffer
= malloc(maxlength
)) == NULL
)
4630 * Loop through the attributes to display...
4635 for (i
= 0; i
< num_displayed
; i
++)
4638 cupsFilePutChar(outfile
, ',');
4642 for (current
= attr
; current
; current
= current
->next
)
4646 else if (!strcmp(current
->name
, displayed
[i
]))
4648 ippAttributeString(current
, buffer
, maxlength
);
4653 if (strchr(buffer
, ',') != NULL
|| strchr(buffer
, '\"') != NULL
||
4654 strchr(buffer
, '\\') != NULL
)
4656 cupsFilePutChar(cupsFileStdout(), '\"');
4657 for (bufptr
= buffer
; *bufptr
; bufptr
++)
4659 if (*bufptr
== '\\' || *bufptr
== '\"')
4660 cupsFilePutChar(cupsFileStdout(), '\\');
4661 cupsFilePutChar(cupsFileStdout(), *bufptr
);
4663 cupsFilePutChar(cupsFileStdout(), '\"');
4666 cupsFilePuts(outfile
, buffer
);
4668 cupsFilePutChar(cupsFileStdout(), '\n');
4672 for (i
= 0; i
< num_displayed
; i
++)
4675 cupsFilePutChar(cupsFileStdout(), ',');
4677 cupsFilePuts(outfile
, displayed
[i
]);
4679 cupsFilePutChar(cupsFileStdout(), '\n');
4687 * 'print_fatal_error()' - Print a fatal error message.
4691 print_fatal_error(cups_file_t
*outfile
, /* I - Output file */
4692 const char *s
, /* I - Printf-style format string */
4693 ...) /* I - Additional arguments as needed */
4695 char buffer
[10240]; /* Format buffer */
4696 va_list ap
; /* Pointer to arguments */
4700 * Format the error message...
4704 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
4711 if (Output
== _CUPS_OUTPUT_PLIST
)
4713 print_xml_header(outfile
);
4714 print_xml_trailer(outfile
, 0, buffer
);
4717 _cupsLangPrintf(stderr
, "ipptool: %s", buffer
);
4722 * 'print_line()' - Print a line of formatted or CSV text.
4727 cups_file_t
*outfile
, /* I - Output file */
4728 ipp_attribute_t
*attr
, /* I - First attribute for line */
4729 int num_displayed
, /* I - Number of attributes to display */
4730 char **displayed
, /* I - Attributes to display */
4731 size_t *widths
) /* I - Column widths */
4733 int i
; /* Looping var */
4734 size_t maxlength
; /* Max length of all columns */
4735 char *buffer
; /* String buffer */
4736 ipp_attribute_t
*current
; /* Current attribute */
4740 * Get the maximum string length we have to show and allocate...
4743 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4744 if (widths
[i
] > maxlength
)
4745 maxlength
= widths
[i
];
4749 if ((buffer
= malloc(maxlength
)) == NULL
)
4753 * Loop through the attributes to display...
4758 for (i
= 0; i
< num_displayed
; i
++)
4761 cupsFilePutChar(cupsFileStdout(), ' ');
4765 for (current
= attr
; current
; current
= current
->next
)
4769 else if (!strcmp(current
->name
, displayed
[i
]))
4771 ippAttributeString(current
, buffer
, maxlength
);
4776 cupsFilePrintf(outfile
, "%*s", (int)-widths
[i
], buffer
);
4778 cupsFilePutChar(cupsFileStdout(), '\n');
4782 for (i
= 0; i
< num_displayed
; i
++)
4785 cupsFilePutChar(cupsFileStdout(), ' ');
4787 cupsFilePrintf(outfile
, "%*s", (int)-widths
[i
], displayed
[i
]);
4789 cupsFilePutChar(cupsFileStdout(), '\n');
4791 for (i
= 0; i
< num_displayed
; i
++)
4794 cupsFilePutChar(cupsFileStdout(), ' ');
4796 memset(buffer
, '-', widths
[i
]);
4797 buffer
[widths
[i
]] = '\0';
4798 cupsFilePuts(outfile
, buffer
);
4800 cupsFilePutChar(cupsFileStdout(), '\n');
4808 * 'print_xml_header()' - Print a standard XML plist header.
4812 print_xml_header(cups_file_t
*outfile
) /* I - Output file */
4816 cupsFilePuts(outfile
, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
4817 cupsFilePuts(outfile
, "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
4818 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n");
4819 cupsFilePuts(outfile
, "<plist version=\"1.0\">\n");
4820 cupsFilePuts(outfile
, "<dict>\n");
4821 cupsFilePuts(outfile
, "<key>ipptoolVersion</key>\n");
4822 cupsFilePuts(outfile
, "<string>" CUPS_SVERSION
"</string>\n");
4823 cupsFilePuts(outfile
, "<key>Transfer</key>\n");
4824 cupsFilePrintf(outfile
, "<string>%s</string>\n",
4825 Transfer
== _CUPS_TRANSFER_AUTO
? "auto" :
4826 Transfer
== _CUPS_TRANSFER_CHUNKED
? "chunked" : "length");
4827 cupsFilePuts(outfile
, "<key>Tests</key>\n");
4828 cupsFilePuts(outfile
, "<array>\n");
4836 * 'print_xml_string()' - Print an XML string with escaping.
4840 print_xml_string(cups_file_t
*outfile
, /* I - Output file */
4841 const char *element
, /* I - Element name or NULL */
4842 const char *s
) /* I - String to print */
4845 cupsFilePrintf(outfile
, "<%s>", element
);
4850 cupsFilePuts(outfile
, "&");
4852 cupsFilePuts(outfile
, "<");
4854 cupsFilePuts(outfile
, ">");
4855 else if ((*s
& 0xe0) == 0xc0)
4858 * Validate UTF-8 two-byte sequence...
4861 if ((s
[1] & 0xc0) != 0x80)
4863 cupsFilePutChar(outfile
, '?');
4868 cupsFilePutChar(outfile
, *s
++);
4869 cupsFilePutChar(outfile
, *s
);
4872 else if ((*s
& 0xf0) == 0xe0)
4875 * Validate UTF-8 three-byte sequence...
4878 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80)
4880 cupsFilePutChar(outfile
, '?');
4885 cupsFilePutChar(outfile
, *s
++);
4886 cupsFilePutChar(outfile
, *s
++);
4887 cupsFilePutChar(outfile
, *s
);
4890 else if ((*s
& 0xf8) == 0xf0)
4893 * Validate UTF-8 four-byte sequence...
4896 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80 ||
4897 (s
[3] & 0xc0) != 0x80)
4899 cupsFilePutChar(outfile
, '?');
4904 cupsFilePutChar(outfile
, *s
++);
4905 cupsFilePutChar(outfile
, *s
++);
4906 cupsFilePutChar(outfile
, *s
++);
4907 cupsFilePutChar(outfile
, *s
);
4910 else if ((*s
& 0x80) || (*s
< ' ' && !isspace(*s
& 255)))
4913 * Invalid control character...
4916 cupsFilePutChar(outfile
, '?');
4919 cupsFilePutChar(outfile
, *s
);
4925 cupsFilePrintf(outfile
, "</%s>\n", element
);
4930 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
4934 print_xml_trailer(cups_file_t
*outfile
, /* I - Output file */
4935 int success
, /* I - 1 on success, 0 on failure */
4936 const char *message
) /* I - Error message or NULL */
4940 cupsFilePuts(outfile
, "</array>\n");
4941 cupsFilePuts(outfile
, "<key>Successful</key>\n");
4942 cupsFilePuts(outfile
, success
? "<true />\n" : "<false />\n");
4945 cupsFilePuts(outfile
, "<key>ErrorMessage</key>\n");
4946 print_xml_string(outfile
, "string", message
);
4948 cupsFilePuts(outfile
, "</dict>\n");
4949 cupsFilePuts(outfile
, "</plist>\n");
4957 * 'set_variable()' - Set a variable value.
4961 set_variable(cups_file_t
*outfile
, /* I - Output file */
4962 _cups_vars_t
*vars
, /* I - Variables */
4963 const char *name
, /* I - Variable name */
4964 const char *value
) /* I - Value string */
4966 _cups_var_t key
, /* Search key */
4967 *var
; /* New variable */
4970 if (!_cups_strcasecmp(name
, "filename"))
4973 free(vars
->filename
);
4975 vars
->filename
= strdup(value
);
4979 key
.name
= (char *)name
;
4980 if ((var
= cupsArrayFind(vars
->vars
, &key
)) != NULL
)
4983 var
->value
= strdup(value
);
4985 else if ((var
= malloc(sizeof(_cups_var_t
))) == NULL
)
4987 print_fatal_error(outfile
, "Unable to allocate memory for variable \"%s\".", name
);
4992 var
->name
= strdup(name
);
4993 var
->value
= strdup(value
);
4995 cupsArrayAdd(vars
->vars
, var
);
5002 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
5006 sigterm_handler(int sig
) /* I - Signal number (unused) */
5012 signal(SIGINT
, SIG_DFL
);
5013 signal(SIGTERM
, SIG_DFL
);
5019 * 'timeout_cb()' - Handle HTTP timeouts.
5022 static int /* O - 1 to continue, 0 to cancel */
5023 timeout_cb(http_t
*http
, /* I - Connection to server */
5024 void *user_data
) /* I - User data (unused) */
5026 int buffered
= 0; /* Bytes buffered but not yet sent */
5032 * If the socket still have data waiting to be sent to the printer (as can
5033 * happen if the printer runs out of paper), continue to wait until the output
5034 * buffer is empty...
5037 #ifdef SO_NWRITE /* macOS and some versions of Linux */
5038 socklen_t len
= sizeof(buffered
); /* Size of return value */
5040 if (getsockopt(httpGetFd(http
), SOL_SOCKET
, SO_NWRITE
, &buffered
, &len
))
5043 #elif defined(SIOCOUTQ) /* Others except Windows */
5044 if (ioctl(httpGetFd(http
), SIOCOUTQ
, &buffered
))
5047 #else /* Windows (not possible) */
5049 #endif /* SO_NWRITE */
5051 return (buffered
> 0);
5056 * 'usage()' - Show program usage.
5062 _cupsLangPuts(stderr
, _("Usage: ipptool [options] URI filename [ ... filenameN ]"));
5063 _cupsLangPuts(stderr
, _("Options:"));
5064 _cupsLangPuts(stderr
, _(" --help Show help."));
5065 _cupsLangPuts(stderr
, _(" --stop-after-include-error\n"
5066 " Stop tests after a failed INCLUDE."));
5067 _cupsLangPuts(stderr
, _(" --version Show version."));
5068 _cupsLangPuts(stderr
, _(" -4 Connect using IPv4."));
5069 _cupsLangPuts(stderr
, _(" -6 Connect using IPv6."));
5070 _cupsLangPuts(stderr
, _(" -C Send requests using "
5071 "chunking (default)."));
5072 _cupsLangPuts(stderr
, _(" -E Test with encryption using HTTP Upgrade to TLS."));
5073 _cupsLangPuts(stderr
, _(" -I Ignore errors."));
5074 _cupsLangPuts(stderr
, _(" -L Send requests using content-length."));
5075 _cupsLangPuts(stderr
, _(" -P filename.plist Produce XML plist to a file and test report to standard output."));
5076 _cupsLangPuts(stderr
, _(" -S Test with encryption using HTTPS."));
5077 _cupsLangPuts(stderr
, _(" -T seconds Set the receive/send timeout in seconds."));
5078 _cupsLangPuts(stderr
, _(" -V version Set default IPP version."));
5079 _cupsLangPuts(stderr
, _(" -X Produce XML plist instead of plain text."));
5080 _cupsLangPuts(stderr
, _(" -c Produce CSV output."));
5081 _cupsLangPuts(stderr
, _(" -d name=value Set named variable to value."));
5082 _cupsLangPuts(stderr
, _(" -f filename Set default request filename."));
5083 _cupsLangPuts(stderr
, _(" -h Validate HTTP response headers."));
5084 _cupsLangPuts(stderr
, _(" -i seconds Repeat the last file with the given time interval."));
5085 _cupsLangPuts(stderr
, _(" -l Produce plain text output."));
5086 _cupsLangPuts(stderr
, _(" -n count Repeat the last file the given number of times."));
5087 _cupsLangPuts(stderr
, _(" -q Run silently."));
5088 _cupsLangPuts(stderr
, _(" -t Produce a test report."));
5089 _cupsLangPuts(stderr
, _(" -v Be verbose."));
5096 * 'validate_attr()' - Determine whether an attribute is valid.
5099 static int /* O - 1 if valid, 0 otherwise */
5100 validate_attr(cups_file_t
*outfile
, /* I - Output file */
5101 cups_array_t
*errors
, /* I - Errors array */
5102 ipp_attribute_t
*attr
) /* I - Attribute to validate */
5104 int i
; /* Looping var */
5105 char scheme
[64], /* Scheme from URI */
5106 userpass
[256], /* Username/password from URI */
5107 hostname
[256], /* Hostname from URI */
5108 resource
[1024]; /* Resource from URI */
5109 int port
, /* Port number from URI */
5110 uri_status
, /* URI separation status */
5111 valid
= 1; /* Is the attribute valid? */
5112 const char *ptr
; /* Pointer into string */
5113 ipp_attribute_t
*colattr
; /* Collection attribute */
5114 regex_t re
; /* Regular expression */
5115 ipp_uchar_t
*date
; /* Current date value */
5126 * Validate the attribute name.
5129 for (ptr
= attr
->name
; *ptr
; ptr
++)
5130 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' && *ptr
!= '_')
5133 if (*ptr
|| ptr
== attr
->name
)
5138 "\"%s\": Bad attribute name - invalid character "
5139 "(RFC 2911 section 4.1.3).", attr
->name
);
5142 if ((ptr
- attr
->name
) > 255)
5147 "\"%s\": Bad attribute name - bad length "
5148 "(RFC 2911 section 4.1.3).", attr
->name
);
5151 switch (attr
->value_tag
)
5153 case IPP_TAG_INTEGER
:
5156 case IPP_TAG_BOOLEAN
:
5157 for (i
= 0; i
< attr
->num_values
; i
++)
5159 if (attr
->values
[i
].boolean
!= 0 &&
5160 attr
->values
[i
].boolean
!= 1)
5165 "\"%s\": Bad boolen value %d "
5166 "(RFC 2911 section 4.1.11).", attr
->name
,
5167 attr
->values
[i
].boolean
);
5173 for (i
= 0; i
< attr
->num_values
; i
++)
5175 if (attr
->values
[i
].integer
< 1)
5180 "\"%s\": Bad enum value %d - out of range "
5181 "(RFC 2911 section 4.1.4).", attr
->name
,
5182 attr
->values
[i
].integer
);
5187 case IPP_TAG_STRING
:
5188 for (i
= 0; i
< attr
->num_values
; i
++)
5190 if (attr
->values
[i
].unknown
.length
> IPP_MAX_OCTETSTRING
)
5195 "\"%s\": Bad octetString value - bad length %d "
5196 "(RFC 2911 section 4.1.10).", attr
->name
,
5197 attr
->values
[i
].unknown
.length
);
5203 for (i
= 0; i
< attr
->num_values
; i
++)
5205 date
= attr
->values
[i
].date
;
5207 if (date
[2] < 1 || date
[2] > 12)
5212 "\"%s\": Bad dateTime month %u "
5213 "(RFC 2911 section 4.1.14).", attr
->name
, date
[2]);
5216 if (date
[3] < 1 || date
[3] > 31)
5221 "\"%s\": Bad dateTime day %u "
5222 "(RFC 2911 section 4.1.14).", attr
->name
, date
[3]);
5230 "\"%s\": Bad dateTime hours %u "
5231 "(RFC 2911 section 4.1.14).", attr
->name
, date
[4]);
5239 "\"%s\": Bad dateTime minutes %u "
5240 "(RFC 2911 section 4.1.14).", attr
->name
, date
[5]);
5248 "\"%s\": Bad dateTime seconds %u "
5249 "(RFC 2911 section 4.1.14).", attr
->name
, date
[6]);
5257 "\"%s\": Bad dateTime deciseconds %u "
5258 "(RFC 2911 section 4.1.14).", attr
->name
, date
[7]);
5261 if (date
[8] != '-' && date
[8] != '+')
5266 "\"%s\": Bad dateTime UTC sign '%c' "
5267 "(RFC 2911 section 4.1.14).", attr
->name
, date
[8]);
5275 "\"%s\": Bad dateTime UTC hours %u "
5276 "(RFC 2911 section 4.1.14).", attr
->name
, date
[9]);
5284 "\"%s\": Bad dateTime UTC minutes %u "
5285 "(RFC 2911 section 4.1.14).", attr
->name
, date
[10]);
5290 case IPP_TAG_RESOLUTION
:
5291 for (i
= 0; i
< attr
->num_values
; i
++)
5293 if (attr
->values
[i
].resolution
.xres
<= 0)
5298 "\"%s\": Bad resolution value %dx%d%s - cross "
5299 "feed resolution must be positive "
5300 "(RFC 2911 section 4.1.15).", attr
->name
,
5301 attr
->values
[i
].resolution
.xres
,
5302 attr
->values
[i
].resolution
.yres
,
5303 attr
->values
[i
].resolution
.units
==
5304 IPP_RES_PER_INCH
? "dpi" :
5305 attr
->values
[i
].resolution
.units
==
5306 IPP_RES_PER_CM
? "dpcm" : "unknown");
5309 if (attr
->values
[i
].resolution
.yres
<= 0)
5314 "\"%s\": Bad resolution value %dx%d%s - feed "
5315 "resolution must be positive "
5316 "(RFC 2911 section 4.1.15).", attr
->name
,
5317 attr
->values
[i
].resolution
.xres
,
5318 attr
->values
[i
].resolution
.yres
,
5319 attr
->values
[i
].resolution
.units
==
5320 IPP_RES_PER_INCH
? "dpi" :
5321 attr
->values
[i
].resolution
.units
==
5322 IPP_RES_PER_CM
? "dpcm" : "unknown");
5325 if (attr
->values
[i
].resolution
.units
!= IPP_RES_PER_INCH
&&
5326 attr
->values
[i
].resolution
.units
!= IPP_RES_PER_CM
)
5331 "\"%s\": Bad resolution value %dx%d%s - bad "
5332 "units value (RFC 2911 section 4.1.15).",
5333 attr
->name
, attr
->values
[i
].resolution
.xres
,
5334 attr
->values
[i
].resolution
.yres
,
5335 attr
->values
[i
].resolution
.units
==
5336 IPP_RES_PER_INCH
? "dpi" :
5337 attr
->values
[i
].resolution
.units
==
5338 IPP_RES_PER_CM
? "dpcm" : "unknown");
5343 case IPP_TAG_RANGE
:
5344 for (i
= 0; i
< attr
->num_values
; i
++)
5346 if (attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
5351 "\"%s\": Bad rangeOfInteger value %d-%d - lower "
5352 "greater than upper (RFC 2911 section 4.1.13).",
5353 attr
->name
, attr
->values
[i
].range
.lower
,
5354 attr
->values
[i
].range
.upper
);
5359 case IPP_TAG_BEGIN_COLLECTION
:
5360 for (i
= 0; i
< attr
->num_values
; i
++)
5362 for (colattr
= attr
->values
[i
].collection
->attrs
;
5364 colattr
= colattr
->next
)
5366 if (!validate_attr(outfile
, NULL
, colattr
))
5373 if (colattr
&& errors
)
5375 add_stringf(errors
, "\"%s\": Bad collection value.", attr
->name
);
5379 validate_attr(outfile
, errors
, colattr
);
5380 colattr
= colattr
->next
;
5387 case IPP_TAG_TEXTLANG
:
5388 for (i
= 0; i
< attr
->num_values
; i
++)
5390 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5392 if ((*ptr
& 0xe0) == 0xc0)
5395 if ((*ptr
& 0xc0) != 0x80)
5398 else if ((*ptr
& 0xf0) == 0xe0)
5401 if ((*ptr
& 0xc0) != 0x80)
5404 if ((*ptr
& 0xc0) != 0x80)
5407 else if ((*ptr
& 0xf8) == 0xf0)
5410 if ((*ptr
& 0xc0) != 0x80)
5413 if ((*ptr
& 0xc0) != 0x80)
5416 if ((*ptr
& 0xc0) != 0x80)
5419 else if (*ptr
& 0x80)
5428 "\"%s\": Bad text value \"%s\" - bad UTF-8 "
5429 "sequence (RFC 2911 section 4.1.1).", attr
->name
,
5430 attr
->values
[i
].string
.text
);
5433 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_TEXT
- 1))
5438 "\"%s\": Bad text value \"%s\" - bad length %d "
5439 "(RFC 2911 section 4.1.1).", attr
->name
,
5440 attr
->values
[i
].string
.text
,
5441 (int)strlen(attr
->values
[i
].string
.text
));
5447 case IPP_TAG_NAMELANG
:
5448 for (i
= 0; i
< attr
->num_values
; i
++)
5450 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5452 if ((*ptr
& 0xe0) == 0xc0)
5455 if ((*ptr
& 0xc0) != 0x80)
5458 else if ((*ptr
& 0xf0) == 0xe0)
5461 if ((*ptr
& 0xc0) != 0x80)
5464 if ((*ptr
& 0xc0) != 0x80)
5467 else if ((*ptr
& 0xf8) == 0xf0)
5470 if ((*ptr
& 0xc0) != 0x80)
5473 if ((*ptr
& 0xc0) != 0x80)
5476 if ((*ptr
& 0xc0) != 0x80)
5479 else if (*ptr
& 0x80)
5488 "\"%s\": Bad name value \"%s\" - bad UTF-8 "
5489 "sequence (RFC 2911 section 4.1.2).", attr
->name
,
5490 attr
->values
[i
].string
.text
);
5493 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_NAME
- 1))
5498 "\"%s\": Bad name value \"%s\" - bad length %d "
5499 "(RFC 2911 section 4.1.2).", attr
->name
,
5500 attr
->values
[i
].string
.text
,
5501 (int)strlen(attr
->values
[i
].string
.text
));
5506 case IPP_TAG_KEYWORD
:
5507 for (i
= 0; i
< attr
->num_values
; i
++)
5509 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5510 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' &&
5514 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5519 "\"%s\": Bad keyword value \"%s\" - invalid "
5520 "character (RFC 2911 section 4.1.3).",
5521 attr
->name
, attr
->values
[i
].string
.text
);
5524 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_KEYWORD
- 1))
5529 "\"%s\": Bad keyword value \"%s\" - bad "
5530 "length %d (RFC 2911 section 4.1.3).",
5531 attr
->name
, attr
->values
[i
].string
.text
,
5532 (int)strlen(attr
->values
[i
].string
.text
));
5538 for (i
= 0; i
< attr
->num_values
; i
++)
5540 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
,
5541 attr
->values
[i
].string
.text
,
5542 scheme
, sizeof(scheme
),
5543 userpass
, sizeof(userpass
),
5544 hostname
, sizeof(hostname
),
5545 &port
, resource
, sizeof(resource
));
5547 if (uri_status
< HTTP_URI_OK
)
5552 "\"%s\": Bad URI value \"%s\" - %s "
5553 "(RFC 2911 section 4.1.5).", attr
->name
,
5554 attr
->values
[i
].string
.text
,
5555 httpURIStatusString(uri_status
));
5558 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_URI
- 1))
5563 "\"%s\": Bad URI value \"%s\" - bad length %d "
5564 "(RFC 2911 section 4.1.5).", attr
->name
,
5565 attr
->values
[i
].string
.text
,
5566 (int)strlen(attr
->values
[i
].string
.text
));
5571 case IPP_TAG_URISCHEME
:
5572 for (i
= 0; i
< attr
->num_values
; i
++)
5574 ptr
= attr
->values
[i
].string
.text
;
5575 if (islower(*ptr
& 255))
5577 for (ptr
++; *ptr
; ptr
++)
5578 if (!islower(*ptr
& 255) && !isdigit(*ptr
& 255) &&
5579 *ptr
!= '+' && *ptr
!= '-' && *ptr
!= '.')
5583 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5588 "\"%s\": Bad uriScheme value \"%s\" - bad "
5589 "characters (RFC 2911 section 4.1.6).",
5590 attr
->name
, attr
->values
[i
].string
.text
);
5593 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_URISCHEME
- 1))
5598 "\"%s\": Bad uriScheme value \"%s\" - bad "
5599 "length %d (RFC 2911 section 4.1.6).",
5600 attr
->name
, attr
->values
[i
].string
.text
,
5601 (int)strlen(attr
->values
[i
].string
.text
));
5606 case IPP_TAG_CHARSET
:
5607 for (i
= 0; i
< attr
->num_values
; i
++)
5609 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5610 if (!isprint(*ptr
& 255) || isupper(*ptr
& 255) ||
5611 isspace(*ptr
& 255))
5614 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5619 "\"%s\": Bad charset value \"%s\" - bad "
5620 "characters (RFC 2911 section 4.1.7).",
5621 attr
->name
, attr
->values
[i
].string
.text
);
5624 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_CHARSET
- 1))
5629 "\"%s\": Bad charset value \"%s\" - bad "
5630 "length %d (RFC 2911 section 4.1.7).",
5631 attr
->name
, attr
->values
[i
].string
.text
,
5632 (int)strlen(attr
->values
[i
].string
.text
));
5637 case IPP_TAG_LANGUAGE
:
5639 * The following regular expression is derived from the ABNF for
5640 * language tags in RFC 4646. All I can say is that this is the
5641 * easiest way to check the values...
5644 if ((i
= regcomp(&re
,
5646 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
5648 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
5649 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
5650 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
5651 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
5652 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
5654 "x(-[a-z0-9]{1,8})+" /* privateuse */
5656 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
5658 REG_NOSUB
| REG_EXTENDED
)) != 0)
5660 char temp
[256]; /* Temporary error string */
5662 regerror(i
, &re
, temp
, sizeof(temp
));
5663 print_fatal_error(outfile
, "Unable to compile naturalLanguage regular "
5664 "expression: %s.", temp
);
5668 for (i
= 0; i
< attr
->num_values
; i
++)
5670 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5675 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5676 "characters (RFC 2911 section 4.1.8).",
5677 attr
->name
, attr
->values
[i
].string
.text
);
5680 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_LANGUAGE
- 1))
5685 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5686 "length %d (RFC 2911 section 4.1.8).",
5687 attr
->name
, attr
->values
[i
].string
.text
,
5688 (int)strlen(attr
->values
[i
].string
.text
));
5695 case IPP_TAG_MIMETYPE
:
5697 * The following regular expression is derived from the ABNF for
5698 * language tags in RFC 2045 and 4288. All I can say is that this is
5699 * the easiest way to check the values...
5702 if ((i
= regcomp(&re
,
5704 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
5706 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
5707 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
5708 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
5711 REG_NOSUB
| REG_EXTENDED
)) != 0)
5713 char temp
[256]; /* Temporary error string */
5715 regerror(i
, &re
, temp
, sizeof(temp
));
5716 print_fatal_error(outfile
, "Unable to compile mimeMediaType regular "
5717 "expression: %s.", temp
);
5721 for (i
= 0; i
< attr
->num_values
; i
++)
5723 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5728 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5729 "characters (RFC 2911 section 4.1.9).",
5730 attr
->name
, attr
->values
[i
].string
.text
);
5733 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_MIMETYPE
- 1))
5738 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5739 "length %d (RFC 2911 section 4.1.9).",
5740 attr
->name
, attr
->values
[i
].string
.text
,
5741 (int)strlen(attr
->values
[i
].string
.text
));
5757 * 'with_flags_string()' - Return the "WITH-xxx" predicate that corresponds to
5761 static const char * /* O - WITH-xxx string */
5762 with_flags_string(int flags
) /* I - WITH flags */
5764 if (flags
& _CUPS_WITH_ALL
)
5766 if (flags
& _CUPS_WITH_HOSTNAME
)
5767 return ("WITH-ALL-HOSTNAMES");
5768 else if (flags
& _CUPS_WITH_RESOURCE
)
5769 return ("WITH-ALL-RESOURCES");
5770 else if (flags
& _CUPS_WITH_SCHEME
)
5771 return ("WITH-ALL-SCHEMES");
5773 return ("WITH-ALL-VALUES");
5775 else if (flags
& _CUPS_WITH_HOSTNAME
)
5776 return ("WITH-HOSTNAME");
5777 else if (flags
& _CUPS_WITH_RESOURCE
)
5778 return ("WITH-RESOURCE");
5779 else if (flags
& _CUPS_WITH_SCHEME
)
5780 return ("WITH-SCHEME");
5782 return ("WITH-VALUE");
5787 * 'with_value()' - Test a WITH-VALUE predicate.
5790 static int /* O - 1 on match, 0 on non-match */
5791 with_value(cups_file_t
*outfile
, /* I - Output file */
5792 cups_array_t
*errors
, /* I - Errors array */
5793 char *value
, /* I - Value string */
5794 int flags
, /* I - Flags for match */
5795 ipp_attribute_t
*attr
, /* I - Attribute to compare */
5796 char *matchbuf
, /* I - Buffer to hold matching value */
5797 size_t matchlen
) /* I - Length of match buffer */
5799 int i
, /* Looping var */
5801 char temp
[1024], /* Temporary value string */
5802 *valptr
; /* Pointer into value */
5806 match
= (flags
& _CUPS_WITH_ALL
) ? 1 : 0;
5809 * NULL matches everything.
5812 if (!value
|| !*value
)
5816 * Compare the value string to the attribute value.
5819 switch (attr
->value_tag
)
5821 case IPP_TAG_INTEGER
:
5823 for (i
= 0; i
< attr
->num_values
; i
++)
5825 char op
, /* Comparison operator */
5826 *nextptr
; /* Next pointer */
5827 int intvalue
, /* Integer value */
5828 valmatch
= 0; /* Does the current value match? */
5832 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5833 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5834 *valptr
== '=' || *valptr
== '>')
5837 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5839 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5847 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
5848 if (nextptr
== valptr
)
5852 if ((op
== '=' && attr
->values
[i
].integer
== intvalue
) ||
5853 (op
== '<' && attr
->values
[i
].integer
< intvalue
) ||
5854 (op
== '>' && attr
->values
[i
].integer
> intvalue
))
5857 snprintf(matchbuf
, matchlen
, "%d", attr
->values
[i
].integer
);
5864 if (flags
& _CUPS_WITH_ALL
)
5879 if (!match
&& errors
)
5881 for (i
= 0; i
< attr
->num_values
; i
++)
5882 add_stringf(errors
, "GOT: %s=%d", attr
->name
,
5883 attr
->values
[i
].integer
);
5887 case IPP_TAG_RANGE
:
5888 for (i
= 0; i
< attr
->num_values
; i
++)
5890 char op
, /* Comparison operator */
5891 *nextptr
; /* Next pointer */
5892 int intvalue
, /* Integer value */
5893 valmatch
= 0; /* Does the current value match? */
5897 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5898 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5899 *valptr
== '=' || *valptr
== '>')
5902 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5904 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5912 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
5913 if (nextptr
== valptr
)
5917 if ((op
== '=' && (attr
->values
[i
].range
.lower
== intvalue
||
5918 attr
->values
[i
].range
.upper
== intvalue
)) ||
5919 (op
== '<' && attr
->values
[i
].range
.upper
< intvalue
) ||
5920 (op
== '>' && attr
->values
[i
].range
.upper
> intvalue
))
5923 snprintf(matchbuf
, matchlen
, "%d-%d",
5924 attr
->values
[0].range
.lower
,
5925 attr
->values
[0].range
.upper
);
5932 if (flags
& _CUPS_WITH_ALL
)
5947 if (!match
&& errors
)
5949 for (i
= 0; i
< attr
->num_values
; i
++)
5950 add_stringf(errors
, "GOT: %s=%d-%d", attr
->name
,
5951 attr
->values
[i
].range
.lower
,
5952 attr
->values
[i
].range
.upper
);
5956 case IPP_TAG_BOOLEAN
:
5957 for (i
= 0; i
< attr
->num_values
; i
++)
5959 if ((!strcmp(value
, "true")) == attr
->values
[i
].boolean
)
5962 strlcpy(matchbuf
, value
, matchlen
);
5964 if (!(flags
& _CUPS_WITH_ALL
))
5970 else if (flags
& _CUPS_WITH_ALL
)
5977 if (!match
&& errors
)
5979 for (i
= 0; i
< attr
->num_values
; i
++)
5980 add_stringf(errors
, "GOT: %s=%s", attr
->name
,
5981 attr
->values
[i
].boolean
? "true" : "false");
5985 case IPP_TAG_RESOLUTION
:
5986 for (i
= 0; i
< attr
->num_values
; i
++)
5988 if (attr
->values
[i
].resolution
.xres
==
5989 attr
->values
[i
].resolution
.yres
)
5990 snprintf(temp
, sizeof(temp
), "%d%s",
5991 attr
->values
[i
].resolution
.xres
,
5992 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5995 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5996 attr
->values
[i
].resolution
.xres
,
5997 attr
->values
[i
].resolution
.yres
,
5998 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
6001 if (!strcmp(value
, temp
))
6004 strlcpy(matchbuf
, value
, matchlen
);
6006 if (!(flags
& _CUPS_WITH_ALL
))
6012 else if (flags
& _CUPS_WITH_ALL
)
6019 if (!match
&& errors
)
6021 for (i
= 0; i
< attr
->num_values
; i
++)
6023 if (attr
->values
[i
].resolution
.xres
==
6024 attr
->values
[i
].resolution
.yres
)
6025 snprintf(temp
, sizeof(temp
), "%d%s",
6026 attr
->values
[i
].resolution
.xres
,
6027 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
6030 snprintf(temp
, sizeof(temp
), "%dx%d%s",
6031 attr
->values
[i
].resolution
.xres
,
6032 attr
->values
[i
].resolution
.yres
,
6033 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
6036 if (strcmp(value
, temp
))
6037 add_stringf(errors
, "GOT: %s=%s", attr
->name
, temp
);
6042 case IPP_TAG_NOVALUE
:
6043 case IPP_TAG_UNKNOWN
:
6046 case IPP_TAG_CHARSET
:
6047 case IPP_TAG_KEYWORD
:
6048 case IPP_TAG_LANGUAGE
:
6049 case IPP_TAG_MIMETYPE
:
6051 case IPP_TAG_NAMELANG
:
6053 case IPP_TAG_TEXTLANG
:
6055 case IPP_TAG_URISCHEME
:
6056 if (flags
& _CUPS_WITH_REGEX
)
6059 * Value is an extended, case-sensitive POSIX regular expression...
6062 regex_t re
; /* Regular expression */
6064 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
6066 regerror(i
, &re
, temp
, sizeof(temp
));
6068 print_fatal_error(outfile
, "Unable to compile WITH-VALUE regular expression "
6069 "\"%s\" - %s", value
, temp
);
6074 * See if ALL of the values match the given regular expression.
6077 for (i
= 0; i
< attr
->num_values
; i
++)
6079 if (!regexec(&re
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
6084 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
6087 if (!(flags
& _CUPS_WITH_ALL
))
6093 else if (flags
& _CUPS_WITH_ALL
)
6102 else if (ippGetValueTag(attr
) == IPP_TAG_URI
&& !(flags
& (_CUPS_WITH_SCHEME
| _CUPS_WITH_HOSTNAME
| _CUPS_WITH_RESOURCE
)))
6105 * Value is a literal URI string, see if the value(s) match...
6108 for (i
= 0; i
< attr
->num_values
; i
++)
6110 if (!compare_uris(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
))))
6114 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
6117 if (!(flags
& _CUPS_WITH_ALL
))
6123 else if (flags
& _CUPS_WITH_ALL
)
6133 * Value is a literal string, see if the value(s) match...
6136 for (i
= 0; i
< attr
->num_values
; i
++)
6140 switch (ippGetValueTag(attr
))
6144 * Some URI components are case-sensitive, some not...
6147 if (flags
& (_CUPS_WITH_SCHEME
| _CUPS_WITH_HOSTNAME
))
6148 result
= _cups_strcasecmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
6150 result
= strcmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
6153 case IPP_TAG_MIMETYPE
:
6155 case IPP_TAG_NAMELANG
:
6157 case IPP_TAG_TEXTLANG
:
6159 * mimeMediaType, nameWithoutLanguage, nameWithLanguage,
6160 * textWithoutLanguage, and textWithLanguage are defined to
6161 * be case-insensitive strings...
6164 result
= _cups_strcasecmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
6169 * Other string syntaxes are defined as lowercased so we use
6170 * case-sensitive comparisons to catch problems...
6173 result
= strcmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
6181 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
6184 if (!(flags
& _CUPS_WITH_ALL
))
6190 else if (flags
& _CUPS_WITH_ALL
)
6198 if (!match
&& errors
)
6200 for (i
= 0; i
< attr
->num_values
; i
++)
6201 add_stringf(errors
, "GOT: %s=\"%s\"", attr
->name
,
6202 attr
->values
[i
].string
.text
);
6215 * 'with_value_from()' - Test a WITH-VALUE-FROM predicate.
6218 static int /* O - 1 on match, 0 on non-match */
6220 cups_array_t
*errors
, /* I - Errors array */
6221 ipp_attribute_t
*fromattr
, /* I - "From" attribute */
6222 ipp_attribute_t
*attr
, /* I - Attribute to compare */
6223 char *matchbuf
, /* I - Buffer to hold matching value */
6224 size_t matchlen
) /* I - Length of match buffer */
6226 int i
, j
, /* Looping vars */
6227 count
= ippGetCount(attr
), /* Number of attribute values */
6228 match
= 1; /* Match? */
6234 * Compare the from value(s) to the attribute value(s)...
6237 switch (ippGetValueTag(attr
))
6239 case IPP_TAG_INTEGER
:
6240 if (ippGetValueTag(fromattr
) != IPP_TAG_INTEGER
&& ippGetValueTag(fromattr
) != IPP_TAG_RANGE
)
6241 goto wrong_value_tag
;
6243 for (i
= 0; i
< count
; i
++)
6245 int value
= ippGetInteger(attr
, i
);
6246 /* Current integer value */
6248 if (ippContainsInteger(fromattr
, value
))
6251 snprintf(matchbuf
, matchlen
, "%d", value
);
6255 add_stringf(errors
, "GOT: %s=%d", ippGetName(attr
), value
);
6262 if (ippGetValueTag(fromattr
) != IPP_TAG_ENUM
)
6263 goto wrong_value_tag
;
6265 for (i
= 0; i
< count
; i
++)
6267 int value
= ippGetInteger(attr
, i
);
6268 /* Current integer value */
6270 if (ippContainsInteger(fromattr
, value
))
6273 snprintf(matchbuf
, matchlen
, "%d", value
);
6277 add_stringf(errors
, "GOT: %s=%d", ippGetName(attr
), value
);
6283 case IPP_TAG_RESOLUTION
:
6284 if (ippGetValueTag(fromattr
) != IPP_TAG_RESOLUTION
)
6285 goto wrong_value_tag
;
6287 for (i
= 0; i
< count
; i
++)
6291 int fromcount
= ippGetCount(fromattr
);
6292 int fromxres
, fromyres
;
6293 ipp_res_t fromunits
;
6295 xres
= ippGetResolution(attr
, i
, &yres
, &units
);
6297 for (j
= 0; j
< fromcount
; j
++)
6299 fromxres
= ippGetResolution(fromattr
, j
, &fromyres
, &fromunits
);
6300 if (fromxres
== xres
&& fromyres
== yres
&& fromunits
== units
)
6309 snprintf(matchbuf
, matchlen
, "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6311 snprintf(matchbuf
, matchlen
, "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6317 add_stringf(errors
, "GOT: %s=%d%s", ippGetName(attr
), xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6319 add_stringf(errors
, "GOT: %s=%dx%d%s", ippGetName(attr
), xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6326 case IPP_TAG_NOVALUE
:
6327 case IPP_TAG_UNKNOWN
:
6330 case IPP_TAG_CHARSET
:
6331 case IPP_TAG_KEYWORD
:
6332 case IPP_TAG_LANGUAGE
:
6333 case IPP_TAG_MIMETYPE
:
6335 case IPP_TAG_NAMELANG
:
6337 case IPP_TAG_TEXTLANG
:
6338 case IPP_TAG_URISCHEME
:
6339 for (i
= 0; i
< count
; i
++)
6341 const char *value
= ippGetString(attr
, i
, NULL
);
6342 /* Current string value */
6344 if (ippContainsString(fromattr
, value
))
6347 strlcpy(matchbuf
, value
, matchlen
);
6351 add_stringf(errors
, "GOT: %s='%s'", ippGetName(attr
), value
);
6358 for (i
= 0; i
< count
; i
++)
6360 const char *value
= ippGetString(attr
, i
, NULL
);
6361 /* Current string value */
6362 int fromcount
= ippGetCount(fromattr
);
6364 for (j
= 0; j
< fromcount
; j
++)
6366 if (!compare_uris(value
, ippGetString(fromattr
, j
, NULL
)))
6369 strlcpy(matchbuf
, value
, matchlen
);
6376 add_stringf(errors
, "GOT: %s='%s'", ippGetName(attr
), value
);
6389 /* value tag mismatch between fromattr and attr */
6392 add_stringf(errors
, "GOT: %s OF-TYPE %s", ippGetName(attr
), ippTagString(ippGetValueTag(attr
)));