2 * ipptool command for CUPS.
4 * Copyright 2007-2016 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 Verbosity
= 0, /* Show all attributes? */
139 Version
= 11, /* Default IPP version */
140 XMLHeader
= 0, /* 1 if header is written */
141 TestCount
= 0, /* Number of tests run */
142 PassCount
= 0, /* Number of passing tests */
143 FailCount
= 0, /* Number of failing tests */
144 SkipCount
= 0; /* Number of skipped tests */
145 static char *Username
= NULL
, /* Username from URI */
146 *Password
= NULL
; /* Password from URI */
147 static int PasswordTries
= 0; /* Number of tries with password */
154 static void add_stringf(cups_array_t
*a
, const char *s
, ...) __attribute__ ((__format__ (__printf__
, 2, 3)));
155 static int compare_vars(_cups_var_t
*a
, _cups_var_t
*b
);
156 static int do_tests(FILE *outfile
, _cups_vars_t
*vars
, const char *testfile
);
157 static void expand_variables(_cups_vars_t
*vars
, char *dst
, const char *src
, size_t dstsize
) __attribute__((nonnull(1,2,3)));
158 static int expect_matches(_cups_expect_t
*expect
, ipp_tag_t value_tag
);
159 static ipp_t
*get_collection(FILE *outfile
, _cups_vars_t
*vars
, FILE *fp
, int *linenum
);
160 static char *get_filename(const char *testfile
, char *dst
, const char *src
, size_t dstsize
);
161 static const char *get_string(ipp_attribute_t
*attr
, int element
, int flags
, char *buffer
, size_t bufsize
);
162 static char *get_token(FILE *fp
, char *buf
, int buflen
, int *linenum
);
163 static char *get_variable(_cups_vars_t
*vars
, const char *name
);
164 static char *iso_date(ipp_uchar_t
*date
);
165 static const char *password_cb(const char *prompt
);
166 static void pause_message(const char *message
);
167 static void print_attr(FILE *outfile
, int format
, ipp_attribute_t
*attr
, ipp_tag_t
*group
);
168 static void print_csv(FILE *outfile
, ipp_attribute_t
*attr
, int num_displayed
, char **displayed
, size_t *widths
);
169 static void print_fatal_error(FILE *outfile
, const char *s
, ...) __attribute__ ((__format__ (__printf__
, 2, 3)));
170 static void print_line(FILE *outfile
, ipp_attribute_t
*attr
, int num_displayed
, char **displayed
, size_t *widths
);
171 static void print_xml_header(FILE *outfile
);
172 static void print_xml_string(FILE *outfile
, const char *element
, const char *s
);
173 static void print_xml_trailer(FILE *outfile
, int success
, const char *message
);
174 static void set_variable(FILE *outfile
, _cups_vars_t
*vars
, const char *name
, const char *value
);
176 static void sigterm_handler(int sig
);
178 static int timeout_cb(http_t
*http
, void *user_data
);
179 static void usage(void) __attribute__((noreturn
));
180 static int validate_attr(FILE *outfile
, cups_array_t
*errors
, ipp_attribute_t
*attr
);
181 static int with_value(FILE *outfile
, cups_array_t
*errors
, char *value
, int flags
, ipp_attribute_t
*attr
, char *matchbuf
, size_t matchlen
);
182 static int with_value_from(cups_array_t
*errors
, ipp_attribute_t
*fromattr
, ipp_attribute_t
*attr
, char *matchbuf
, size_t matchlen
);
186 * 'main()' - Parse options and do tests.
189 int /* O - Exit status */
190 main(int argc
, /* I - Number of command-line args */
191 char *argv
[]) /* I - Command-line arguments */
193 int i
; /* Looping var */
194 int status
; /* Status of tests... */
195 FILE *outfile
= stdout
;
197 char *opt
, /* Current option */
198 name
[1024], /* Name/value buffer */
199 *value
, /* Pointer to value */
200 filename
[1024], /* Real filename */
201 testname
[1024], /* Real test filename */
202 uri
[1024]; /* Copy of printer URI */
203 const char *ext
, /* Extension on filename */
204 *testfile
; /* Test file to use */
205 int interval
, /* Test interval in microseconds */
206 repeat
; /* Repeat count */
207 _cups_vars_t vars
; /* Variables */
208 http_uri_status_t uri_status
; /* URI separation status */
209 _cups_globals_t
*cg
= _cupsGlobals();
215 * Catch SIGINT and SIGTERM...
218 signal(SIGINT
, sigterm_handler
);
219 signal(SIGTERM
, sigterm_handler
);
223 * Initialize the locale and variables...
226 _cupsSetLocale(argv
);
228 memset(&vars
, 0, sizeof(vars
));
229 vars
.family
= AF_UNSPEC
;
230 vars
.vars
= cupsArrayNew((cups_array_func_t
)compare_vars
, NULL
);
235 * ipptool URI testfile
243 for (i
= 1; i
< argc
; i
++)
245 if (!strcmp(argv
[i
], "--help"))
249 else if (!strcmp(argv
[i
], "--stop-after-include-error"))
251 StopAfterIncludeError
= 1;
253 else if (!strcmp(argv
[i
], "--version"))
258 else if (argv
[i
][0] == '-')
260 for (opt
= argv
[i
] + 1; *opt
; opt
++)
264 case '4' : /* Connect using IPv4 only */
265 vars
.family
= AF_INET
;
269 case '6' : /* Connect using IPv6 only */
270 vars
.family
= AF_INET6
;
272 #endif /* AF_INET6 */
274 case 'C' : /* Enable HTTP chunking */
275 Transfer
= _CUPS_TRANSFER_CHUNKED
;
278 case 'E' : /* Encrypt with TLS */
280 vars
.encryption
= HTTP_ENCRYPT_REQUIRED
;
282 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
284 #endif /* HAVE_SSL */
287 case 'I' : /* Ignore errors */
291 case 'L' : /* Disable HTTP chunking */
292 Transfer
= _CUPS_TRANSFER_LENGTH
;
295 case 'P' : /* Output to plist file */
300 _cupsLangPrintf(stderr
, _("%s: Missing filename for \"-P\"."), "ipptool");
304 if (outfile
!= stdout
)
307 if ((outfile
= fopen(argv
[i
], "w")) == NULL
)
309 _cupsLangPrintf(stderr
, _("%s: Unable to open \"%s\": %s"), "ipptool", argv
[i
], strerror(errno
));
313 Output
= _CUPS_OUTPUT_PLIST
;
315 if (interval
|| repeat
)
317 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
322 case 'S' : /* Encrypt with SSL */
324 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
326 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
328 #endif /* HAVE_SSL */
331 case 'T' : /* Set timeout */
336 _cupsLangPrintf(stderr
,
337 _("%s: Missing timeout for \"-T\"."),
342 vars
.timeout
= _cupsStrScand(argv
[i
], NULL
, localeconv());
345 case 'V' : /* Set IPP version */
350 _cupsLangPrintf(stderr
,
351 _("%s: Missing version for \"-V\"."),
356 if (!strcmp(argv
[i
], "1.0"))
358 else if (!strcmp(argv
[i
], "1.1"))
360 else if (!strcmp(argv
[i
], "2.0"))
362 else if (!strcmp(argv
[i
], "2.1"))
364 else if (!strcmp(argv
[i
], "2.2"))
368 _cupsLangPrintf(stderr
,
369 _("%s: Bad version %s for \"-V\"."),
375 case 'X' : /* Produce XML output */
376 Output
= _CUPS_OUTPUT_PLIST
;
378 if (interval
|| repeat
)
380 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
385 case 'c' : /* CSV output */
386 Output
= _CUPS_OUTPUT_CSV
;
389 case 'd' : /* Define a variable */
394 _cupsLangPuts(stderr
,
395 _("ipptool: Missing name=value for \"-d\"."));
399 strlcpy(name
, argv
[i
], sizeof(name
));
400 if ((value
= strchr(name
, '=')) != NULL
)
403 value
= name
+ strlen(name
);
405 set_variable(outfile
, &vars
, name
, value
);
408 case 'f' : /* Set the default test filename */
413 _cupsLangPuts(stderr
,
414 _("ipptool: Missing filename for \"-f\"."));
421 vars
.filename
= NULL
;
424 if (access(argv
[i
], 0))
430 snprintf(filename
, sizeof(filename
), "%s.gz", argv
[i
]);
431 if (access(filename
, 0) && filename
[0] != '/'
433 && (!isalpha(filename
[0] & 255) || filename
[1] != ':')
437 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s",
438 cg
->cups_datadir
, argv
[i
]);
439 if (access(filename
, 0))
441 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s.gz",
442 cg
->cups_datadir
, argv
[i
]);
443 if (access(filename
, 0))
444 vars
.filename
= strdup(argv
[i
]);
446 vars
.filename
= strdup(filename
);
449 vars
.filename
= strdup(filename
);
452 vars
.filename
= strdup(filename
);
455 vars
.filename
= strdup(argv
[i
]);
457 if ((ext
= strrchr(vars
.filename
, '.')) != NULL
)
460 * Guess the MIME media type based on the extension...
463 if (!_cups_strcasecmp(ext
, ".gif"))
464 set_variable(outfile
, &vars
, "filetype", "image/gif");
465 else if (!_cups_strcasecmp(ext
, ".htm") ||
466 !_cups_strcasecmp(ext
, ".htm.gz") ||
467 !_cups_strcasecmp(ext
, ".html") ||
468 !_cups_strcasecmp(ext
, ".html.gz"))
469 set_variable(outfile
, &vars
, "filetype", "text/html");
470 else if (!_cups_strcasecmp(ext
, ".jpg") ||
471 !_cups_strcasecmp(ext
, ".jpeg"))
472 set_variable(outfile
, &vars
, "filetype", "image/jpeg");
473 else if (!_cups_strcasecmp(ext
, ".pcl") ||
474 !_cups_strcasecmp(ext
, ".pcl.gz"))
475 set_variable(outfile
, &vars
, "filetype", "application/vnd.hp-PCL");
476 else if (!_cups_strcasecmp(ext
, ".pdf"))
477 set_variable(outfile
, &vars
, "filetype", "application/pdf");
478 else if (!_cups_strcasecmp(ext
, ".png"))
479 set_variable(outfile
, &vars
, "filetype", "image/png");
480 else if (!_cups_strcasecmp(ext
, ".ps") ||
481 !_cups_strcasecmp(ext
, ".ps.gz"))
482 set_variable(outfile
, &vars
, "filetype", "application/postscript");
483 else if (!_cups_strcasecmp(ext
, ".pwg") ||
484 !_cups_strcasecmp(ext
, ".pwg.gz") ||
485 !_cups_strcasecmp(ext
, ".ras") ||
486 !_cups_strcasecmp(ext
, ".ras.gz"))
487 set_variable(outfile
, &vars
, "filetype", "image/pwg-raster");
488 else if (!_cups_strcasecmp(ext
, ".tif") ||
489 !_cups_strcasecmp(ext
, ".tiff"))
490 set_variable(outfile
, &vars
, "filetype", "image/tiff");
491 else if (!_cups_strcasecmp(ext
, ".txt") ||
492 !_cups_strcasecmp(ext
, ".txt.gz"))
493 set_variable(outfile
, &vars
, "filetype", "text/plain");
494 else if (!_cups_strcasecmp(ext
, ".urf") ||
495 !_cups_strcasecmp(ext
, ".urf.gz"))
496 set_variable(outfile
, &vars
, "filetype", "image/urf");
497 else if (!_cups_strcasecmp(ext
, ".xps"))
498 set_variable(outfile
, &vars
, "filetype", "application/openxps");
500 set_variable(outfile
, &vars
, "filetype", "application/octet-stream");
505 * Use the "auto-type" MIME media type...
508 set_variable(outfile
, &vars
, "filetype", "application/octet-stream");
512 case 'i' : /* Test every N seconds */
517 _cupsLangPuts(stderr
,
518 _("ipptool: Missing seconds for \"-i\"."));
523 interval
= (int)(_cupsStrScand(argv
[i
], NULL
, localeconv()) *
527 _cupsLangPuts(stderr
,
528 _("ipptool: Invalid seconds for \"-i\"."));
533 if (Output
== _CUPS_OUTPUT_PLIST
&& interval
)
535 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
540 case 'l' : /* List as a table */
541 Output
= _CUPS_OUTPUT_LIST
;
544 case 'n' : /* Repeat count */
549 _cupsLangPuts(stderr
,
550 _("ipptool: Missing count for \"-n\"."));
554 repeat
= atoi(argv
[i
]);
556 if (Output
== _CUPS_OUTPUT_PLIST
&& repeat
)
558 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
563 case 'q' : /* Be quiet */
564 Output
= _CUPS_OUTPUT_QUIET
;
567 case 't' : /* CUPS test output */
568 Output
= _CUPS_OUTPUT_TEST
;
571 case 'v' : /* Be verbose */
576 _cupsLangPrintf(stderr
, _("ipptool: Unknown option \"-%c\"."),
582 else if (!strncmp(argv
[i
], "ipp://", 6) || !strncmp(argv
[i
], "http://", 7)
584 || !strncmp(argv
[i
], "ipps://", 7)
585 || !strncmp(argv
[i
], "https://", 8)
586 #endif /* HAVE_SSL */
595 _cupsLangPuts(stderr
, _("ipptool: May only specify a single URI."));
600 if (!strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8))
601 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
602 #endif /* HAVE_SSL */
605 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, vars
.uri
,
606 vars
.scheme
, sizeof(vars
.scheme
),
607 vars
.userpass
, sizeof(vars
.userpass
),
608 vars
.hostname
, sizeof(vars
.hostname
),
610 vars
.resource
, sizeof(vars
.resource
));
612 if (uri_status
!= HTTP_URI_OK
)
614 _cupsLangPrintf(stderr
, _("ipptool: Bad URI - %s."), httpURIStatusString(uri_status
));
618 if (vars
.userpass
[0])
620 if ((Password
= strchr(vars
.userpass
, ':')) != NULL
)
623 Username
= vars
.userpass
;
624 cupsSetPasswordCB(password_cb
);
625 set_variable(outfile
, &vars
, "uriuser", vars
.userpass
);
628 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), vars
.scheme
, NULL
,
629 vars
.hostname
, vars
.port
, vars
.resource
);
640 _cupsLangPuts(stderr
, _("ipptool: URI required before test file."));
641 _cupsLangPuts(stderr
, argv
[i
]);
645 if (access(argv
[i
], 0) && argv
[i
][0] != '/'
647 && (!isalpha(argv
[i
][0] & 255) || argv
[i
][1] != ':')
651 snprintf(testname
, sizeof(testname
), "%s/ipptool/%s", cg
->cups_datadir
,
653 if (access(testname
, 0))
661 if (!do_tests(outfile
, &vars
, testfile
))
666 if (!vars
.uri
|| !testfile
)
670 * Loop if the interval is set...
673 if (Output
== _CUPS_OUTPUT_PLIST
)
674 print_xml_trailer(outfile
, !status
, NULL
);
675 else if (interval
> 0 && repeat
> 0)
679 usleep((useconds_t
)interval
);
680 do_tests(outfile
, &vars
, testfile
);
684 else if (interval
> 0)
688 usleep((useconds_t
)interval
);
689 do_tests(outfile
, &vars
, testfile
);
693 if ((Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
)) && TestCount
> 1)
696 * Show a summary report if there were multiple tests...
699 printf("\nSummary: %d tests, %d passed, %d failed, %d skipped\n"
700 "Score: %d%%\n", TestCount
, PassCount
, FailCount
, SkipCount
,
701 100 * (PassCount
+ SkipCount
) / TestCount
);
713 * 'add_stringf()' - Add a formatted string to an array.
717 add_stringf(cups_array_t
*a
, /* I - Array */
718 const char *s
, /* I - Printf-style format string */
719 ...) /* I - Additional args as needed */
721 char buffer
[10240]; /* Format buffer */
722 va_list ap
; /* Argument pointer */
726 * Don't bother is the array is NULL...
733 * Format the message...
737 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
741 * Add it to the array...
744 cupsArrayAdd(a
, buffer
);
749 * 'compare_vars()' - Compare two variables.
752 static int /* O - Result of comparison */
753 compare_vars(_cups_var_t
*a
, /* I - First variable */
754 _cups_var_t
*b
) /* I - Second variable */
756 return (_cups_strcasecmp(a
->name
, b
->name
));
761 * 'do_tests()' - Do tests as specified in the test file.
764 static int /* 1 = success, 0 = failure */
765 do_tests(FILE *outfile
, /* I - Output file */
766 _cups_vars_t
*vars
, /* I - Variables */
767 const char *testfile
) /* I - Test file to use */
769 int i
, /* Looping var */
770 linenum
, /* Current line number */
771 pass
, /* Did we pass the test? */
772 prev_pass
= 1, /* Did we pass the previous test? */
773 request_id
, /* Current request ID */
774 show_header
= 1, /* Show the test header? */
775 ignore_errors
, /* Ignore test failures? */
776 skip_previous
= 0, /* Skip on previous test failure? */
777 repeat_count
, /* Repeat count */
778 repeat_test
; /* Repeat a test? */
779 useconds_t delay
, /* Initial delay */
780 repeat_interval
; /* Repeat interval (delay) */
781 http_t
*http
= NULL
; /* HTTP connection to server */
782 FILE *fp
= NULL
; /* Test file */
783 char resource
[512], /* Resource for request */
784 token
[1024], /* Token from file */
785 *tokenptr
, /* Pointer into token */
786 temp
[1024], /* Temporary string */
787 buffer
[131072], /* Copy buffer */
788 compression
[16]; /* COMPRESSION value */
789 ipp_t
*request
= NULL
, /* IPP request */
790 *response
= NULL
; /* IPP response */
791 size_t length
; /* Length of IPP request */
792 http_status_t status
; /* HTTP status */
793 cups_file_t
*reqfile
; /* File to send */
794 ssize_t bytes
; /* Bytes read/written */
795 char attr
[128]; /* Attribute name */
796 ipp_op_t op
; /* Operation */
797 ipp_tag_t group
; /* Current group */
798 ipp_tag_t value
; /* Current value type */
799 ipp_attribute_t
*attrptr
, /* Attribute pointer */
800 *found
, /* Found attribute */
801 *lastcol
= NULL
; /* Last collection attribute */
802 char name
[1024], /* Name of test */
803 file_id
[1024], /* File identifier */
804 test_id
[1024]; /* Test identifier */
805 char filename
[1024]; /* Filename */
806 _cups_transfer_t transfer
; /* To chunk or not to chunk */
807 int version
, /* IPP version number to use */
808 skip_test
; /* Skip this test? */
809 int num_statuses
= 0; /* Number of valid status codes */
810 _cups_status_t statuses
[100], /* Valid status codes */
811 *last_status
; /* Last STATUS (for predicates) */
812 int num_expects
= 0; /* Number of expected attributes */
813 _cups_expect_t expects
[200], /* Expected attributes */
814 *expect
, /* Current expected attribute */
815 *last_expect
; /* Last EXPECT (for predicates) */
816 int num_displayed
= 0; /* Number of displayed attributes */
817 char *displayed
[200]; /* Displayed attributes */
818 size_t widths
[200]; /* Width of columns */
819 cups_array_t
*a
, /* Duplicate attribute array */
820 *errors
= NULL
; /* Errors array */
821 const char *error
; /* Current error */
825 * Open the test file...
828 if ((fp
= fopen(testfile
, "r")) == NULL
)
830 print_fatal_error(outfile
, "Unable to open test file %s - %s", testfile
,
837 * Connect to the server...
840 if ((http
= httpConnect2(vars
->hostname
, vars
->port
, NULL
, vars
->family
,
841 vars
->encryption
, 1, 30000, NULL
)) == NULL
)
843 print_fatal_error(outfile
, "Unable to connect to %s on port %d - %s", vars
->hostname
,
844 vars
->port
, cupsLastErrorString());
850 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
,
851 "deflate, gzip, identity");
853 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
, "identity");
854 #endif /* HAVE_LIBZ */
856 if (vars
->timeout
> 0.0)
857 httpSetTimeout(http
, vars
->timeout
, timeout_cb
, NULL
);
863 CUPS_SRAND((unsigned)time(NULL
));
865 errors
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
,
866 (cups_afree_func_t
)free
);
870 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
872 while (!Cancel
&& get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
875 * Expect an open brace...
878 if (!strcmp(token
, "DEFINE"))
884 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
885 get_token(fp
, temp
, sizeof(temp
), &linenum
))
887 expand_variables(vars
, token
, temp
, sizeof(token
));
888 set_variable(outfile
, vars
, attr
, token
);
892 print_fatal_error(outfile
, "Missing DEFINE name and/or value on line %d.",
900 else if (!strcmp(token
, "DEFINE-DEFAULT"))
903 * DEFINE-DEFAULT name value
906 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
907 get_token(fp
, temp
, sizeof(temp
), &linenum
))
909 expand_variables(vars
, token
, temp
, sizeof(token
));
910 if (!get_variable(vars
, attr
))
911 set_variable(outfile
, vars
, attr
, token
);
915 print_fatal_error(outfile
, "Missing DEFINE-DEFAULT name and/or value on line "
923 else if (!strcmp(token
, "FILE-ID"))
929 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
931 expand_variables(vars
, file_id
, temp
, sizeof(file_id
));
935 print_fatal_error(outfile
, "Missing FILE-ID value on line %d.", linenum
);
942 else if (!strcmp(token
, "IGNORE-ERRORS"))
949 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
950 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
952 IgnoreErrors
= !_cups_strcasecmp(temp
, "yes");
956 print_fatal_error(outfile
, "Missing IGNORE-ERRORS value on line %d.", linenum
);
963 else if (!strcmp(token
, "INCLUDE"))
970 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
973 * Map the filename to and then run the tests...
976 if (!do_tests(outfile
, vars
, get_filename(testfile
, filename
, temp
, sizeof(filename
))))
980 if (StopAfterIncludeError
)
986 print_fatal_error(outfile
, "Missing INCLUDE filename on line %d.", linenum
);
994 else if (!strcmp(token
, "INCLUDE-IF-DEFINED"))
997 * INCLUDE-IF-DEFINED name "filename"
998 * INCLUDE-IF-DEFINED name <filename>
1001 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1002 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1005 * Map the filename to and then run the tests...
1008 if (get_variable(vars
, attr
) &&
1009 !do_tests(outfile
, vars
, get_filename(testfile
, filename
, temp
, sizeof(filename
))))
1013 if (StopAfterIncludeError
)
1019 print_fatal_error(outfile
, "Missing INCLUDE-IF-DEFINED name or filename on line "
1028 else if (!strcmp(token
, "INCLUDE-IF-NOT-DEFINED"))
1031 * INCLUDE-IF-NOT-DEFINED name "filename"
1032 * INCLUDE-IF-NOT-DEFINED name <filename>
1035 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1036 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1039 * Map the filename to and then run the tests...
1042 if (!get_variable(vars
, attr
) &&
1043 !do_tests(outfile
, vars
, get_filename(testfile
, filename
, temp
, sizeof(filename
))))
1047 if (StopAfterIncludeError
)
1053 print_fatal_error(outfile
, "Missing INCLUDE-IF-NOT-DEFINED name or filename on "
1054 "line %d.", linenum
);
1062 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1065 * SKIP-IF-DEFINED variable
1068 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1070 if (get_variable(vars
, temp
))
1075 print_fatal_error(outfile
, "Missing SKIP-IF-DEFINED variable on line %d.",
1081 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1084 * SKIP-IF-NOT-DEFINED variable
1087 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1089 if (!get_variable(vars
, temp
))
1094 print_fatal_error(outfile
, "Missing SKIP-IF-NOT-DEFINED variable on line %d.",
1100 else if (!strcmp(token
, "STOP-AFTER-INCLUDE-ERROR"))
1103 * STOP-AFTER-INCLUDE-ERROR yes
1104 * STOP-AFTER-INCLUDE-ERROR no
1107 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1108 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1110 StopAfterIncludeError
= !_cups_strcasecmp(temp
, "yes");
1114 print_fatal_error(outfile
, "Missing STOP-AFTER-INCLUDE-ERROR value on line %d.",
1122 else if (!strcmp(token
, "TRANSFER"))
1130 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1132 if (!strcmp(temp
, "auto"))
1133 Transfer
= _CUPS_TRANSFER_AUTO
;
1134 else if (!strcmp(temp
, "chunked"))
1135 Transfer
= _CUPS_TRANSFER_CHUNKED
;
1136 else if (!strcmp(temp
, "length"))
1137 Transfer
= _CUPS_TRANSFER_LENGTH
;
1140 print_fatal_error(outfile
, "Bad TRANSFER value \"%s\" on line %d.", temp
,
1148 print_fatal_error(outfile
, "Missing TRANSFER value on line %d.", linenum
);
1155 else if (!strcmp(token
, "VERSION"))
1157 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1159 if (!strcmp(temp
, "1.0"))
1161 else if (!strcmp(temp
, "1.1"))
1163 else if (!strcmp(temp
, "2.0"))
1165 else if (!strcmp(temp
, "2.1"))
1167 else if (!strcmp(temp
, "2.2"))
1171 print_fatal_error(outfile
, "Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1178 print_fatal_error(outfile
, "Missing VERSION number on line %d.", linenum
);
1185 else if (strcmp(token
, "{"))
1187 print_fatal_error(outfile
, "Unexpected token %s seen on line %d.", token
, linenum
);
1193 * Initialize things...
1198 if (Output
== _CUPS_OUTPUT_PLIST
)
1199 print_xml_header(outfile
);
1200 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
1201 printf("\"%s\":\n", testfile
);
1206 strlcpy(resource
, vars
->resource
, sizeof(resource
));
1211 group
= IPP_TAG_ZERO
;
1212 ignore_errors
= IgnoreErrors
;
1220 transfer
= Transfer
;
1221 compression
[0] = '\0';
1224 repeat_interval
= 5000000;
1226 strlcpy(name
, testfile
, sizeof(name
));
1227 if (strrchr(name
, '.') != NULL
)
1228 *strrchr(name
, '.') = '\0';
1231 * Parse until we see a close brace...
1234 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
1236 if (_cups_strcasecmp(token
, "COUNT") &&
1237 _cups_strcasecmp(token
, "DEFINE-MATCH") &&
1238 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1239 _cups_strcasecmp(token
, "DEFINE-VALUE") &&
1240 _cups_strcasecmp(token
, "IF-DEFINED") &&
1241 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1242 _cups_strcasecmp(token
, "IN-GROUP") &&
1243 _cups_strcasecmp(token
, "OF-TYPE") &&
1244 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1245 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1246 _cups_strcasecmp(token
, "REPEAT-NO-MATCH") &&
1247 _cups_strcasecmp(token
, "SAME-COUNT-AS") &&
1248 _cups_strcasecmp(token
, "WITH-ALL-VALUES") &&
1249 _cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") &&
1250 _cups_strcasecmp(token
, "WITH-ALL-RESOURCES") &&
1251 _cups_strcasecmp(token
, "WITH-ALL-SCHEMES") &&
1252 _cups_strcasecmp(token
, "WITH-HOSTNAME") &&
1253 _cups_strcasecmp(token
, "WITH-RESOURCE") &&
1254 _cups_strcasecmp(token
, "WITH-SCHEME") &&
1255 _cups_strcasecmp(token
, "WITH-VALUE") &&
1256 _cups_strcasecmp(token
, "WITH-VALUE-FROM"))
1259 if (_cups_strcasecmp(token
, "DEFINE-MATCH") &&
1260 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1261 _cups_strcasecmp(token
, "IF-DEFINED") &&
1262 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1263 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1264 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1265 _cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
1268 if (!strcmp(token
, "}"))
1270 else if (!strcmp(token
, "{") && lastcol
)
1273 * Another collection value
1276 ipp_t
*col
= get_collection(outfile
, vars
, fp
, &linenum
);
1277 /* Collection value */
1281 ippSetCollection(request
, &lastcol
, ippGetCount(lastcol
), col
);
1289 else if (!strcmp(token
, "COMPRESSION"))
1293 * COMPRESSION deflate
1297 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1299 expand_variables(vars
, compression
, temp
, sizeof(compression
));
1301 if (strcmp(compression
, "none") && strcmp(compression
, "deflate") &&
1302 strcmp(compression
, "gzip"))
1304 if (strcmp(compression
, "none"))
1305 #endif /* HAVE_LIBZ */
1307 print_fatal_error(outfile
, "Unsupported COMPRESSION value '%s' on line %d.",
1308 compression
, linenum
);
1313 if (!strcmp(compression
, "none"))
1314 compression
[0] = '\0';
1318 print_fatal_error(outfile
, "Missing COMPRESSION value on line %d.", linenum
);
1323 else if (!strcmp(token
, "DEFINE"))
1329 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1330 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1332 expand_variables(vars
, token
, temp
, sizeof(token
));
1333 set_variable(outfile
, vars
, attr
, token
);
1337 print_fatal_error(outfile
, "Missing DEFINE name and/or value on line %d.",
1343 else if (!strcmp(token
, "IGNORE-ERRORS"))
1350 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1351 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1353 ignore_errors
= !_cups_strcasecmp(temp
, "yes");
1357 print_fatal_error(outfile
, "Missing IGNORE-ERRORS value on line %d.", linenum
);
1364 else if (!_cups_strcasecmp(token
, "NAME"))
1370 get_token(fp
, temp
, sizeof(temp
), &linenum
);
1371 expand_variables(vars
, name
, temp
, sizeof(name
));
1373 else if (!_cups_strcasecmp(token
, "PAUSE"))
1376 * Pause with a message...
1379 get_token(fp
, token
, sizeof(token
), &linenum
);
1380 pause_message(token
);
1382 else if (!strcmp(token
, "REQUEST-ID"))
1389 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1391 if (isdigit(temp
[0] & 255))
1392 request_id
= atoi(temp
);
1393 else if (!_cups_strcasecmp(temp
, "random"))
1394 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
1397 print_fatal_error(outfile
, "Bad REQUEST-ID value \"%s\" on line %d.", temp
,
1405 print_fatal_error(outfile
, "Missing REQUEST-ID value on line %d.", linenum
);
1410 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1413 * SKIP-IF-DEFINED variable
1416 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1418 if (get_variable(vars
, temp
))
1423 print_fatal_error(outfile
, "Missing SKIP-IF-DEFINED value on line %d.",
1429 else if (!strcmp(token
, "SKIP-IF-MISSING"))
1432 * SKIP-IF-MISSING filename
1435 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1437 expand_variables(vars
, token
, temp
, sizeof(token
));
1438 get_filename(testfile
, filename
, token
, sizeof(filename
));
1440 if (access(filename
, R_OK
))
1445 print_fatal_error(outfile
, "Missing SKIP-IF-MISSING filename on line %d.",
1451 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1454 * SKIP-IF-NOT-DEFINED variable
1457 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1459 if (!get_variable(vars
, temp
))
1464 print_fatal_error(outfile
, "Missing SKIP-IF-NOT-DEFINED value on line %d.",
1470 else if (!strcmp(token
, "SKIP-PREVIOUS-ERROR"))
1473 * SKIP-PREVIOUS-ERROR yes
1474 * SKIP-PREVIOUS-ERROR no
1477 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1478 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1480 skip_previous
= !_cups_strcasecmp(temp
, "yes");
1484 print_fatal_error(outfile
, "Missing SKIP-PREVIOUS-ERROR value on line %d.", linenum
);
1491 else if (!strcmp(token
, "TEST-ID"))
1497 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1499 expand_variables(vars
, test_id
, temp
, sizeof(test_id
));
1503 print_fatal_error(outfile
, "Missing TEST-ID value on line %d.", linenum
);
1510 else if (!strcmp(token
, "TRANSFER"))
1518 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1520 if (!strcmp(temp
, "auto"))
1521 transfer
= _CUPS_TRANSFER_AUTO
;
1522 else if (!strcmp(temp
, "chunked"))
1523 transfer
= _CUPS_TRANSFER_CHUNKED
;
1524 else if (!strcmp(temp
, "length"))
1525 transfer
= _CUPS_TRANSFER_LENGTH
;
1528 print_fatal_error(outfile
, "Bad TRANSFER value \"%s\" on line %d.", temp
,
1536 print_fatal_error(outfile
, "Missing TRANSFER value on line %d.", linenum
);
1541 else if (!_cups_strcasecmp(token
, "VERSION"))
1543 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1545 if (!strcmp(temp
, "0.0"))
1547 else if (!strcmp(temp
, "1.0"))
1549 else if (!strcmp(temp
, "1.1"))
1551 else if (!strcmp(temp
, "2.0"))
1553 else if (!strcmp(temp
, "2.1"))
1555 else if (!strcmp(temp
, "2.2"))
1559 print_fatal_error(outfile
, "Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1566 print_fatal_error(outfile
, "Missing VERSION number on line %d.", linenum
);
1571 else if (!_cups_strcasecmp(token
, "RESOURCE"))
1577 if (!get_token(fp
, resource
, sizeof(resource
), &linenum
))
1579 print_fatal_error(outfile
, "Missing RESOURCE path on line %d.", linenum
);
1584 else if (!_cups_strcasecmp(token
, "OPERATION"))
1590 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1592 print_fatal_error(outfile
, "Missing OPERATION code on line %d.", linenum
);
1597 expand_variables(vars
, token
, temp
, sizeof(token
));
1599 if ((op
= ippOpValue(token
)) == (ipp_op_t
)-1 &&
1600 (op
= (ipp_op_t
)strtol(token
, NULL
, 0)) == 0)
1602 print_fatal_error(outfile
, "Bad OPERATION code \"%s\" on line %d.", token
,
1608 else if (!_cups_strcasecmp(token
, "GROUP"))
1611 * Attribute group...
1614 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1616 print_fatal_error(outfile
, "Missing GROUP tag on line %d.", linenum
);
1621 if ((value
= ippTagValue(token
)) < 0)
1623 print_fatal_error(outfile
, "Bad GROUP tag \"%s\" on line %d.", token
, linenum
);
1629 ippAddSeparator(request
);
1633 else if (!_cups_strcasecmp(token
, "DELAY"))
1636 * Delay before operation...
1639 double dval
; /* Delay value */
1641 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1643 print_fatal_error(outfile
, "Missing DELAY value on line %d.", linenum
);
1648 expand_variables(vars
, token
, temp
, sizeof(token
));
1650 if ((dval
= _cupsStrScand(token
, &tokenptr
, localeconv())) <= 0.0 || (*tokenptr
&& *tokenptr
!= ','))
1652 print_fatal_error(outfile
, "Bad DELAY value \"%s\" on line %d.", token
,
1658 delay
= (useconds_t
)(1000000.0 * dval
);
1660 if (*tokenptr
== ',')
1662 if ((dval
= _cupsStrScand(tokenptr
+ 1, &tokenptr
, localeconv())) <= 0.0 || *tokenptr
)
1664 print_fatal_error(outfile
, "Bad DELAY value \"%s\" on line %d.", token
,
1670 repeat_interval
= (useconds_t
)(1000000.0 * dval
);
1673 repeat_interval
= delay
;
1677 if (Output
== _CUPS_OUTPUT_TEST
)
1678 printf(" [%g second delay]\n", delay
* 0.000001);
1683 else if (!_cups_strcasecmp(token
, "ATTR"))
1689 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1691 print_fatal_error(outfile
, "Missing ATTR value tag on line %d.", linenum
);
1696 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
1698 print_fatal_error(outfile
, "Bad ATTR value tag \"%s\" on line %d.", token
,
1704 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
1706 print_fatal_error(outfile
, "Missing ATTR name on line %d.", linenum
);
1711 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1713 print_fatal_error(outfile
, "Missing ATTR value on line %d.", linenum
);
1718 expand_variables(vars
, token
, temp
, sizeof(token
));
1723 case IPP_TAG_BOOLEAN
:
1724 if (!_cups_strcasecmp(token
, "true"))
1725 attrptr
= ippAddBoolean(request
, group
, attr
, 1);
1727 attrptr
= ippAddBoolean(request
, group
, attr
, (char)atoi(token
));
1730 case IPP_TAG_INTEGER
:
1732 if (!strchr(token
, ','))
1734 if (isdigit(token
[0] & 255) || token
[0] == '-')
1735 attrptr
= ippAddInteger(request
, group
, value
, attr
, (int)strtol(token
, &tokenptr
, 0));
1739 if ((i
= ippEnumValue(attr
, tokenptr
)) >= 0)
1741 attrptr
= ippAddInteger(request
, group
, value
, attr
, i
);
1742 tokenptr
+= strlen(tokenptr
);
1748 int values
[100], /* Values */
1749 num_values
= 1; /* Number of values */
1751 if (!isdigit(token
[0] & 255) && token
[0] != '-' && value
== IPP_TAG_ENUM
)
1753 char *ptr
; /* Pointer to next terminator */
1755 if ((ptr
= strchr(token
, ',')) != NULL
)
1758 ptr
= token
+ strlen(token
);
1760 if ((i
= ippEnumValue(attr
, token
)) < 0)
1766 i
= (int)strtol(token
, &tokenptr
, 0);
1770 while (tokenptr
&& *tokenptr
&&
1771 num_values
< (int)(sizeof(values
) / sizeof(values
[0])))
1773 if (*tokenptr
== ',')
1776 if (!isdigit(*tokenptr
& 255) && *tokenptr
!= '-')
1778 char *ptr
; /* Pointer to next terminator */
1780 if (value
!= IPP_TAG_ENUM
)
1783 if ((ptr
= strchr(tokenptr
, ',')) != NULL
)
1786 ptr
= tokenptr
+ strlen(tokenptr
);
1788 if ((i
= ippEnumValue(attr
, tokenptr
)) < 0)
1794 i
= (int)strtol(tokenptr
, &tokenptr
, 0);
1796 values
[num_values
++] = i
;
1799 attrptr
= ippAddIntegers(request
, group
, value
, attr
, num_values
, values
);
1802 if ((!token
[0] || !tokenptr
|| *tokenptr
) && !skip_test
)
1804 print_fatal_error(outfile
, "Bad %s value \'%s\' for \"%s\" on line %d.",
1805 ippTagString(value
), token
, attr
, linenum
);
1811 case IPP_TAG_RESOLUTION
:
1813 int xres
, /* X resolution */
1814 yres
; /* Y resolution */
1815 char *ptr
; /* Pointer into value */
1817 xres
= yres
= (int)strtol(token
, (char **)&ptr
, 10);
1818 if (ptr
> token
&& xres
> 0)
1821 yres
= (int)strtol(ptr
+ 1, (char **)&ptr
, 10);
1824 if (ptr
<= token
|| xres
<= 0 || yres
<= 0 || !ptr
||
1825 (_cups_strcasecmp(ptr
, "dpi") &&
1826 _cups_strcasecmp(ptr
, "dpc") &&
1827 _cups_strcasecmp(ptr
, "dpcm") &&
1828 _cups_strcasecmp(ptr
, "other")))
1833 print_fatal_error(outfile
, "Bad resolution value \'%s\' for \"%s\" on line %d.", token
, attr
, linenum
);
1838 if (!_cups_strcasecmp(ptr
, "dpi"))
1839 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
1840 else if (!_cups_strcasecmp(ptr
, "dpc") ||
1841 !_cups_strcasecmp(ptr
, "dpcm"))
1842 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_CM
, xres
, yres
);
1844 attrptr
= ippAddResolution(request
, group
, attr
, (ipp_res_t
)0, xres
, yres
);
1848 case IPP_TAG_RANGE
:
1850 int lowers
[4], /* Lower value */
1851 uppers
[4], /* Upper values */
1852 num_vals
; /* Number of values */
1855 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
1856 lowers
+ 0, uppers
+ 0,
1857 lowers
+ 1, uppers
+ 1,
1858 lowers
+ 2, uppers
+ 2,
1859 lowers
+ 3, uppers
+ 3);
1861 if ((num_vals
& 1) || num_vals
== 0)
1866 print_fatal_error(outfile
, "Bad rangeOfInteger value \'%s\' for \"%s\" on line %d.", token
, attr
, linenum
);
1871 attrptr
= ippAddRanges(request
, group
, attr
, num_vals
/ 2, lowers
,
1876 case IPP_TAG_BEGIN_COLLECTION
:
1877 if (!strcmp(token
, "{"))
1879 ipp_t
*col
= get_collection(outfile
, vars
, fp
, &linenum
);
1880 /* Collection value */
1884 attrptr
= lastcol
= ippAddCollection(request
, group
, attr
, col
);
1897 print_fatal_error(outfile
, "Bad ATTR collection value for \"%s\" on line %d.", attr
, linenum
);
1904 ipp_t
*col
; /* Collection value */
1905 long savepos
= ftell(fp
); /* Save position of file */
1906 int savelinenum
= linenum
; /* Save line number */
1908 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1911 if (strcmp(token
, ","))
1913 fseek(fp
, savepos
, SEEK_SET
);
1914 linenum
= savelinenum
;
1918 if (!get_token(fp
, token
, sizeof(token
), &linenum
) || strcmp(token
, "{"))
1920 print_fatal_error(outfile
, "Unexpected \"%s\" on line %d.", token
, linenum
);
1926 if ((col
= get_collection(outfile
, vars
, fp
, &linenum
)) == NULL
)
1929 ippSetCollection(request
, &attrptr
, ippGetCount(attrptr
), col
);
1932 while (!strcmp(token
, "{"));
1935 case IPP_TAG_STRING
:
1936 attrptr
= ippAddOctetString(request
, group
, attr
, token
, (int)strlen(token
));
1940 print_fatal_error(outfile
, "Unsupported ATTR value tag %s for \"%s\" on line %d.", ippTagString(value
), attr
, linenum
);
1944 case IPP_TAG_TEXTLANG
:
1945 case IPP_TAG_NAMELANG
:
1948 case IPP_TAG_KEYWORD
:
1950 case IPP_TAG_URISCHEME
:
1951 case IPP_TAG_CHARSET
:
1952 case IPP_TAG_LANGUAGE
:
1953 case IPP_TAG_MIMETYPE
:
1954 if (!strchr(token
, ','))
1955 attrptr
= ippAddString(request
, group
, value
, attr
, NULL
, token
);
1959 * Multiple string values...
1962 int num_values
; /* Number of values */
1963 char *values
[100], /* Values */
1964 *ptr
; /* Pointer to next value */
1970 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
1972 if (ptr
> token
&& ptr
[-1] == '\\')
1973 _cups_strcpy(ptr
- 1, ptr
);
1977 values
[num_values
] = ptr
;
1982 attrptr
= ippAddStrings(request
, group
, value
, attr
, num_values
,
1983 NULL
, (const char **)values
);
1988 if (!attrptr
&& !skip_test
)
1990 print_fatal_error(outfile
, "Unable to add attribute \"%s\" on line %d.", attr
, linenum
);
1995 else if (!_cups_strcasecmp(token
, "FILE"))
2001 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
2003 print_fatal_error(outfile
, "Missing FILE filename on line %d.", linenum
);
2008 expand_variables(vars
, token
, temp
, sizeof(token
));
2009 get_filename(testfile
, filename
, token
, sizeof(filename
));
2011 if (access(filename
, R_OK
))
2013 print_fatal_error(outfile
, "Filename \"%s\" on line %d cannot be read.",
2015 print_fatal_error(outfile
, "Filename mapped to \"%s\".", filename
);
2020 else if (!_cups_strcasecmp(token
, "STATUS"))
2026 if (num_statuses
>= (int)(sizeof(statuses
) / sizeof(statuses
[0])))
2028 print_fatal_error(outfile
, "Too many STATUS's on line %d.", linenum
);
2033 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2035 print_fatal_error(outfile
, "Missing STATUS code on line %d.", linenum
);
2040 if ((statuses
[num_statuses
].status
= ippErrorValue(token
))
2041 == (ipp_status_t
)-1 &&
2042 (statuses
[num_statuses
].status
= (ipp_status_t
)strtol(token
, NULL
, 0)) == 0)
2044 print_fatal_error(outfile
, "Bad STATUS code \"%s\" on line %d.", token
,
2050 last_status
= statuses
+ num_statuses
;
2053 last_status
->define_match
= NULL
;
2054 last_status
->define_no_match
= NULL
;
2055 last_status
->if_defined
= NULL
;
2056 last_status
->if_not_defined
= NULL
;
2057 last_status
->repeat_limit
= 1000;
2058 last_status
->repeat_match
= 0;
2059 last_status
->repeat_no_match
= 0;
2061 else if (!_cups_strcasecmp(token
, "EXPECT") || !_cups_strcasecmp(token
, "EXPECT-ALL"))
2064 * Expected attributes...
2067 int expect_all
= !_cups_strcasecmp(token
, "EXPECT-ALL");
2069 if (num_expects
>= (int)(sizeof(expects
) / sizeof(expects
[0])))
2071 print_fatal_error(outfile
, "Too many EXPECT's on line %d.", linenum
);
2076 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2078 print_fatal_error(outfile
, "Missing EXPECT name on line %d.", linenum
);
2083 last_expect
= expects
+ num_expects
;
2086 memset(last_expect
, 0, sizeof(_cups_expect_t
));
2087 last_expect
->repeat_limit
= 1000;
2088 last_expect
->expect_all
= expect_all
;
2090 if (token
[0] == '!')
2092 last_expect
->not_expect
= 1;
2093 last_expect
->name
= strdup(token
+ 1);
2095 else if (token
[0] == '?')
2097 last_expect
->optional
= 1;
2098 last_expect
->name
= strdup(token
+ 1);
2101 last_expect
->name
= strdup(token
);
2103 else if (!_cups_strcasecmp(token
, "COUNT"))
2105 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2107 print_fatal_error(outfile
, "Missing COUNT number on line %d.", linenum
);
2112 if ((i
= atoi(token
)) <= 0)
2114 print_fatal_error(outfile
, "Bad COUNT \"%s\" on line %d.", token
, linenum
);
2120 last_expect
->count
= i
;
2123 print_fatal_error(outfile
, "COUNT without a preceding EXPECT on line %d.",
2129 else if (!_cups_strcasecmp(token
, "DEFINE-MATCH"))
2131 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2133 print_fatal_error(outfile
, "Missing DEFINE-MATCH variable on line %d.",
2140 last_expect
->define_match
= strdup(token
);
2141 else if (last_status
)
2142 last_status
->define_match
= strdup(token
);
2145 print_fatal_error(outfile
, "DEFINE-MATCH without a preceding EXPECT or STATUS "
2146 "on line %d.", linenum
);
2151 else if (!_cups_strcasecmp(token
, "DEFINE-NO-MATCH"))
2153 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2155 print_fatal_error(outfile
, "Missing DEFINE-NO-MATCH variable on line %d.",
2162 last_expect
->define_no_match
= strdup(token
);
2163 else if (last_status
)
2164 last_status
->define_no_match
= strdup(token
);
2167 print_fatal_error(outfile
, "DEFINE-NO-MATCH without a preceding EXPECT or "
2168 "STATUS on line %d.", linenum
);
2173 else if (!_cups_strcasecmp(token
, "DEFINE-VALUE"))
2175 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2177 print_fatal_error(outfile
, "Missing DEFINE-VALUE variable on line %d.",
2184 last_expect
->define_value
= strdup(token
);
2187 print_fatal_error(outfile
, "DEFINE-VALUE without a preceding EXPECT on "
2188 "line %d.", linenum
);
2193 else if (!_cups_strcasecmp(token
, "OF-TYPE"))
2195 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2197 print_fatal_error(outfile
, "Missing OF-TYPE value tag(s) on line %d.",
2204 last_expect
->of_type
= strdup(token
);
2207 print_fatal_error(outfile
, "OF-TYPE without a preceding EXPECT on line %d.",
2213 else if (!_cups_strcasecmp(token
, "IN-GROUP"))
2215 ipp_tag_t in_group
; /* IN-GROUP value */
2218 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2220 print_fatal_error(outfile
, "Missing IN-GROUP group tag on line %d.", linenum
);
2225 if ((in_group
= ippTagValue(token
)) == (ipp_tag_t
)-1)
2228 else if (last_expect
)
2229 last_expect
->in_group
= in_group
;
2232 print_fatal_error(outfile
, "IN-GROUP without a preceding EXPECT on line %d.",
2238 else if (!_cups_strcasecmp(token
, "REPEAT-LIMIT"))
2240 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2242 print_fatal_error(outfile
, "Missing REPEAT-LIMIT value on line %d.", linenum
);
2246 else if (atoi(token
) <= 0)
2248 print_fatal_error(outfile
, "Bad REPEAT-LIMIT value on line %d.", linenum
);
2254 last_status
->repeat_limit
= atoi(token
);
2255 else if (last_expect
)
2256 last_expect
->repeat_limit
= atoi(token
);
2259 print_fatal_error(outfile
, "REPEAT-LIMIT without a preceding EXPECT or STATUS "
2260 "on line %d.", linenum
);
2265 else if (!_cups_strcasecmp(token
, "REPEAT-MATCH"))
2268 last_status
->repeat_match
= 1;
2269 else if (last_expect
)
2270 last_expect
->repeat_match
= 1;
2273 print_fatal_error(outfile
, "REPEAT-MATCH without a preceding EXPECT or STATUS "
2274 "on line %d.", linenum
);
2279 else if (!_cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
2282 last_status
->repeat_no_match
= 1;
2283 else if (last_expect
)
2284 last_expect
->repeat_no_match
= 1;
2287 print_fatal_error(outfile
, "REPEAT-NO-MATCH without a preceding EXPECT or "
2288 "STATUS on ine %d.", linenum
);
2293 else if (!_cups_strcasecmp(token
, "SAME-COUNT-AS"))
2295 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2297 print_fatal_error(outfile
, "Missing SAME-COUNT-AS name on line %d.", linenum
);
2303 last_expect
->same_count_as
= strdup(token
);
2306 print_fatal_error(outfile
, "SAME-COUNT-AS without a preceding EXPECT on line "
2312 else if (!_cups_strcasecmp(token
, "IF-DEFINED"))
2314 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2316 print_fatal_error(outfile
, "Missing IF-DEFINED name on line %d.", linenum
);
2322 last_expect
->if_defined
= strdup(token
);
2323 else if (last_status
)
2324 last_status
->if_defined
= strdup(token
);
2327 print_fatal_error(outfile
, "IF-DEFINED without a preceding EXPECT or STATUS "
2328 "on line %d.", linenum
);
2333 else if (!_cups_strcasecmp(token
, "IF-NOT-DEFINED"))
2335 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2337 print_fatal_error(outfile
, "Missing IF-NOT-DEFINED name on line %d.", linenum
);
2343 last_expect
->if_not_defined
= strdup(token
);
2344 else if (last_status
)
2345 last_status
->if_not_defined
= strdup(token
);
2348 print_fatal_error(outfile
, "IF-NOT-DEFINED without a preceding EXPECT or STATUS "
2349 "on line %d.", linenum
);
2354 else if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") ||
2355 !_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
2356 !_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
2357 !_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
2358 !_cups_strcasecmp(token
, "WITH-HOSTNAME") ||
2359 !_cups_strcasecmp(token
, "WITH-RESOURCE") ||
2360 !_cups_strcasecmp(token
, "WITH-SCHEME") ||
2361 !_cups_strcasecmp(token
, "WITH-VALUE"))
2365 if (!_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
2366 !_cups_strcasecmp(token
, "WITH-HOSTNAME"))
2367 last_expect
->with_flags
= _CUPS_WITH_HOSTNAME
;
2368 else if (!_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
2369 !_cups_strcasecmp(token
, "WITH-RESOURCE"))
2370 last_expect
->with_flags
= _CUPS_WITH_RESOURCE
;
2371 else if (!_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
2372 !_cups_strcasecmp(token
, "WITH-SCHEME"))
2373 last_expect
->with_flags
= _CUPS_WITH_SCHEME
;
2375 if (!_cups_strncasecmp(token
, "WITH-ALL-", 9))
2376 last_expect
->with_flags
|= _CUPS_WITH_ALL
;
2379 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
2381 print_fatal_error(outfile
, "Missing %s value on line %d.", token
, linenum
);
2389 * Expand any variables in the value and then save it.
2392 expand_variables(vars
, token
, temp
, sizeof(token
));
2394 tokenptr
= token
+ strlen(token
) - 1;
2396 if (token
[0] == '/' && tokenptr
> token
&& *tokenptr
== '/')
2399 * WITH-VALUE is a POSIX extended regular expression.
2402 last_expect
->with_value
= calloc(1, (size_t)(tokenptr
- token
));
2403 last_expect
->with_flags
|= _CUPS_WITH_REGEX
;
2405 if (last_expect
->with_value
)
2406 memcpy(last_expect
->with_value
, token
+ 1, (size_t)(tokenptr
- token
- 1));
2411 * WITH-VALUE is a literal value...
2414 char *ptr
; /* Pointer into value */
2416 for (ptr
= token
; *ptr
; ptr
++)
2418 if (*ptr
== '\\' && ptr
[1])
2421 * Remove \ from \foo...
2424 _cups_strcpy(ptr
, ptr
+ 1);
2428 last_expect
->with_value
= strdup(token
);
2429 last_expect
->with_flags
|= _CUPS_WITH_LITERAL
;
2434 print_fatal_error(outfile
, "%s without a preceding EXPECT on line %d.", token
,
2440 else if (!_cups_strcasecmp(token
, "WITH-VALUE-FROM"))
2442 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
2444 print_fatal_error(outfile
, "Missing %s value on line %d.", token
, linenum
);
2452 * Expand any variables in the value and then save it.
2455 expand_variables(vars
, token
, temp
, sizeof(token
));
2457 last_expect
->with_value_from
= strdup(token
);
2458 last_expect
->with_flags
= _CUPS_WITH_LITERAL
;
2462 print_fatal_error(outfile
, "%s without a preceding EXPECT on line %d.", token
,
2468 else if (!_cups_strcasecmp(token
, "DISPLAY"))
2471 * Display attributes...
2474 if (num_displayed
>= (int)(sizeof(displayed
) / sizeof(displayed
[0])))
2476 print_fatal_error(outfile
, "Too many DISPLAY's on line %d", linenum
);
2481 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2483 print_fatal_error(outfile
, "Missing DISPLAY name on line %d.", linenum
);
2488 displayed
[num_displayed
] = strdup(token
);
2493 print_fatal_error(outfile
, "Unexpected token %s seen on line %d.", token
,
2501 * Submit the IPP request...
2506 ippSetVersion(request
, version
/ 10, version
% 10);
2507 ippSetOperation(request
, op
);
2508 ippSetRequestId(request
, request_id
);
2510 if (Output
== _CUPS_OUTPUT_PLIST
)
2512 fputs("<dict>\n", outfile
);
2513 fputs("<key>Name</key>\n", outfile
);
2514 print_xml_string(outfile
, "string", name
);
2517 fputs("<key>FileId</key>\n", outfile
);
2518 print_xml_string(outfile
, "string", file_id
);
2522 fputs("<key>TestId</key>\n", outfile
);
2523 print_xml_string(outfile
, "string", test_id
);
2525 fputs("<key>Version</key>\n", outfile
);
2526 fprintf(outfile
, "<string>%d.%d</string>\n", version
/ 10, version
% 10);
2527 fputs("<key>Operation</key>\n", outfile
);
2528 print_xml_string(outfile
, "string", ippOpString(op
));
2529 fputs("<key>RequestId</key>\n", outfile
);
2530 fprintf(outfile
, "<integer>%d</integer>\n", request_id
);
2531 fputs("<key>RequestAttributes</key>\n", outfile
);
2532 fputs("<array>\n", outfile
);
2535 fputs("<dict>\n", outfile
);
2536 for (attrptr
= request
->attrs
,
2537 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2539 attrptr
= attrptr
->next
)
2540 print_attr(outfile
, Output
, attrptr
, &group
);
2541 fputs("</dict>\n", outfile
);
2543 fputs("</array>\n", outfile
);
2546 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
2550 printf(" %s:\n", ippOpString(op
));
2552 for (attrptr
= request
->attrs
; attrptr
; attrptr
= attrptr
->next
)
2553 print_attr(stdout
, _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
2556 printf(" %-68.68s [", name
);
2560 if ((skip_previous
&& !prev_pass
) || skip_test
)
2567 if (Output
== _CUPS_OUTPUT_PLIST
)
2569 fputs("<key>Successful</key>\n", outfile
);
2570 fputs("<true />\n", outfile
);
2571 fputs("<key>Skipped</key>\n", outfile
);
2572 fputs("<true />\n", outfile
);
2573 fputs("<key>StatusCode</key>\n", outfile
);
2574 print_xml_string(outfile
, "string", "skip");
2575 fputs("<key>ResponseAttributes</key>\n", outfile
);
2576 fputs("<dict />\n", outfile
);
2579 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
2591 status
= HTTP_STATUS_OK
;
2593 if (transfer
== _CUPS_TRANSFER_CHUNKED
||
2594 (transfer
== _CUPS_TRANSFER_AUTO
&& filename
[0]))
2597 * Send request using chunking - a 0 length means "chunk".
2605 * Send request using content length...
2608 length
= ippLength(request
);
2610 if (filename
[0] && (reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2613 * Read the file to get the uncompressed file size...
2616 while ((bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
2617 length
+= (size_t)bytes
;
2619 cupsFileClose(reqfile
);
2624 * Send the request...
2631 if (status
!= HTTP_STATUS_ERROR
)
2633 while (!response
&& !Cancel
&& prev_pass
)
2635 status
= cupsSendRequest(http
, request
, resource
, length
);
2639 httpSetField(http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
2640 #endif /* HAVE_LIBZ */
2642 if (!Cancel
&& status
== HTTP_STATUS_CONTINUE
&&
2643 request
->state
== IPP_DATA
&& filename
[0])
2645 if ((reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2648 (bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
2649 if ((status
= cupsWriteRequestData(http
, buffer
, (size_t)bytes
)) != HTTP_STATUS_CONTINUE
)
2652 cupsFileClose(reqfile
);
2656 snprintf(buffer
, sizeof(buffer
), "%s: %s", filename
,
2658 _cupsSetError(IPP_INTERNAL_ERROR
, buffer
, 0);
2660 status
= HTTP_STATUS_ERROR
;
2665 * Get the server's response...
2668 if (!Cancel
&& status
!= HTTP_STATUS_ERROR
)
2670 response
= cupsGetResponse(http
, resource
);
2671 status
= httpGetStatus(http
);
2674 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& http
->error
!= EINVAL
&&
2676 http
->error
!= WSAETIMEDOUT
)
2678 http
->error
!= ETIMEDOUT
)
2681 if (httpReconnect2(http
, 30000, NULL
))
2684 else if (status
== HTTP_STATUS_ERROR
|| status
== HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED
)
2689 else if (status
!= HTTP_STATUS_OK
)
2693 if (status
== HTTP_STATUS_UNAUTHORIZED
)
2701 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& http
->error
!= EINVAL
&&
2703 http
->error
!= WSAETIMEDOUT
)
2705 http
->error
!= ETIMEDOUT
)
2708 if (httpReconnect2(http
, 30000, NULL
))
2711 else if (status
== HTTP_STATUS_ERROR
)
2714 httpReconnect2(http
, 30000, NULL
);
2718 else if (status
!= HTTP_STATUS_OK
)
2725 * Check results of request...
2728 cupsArrayClear(errors
);
2730 if (http
->version
!= HTTP_1_1
)
2731 add_stringf(errors
, "Bad HTTP version (%d.%d)", http
->version
/ 100,
2732 http
->version
% 100);
2737 * No response, log error...
2740 add_stringf(errors
, "IPP request failed with status %s (%s)",
2741 ippErrorString(cupsLastError()),
2742 cupsLastErrorString());
2747 * Collect common attribute values...
2750 if ((attrptr
= ippFindAttribute(response
, "job-id",
2751 IPP_TAG_INTEGER
)) != NULL
)
2753 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2754 set_variable(outfile
, vars
, "job-id", temp
);
2757 if ((attrptr
= ippFindAttribute(response
, "job-uri",
2758 IPP_TAG_URI
)) != NULL
)
2759 set_variable(outfile
, vars
, "job-uri", attrptr
->values
[0].string
.text
);
2761 if ((attrptr
= ippFindAttribute(response
, "notify-subscription-id",
2762 IPP_TAG_INTEGER
)) != NULL
)
2764 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2765 set_variable(outfile
, vars
, "notify-subscription-id", temp
);
2769 * Check response, validating groups and attributes and logging errors
2773 if (response
->state
!= IPP_DATA
)
2775 "Missing end-of-attributes-tag in response "
2776 "(RFC 2910 section 3.5.1)");
2779 (response
->request
.status
.version
[0] != (version
/ 10) ||
2780 response
->request
.status
.version
[1] != (version
% 10)))
2782 "Bad version %d.%d in response - expected %d.%d "
2783 "(RFC 2911 section 3.1.8).",
2784 response
->request
.status
.version
[0],
2785 response
->request
.status
.version
[1],
2786 version
/ 10, version
% 10);
2788 if (response
->request
.status
.request_id
!= request_id
)
2790 "Bad request ID %d in response - expected %d "
2791 "(RFC 2911 section 3.1.1)",
2792 response
->request
.status
.request_id
, request_id
);
2794 attrptr
= response
->attrs
;
2797 "Missing first attribute \"attributes-charset "
2798 "(charset)\" in group operation-attributes-tag "
2799 "(RFC 2911 section 3.1.4).");
2802 if (!attrptr
->name
||
2803 attrptr
->value_tag
!= IPP_TAG_CHARSET
||
2804 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2805 attrptr
->num_values
!= 1 ||
2806 strcmp(attrptr
->name
, "attributes-charset"))
2808 "Bad first attribute \"%s (%s%s)\" in group %s, "
2809 "expected \"attributes-charset (charset)\" in "
2810 "group operation-attributes-tag (RFC 2911 section "
2812 attrptr
->name
? attrptr
->name
: "(null)",
2813 attrptr
->num_values
> 1 ? "1setOf " : "",
2814 ippTagString(attrptr
->value_tag
),
2815 ippTagString(attrptr
->group_tag
));
2817 attrptr
= attrptr
->next
;
2820 "Missing second attribute \"attributes-natural-"
2821 "language (naturalLanguage)\" in group "
2822 "operation-attributes-tag (RFC 2911 section "
2824 else if (!attrptr
->name
||
2825 attrptr
->value_tag
!= IPP_TAG_LANGUAGE
||
2826 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2827 attrptr
->num_values
!= 1 ||
2828 strcmp(attrptr
->name
, "attributes-natural-language"))
2830 "Bad first attribute \"%s (%s%s)\" in group %s, "
2831 "expected \"attributes-natural-language "
2832 "(naturalLanguage)\" in group "
2833 "operation-attributes-tag (RFC 2911 section "
2835 attrptr
->name
? attrptr
->name
: "(null)",
2836 attrptr
->num_values
> 1 ? "1setOf " : "",
2837 ippTagString(attrptr
->value_tag
),
2838 ippTagString(attrptr
->group_tag
));
2841 if ((attrptr
= ippFindAttribute(response
, "status-message",
2842 IPP_TAG_ZERO
)) != NULL
)
2844 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2846 "status-message (text(255)) has wrong value tag "
2847 "%s (RFC 2911 section 3.1.6.2).",
2848 ippTagString(attrptr
->value_tag
));
2849 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2851 "status-message (text(255)) has wrong group tag "
2852 "%s (RFC 2911 section 3.1.6.2).",
2853 ippTagString(attrptr
->group_tag
));
2854 if (attrptr
->num_values
!= 1)
2856 "status-message (text(255)) has %d values "
2857 "(RFC 2911 section 3.1.6.2).",
2858 attrptr
->num_values
);
2859 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2860 strlen(attrptr
->values
[0].string
.text
) > 255)
2862 "status-message (text(255)) has bad length %d"
2863 " (RFC 2911 section 3.1.6.2).",
2864 (int)strlen(attrptr
->values
[0].string
.text
));
2867 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
2868 IPP_TAG_ZERO
)) != NULL
)
2870 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2872 "detailed-status-message (text(MAX)) has wrong "
2873 "value tag %s (RFC 2911 section 3.1.6.3).",
2874 ippTagString(attrptr
->value_tag
));
2875 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2877 "detailed-status-message (text(MAX)) has wrong "
2878 "group tag %s (RFC 2911 section 3.1.6.3).",
2879 ippTagString(attrptr
->group_tag
));
2880 if (attrptr
->num_values
!= 1)
2882 "detailed-status-message (text(MAX)) has %d values"
2883 " (RFC 2911 section 3.1.6.3).",
2884 attrptr
->num_values
);
2885 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2886 strlen(attrptr
->values
[0].string
.text
) > 1023)
2888 "detailed-status-message (text(MAX)) has bad "
2889 "length %d (RFC 2911 section 3.1.6.3).",
2890 (int)strlen(attrptr
->values
[0].string
.text
));
2893 a
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2895 for (attrptr
= response
->attrs
,
2896 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2898 attrptr
= attrptr
->next
)
2900 if (attrptr
->group_tag
!= group
)
2902 int out_of_order
= 0; /* Are attribute groups out-of-order? */
2905 switch (attrptr
->group_tag
)
2910 case IPP_TAG_OPERATION
:
2914 case IPP_TAG_UNSUPPORTED_GROUP
:
2915 if (group
!= IPP_TAG_OPERATION
)
2920 case IPP_TAG_PRINTER
:
2921 if (group
!= IPP_TAG_OPERATION
&&
2922 group
!= IPP_TAG_UNSUPPORTED_GROUP
)
2926 case IPP_TAG_SUBSCRIPTION
:
2927 if (group
> attrptr
->group_tag
&&
2928 group
!= IPP_TAG_DOCUMENT
)
2933 if (group
> attrptr
->group_tag
)
2939 add_stringf(errors
, "Attribute groups out of order (%s < %s)",
2940 ippTagString(attrptr
->group_tag
),
2941 ippTagString(group
));
2943 if (attrptr
->group_tag
!= IPP_TAG_ZERO
)
2944 group
= attrptr
->group_tag
;
2947 validate_attr(outfile
, errors
, attrptr
);
2951 if (cupsArrayFind(a
, attrptr
->name
))
2952 add_stringf(errors
, "Duplicate \"%s\" attribute in %s group",
2953 attrptr
->name
, ippTagString(group
));
2955 cupsArrayAdd(a
, attrptr
->name
);
2962 * Now check the test-defined expected status-code and attribute
2966 for (i
= 0; i
< num_statuses
; i
++)
2968 if (statuses
[i
].if_defined
&&
2969 !get_variable(vars
, statuses
[i
].if_defined
))
2972 if (statuses
[i
].if_not_defined
&&
2973 get_variable(vars
, statuses
[i
].if_not_defined
))
2976 if (response
->request
.status
.status_code
== statuses
[i
].status
)
2978 if (statuses
[i
].repeat_match
&&
2979 repeat_count
< statuses
[i
].repeat_limit
)
2982 if (statuses
[i
].define_match
)
2983 set_variable(outfile
, vars
, statuses
[i
].define_match
, "1");
2989 if (statuses
[i
].repeat_no_match
&&
2990 repeat_count
< statuses
[i
].repeat_limit
)
2993 if (statuses
[i
].define_no_match
)
2995 set_variable(outfile
, vars
, statuses
[i
].define_no_match
, "1");
3001 if (i
== num_statuses
&& num_statuses
> 0)
3003 for (i
= 0; i
< num_statuses
; i
++)
3005 if (statuses
[i
].if_defined
&&
3006 !get_variable(vars
, statuses
[i
].if_defined
))
3009 if (statuses
[i
].if_not_defined
&&
3010 get_variable(vars
, statuses
[i
].if_not_defined
))
3013 if (!statuses
[i
].repeat_match
)
3014 add_stringf(errors
, "EXPECTED: STATUS %s (got %s)",
3015 ippErrorString(statuses
[i
].status
),
3016 ippErrorString(cupsLastError()));
3019 if ((attrptr
= ippFindAttribute(response
, "status-message",
3020 IPP_TAG_TEXT
)) != NULL
)
3021 add_stringf(errors
, "status-message=\"%s\"",
3022 attrptr
->values
[0].string
.text
);
3025 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3027 if (expect
->if_defined
&& !get_variable(vars
, expect
->if_defined
))
3030 if (expect
->if_not_defined
&&
3031 get_variable(vars
, expect
->if_not_defined
))
3034 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
3038 if ((found
&& expect
->not_expect
) ||
3039 (!found
&& !(expect
->not_expect
|| expect
->optional
)) ||
3040 (found
&& !expect_matches(expect
, found
->value_tag
)) ||
3041 (found
&& expect
->in_group
&&
3042 found
->group_tag
!= expect
->in_group
))
3044 if (expect
->define_no_match
)
3045 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3046 else if (!expect
->define_match
&& !expect
->define_value
)
3048 if (found
&& expect
->not_expect
)
3049 add_stringf(errors
, "NOT EXPECTED: %s", expect
->name
);
3050 else if (!found
&& !(expect
->not_expect
|| expect
->optional
))
3051 add_stringf(errors
, "EXPECTED: %s", expect
->name
);
3054 if (!expect_matches(expect
, found
->value_tag
))
3055 add_stringf(errors
, "EXPECTED: %s OF-TYPE %s (got %s)",
3056 expect
->name
, expect
->of_type
,
3057 ippTagString(found
->value_tag
));
3059 if (expect
->in_group
&& found
->group_tag
!= expect
->in_group
)
3060 add_stringf(errors
, "EXPECTED: %s IN-GROUP %s (got %s).",
3061 expect
->name
, ippTagString(expect
->in_group
),
3062 ippTagString(found
->group_tag
));
3066 if (expect
->repeat_no_match
&&
3067 repeat_count
< expect
->repeat_limit
)
3074 ippAttributeString(found
, buffer
, sizeof(buffer
));
3076 if (found
&& expect
->with_value_from
&& !with_value_from(NULL
, ippFindAttribute(response
, expect
->with_value_from
, IPP_TAG_ZERO
), found
, buffer
, sizeof(buffer
)))
3078 if (expect
->define_no_match
)
3079 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3080 else if (!expect
->define_match
&& !expect
->define_value
&& !expect
->repeat_match
&& !expect
->repeat_no_match
)
3082 add_stringf(errors
, "EXPECTED: %s WITH-VALUES-FROM %s", expect
->name
, expect
->with_value_from
);
3084 with_value_from(errors
, ippFindAttribute(response
, expect
->with_value_from
, IPP_TAG_ZERO
), found
, buffer
, sizeof(buffer
));
3087 if (expect
->repeat_no_match
&& repeat_count
< expect
->repeat_limit
)
3092 else if (found
&& !with_value(outfile
, NULL
, expect
->with_value
, expect
->with_flags
, found
, buffer
, sizeof(buffer
)))
3094 if (expect
->define_no_match
)
3095 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3096 else if (!expect
->define_match
&& !expect
->define_value
&&
3097 !expect
->repeat_match
&& !expect
->repeat_no_match
)
3099 if (expect
->with_flags
& _CUPS_WITH_REGEX
)
3100 add_stringf(errors
, "EXPECTED: %s %s /%s/",
3102 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
3103 "WITH-ALL-VALUES" : "WITH-VALUE",
3104 expect
->with_value
);
3106 add_stringf(errors
, "EXPECTED: %s %s \"%s\"",
3108 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
3109 "WITH-ALL-VALUES" : "WITH-VALUE",
3110 expect
->with_value
);
3112 with_value(outfile
, errors
, expect
->with_value
, expect
->with_flags
, found
,
3113 buffer
, sizeof(buffer
));
3116 if (expect
->repeat_no_match
&&
3117 repeat_count
< expect
->repeat_limit
)
3123 if (found
&& expect
->count
> 0 &&
3124 found
->num_values
!= expect
->count
)
3126 if (expect
->define_no_match
)
3127 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3128 else if (!expect
->define_match
&& !expect
->define_value
)
3130 add_stringf(errors
, "EXPECTED: %s COUNT %d (got %d)", expect
->name
,
3131 expect
->count
, found
->num_values
);
3134 if (expect
->repeat_no_match
&&
3135 repeat_count
< expect
->repeat_limit
)
3141 if (found
&& expect
->same_count_as
)
3143 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
3146 if (!attrptr
|| attrptr
->num_values
!= found
->num_values
)
3148 if (expect
->define_no_match
)
3149 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3150 else if (!expect
->define_match
&& !expect
->define_value
)
3154 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
3155 "(not returned)", expect
->name
,
3156 found
->num_values
, expect
->same_count_as
);
3157 else if (attrptr
->num_values
!= found
->num_values
)
3159 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
3160 "(%d values)", expect
->name
, found
->num_values
,
3161 expect
->same_count_as
, attrptr
->num_values
);
3164 if (expect
->repeat_no_match
&&
3165 repeat_count
< expect
->repeat_limit
)
3172 if (found
&& expect
->define_match
)
3173 set_variable(outfile
, vars
, expect
->define_match
, "1");
3175 if (found
&& expect
->define_value
)
3177 if (!expect
->with_value
)
3179 int last
= ippGetCount(found
) - 1;
3180 /* Last element in attribute */
3182 switch (ippGetValueTag(found
))
3185 case IPP_TAG_INTEGER
:
3186 snprintf(buffer
, sizeof(buffer
), "%d", ippGetInteger(found
, last
));
3189 case IPP_TAG_BOOLEAN
:
3190 if (ippGetBoolean(found
, last
))
3191 strlcpy(buffer
, "true", sizeof(buffer
));
3193 strlcpy(buffer
, "false", sizeof(buffer
));
3196 case IPP_TAG_RESOLUTION
:
3198 int xres
, /* Horizontal resolution */
3199 yres
; /* Vertical resolution */
3200 ipp_res_t units
; /* Resolution units */
3202 xres
= ippGetResolution(found
, last
, &yres
, &units
);
3205 snprintf(buffer
, sizeof(buffer
), "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
3207 snprintf(buffer
, sizeof(buffer
), "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
3211 case IPP_TAG_CHARSET
:
3212 case IPP_TAG_KEYWORD
:
3213 case IPP_TAG_LANGUAGE
:
3214 case IPP_TAG_MIMETYPE
:
3216 case IPP_TAG_NAMELANG
:
3218 case IPP_TAG_TEXTLANG
:
3220 case IPP_TAG_URISCHEME
:
3221 strlcpy(buffer
, ippGetString(found
, last
, NULL
), sizeof(buffer
));
3225 ippAttributeString(found
, buffer
, sizeof(buffer
));
3230 set_variable(outfile
, vars
, expect
->define_value
, buffer
);
3233 if (found
&& expect
->repeat_match
&&
3234 repeat_count
< expect
->repeat_limit
)
3237 while (expect
->expect_all
&& (found
= ippFindNextAttribute(response
, expect
->name
, IPP_TAG_ZERO
)) != NULL
);
3242 * If we are going to repeat this test, sleep 1 second so we don't flood
3243 * the printer with requests...
3248 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
3250 printf("%04d]\n", repeat_count
);
3253 if (num_displayed
> 0)
3255 for (attrptr
= ippFirstAttribute(response
); attrptr
; attrptr
= ippNextAttribute(response
))
3257 const char *attrname
= ippGetName(attrptr
);
3260 for (i
= 0; i
< num_displayed
; i
++)
3262 if (!strcmp(displayed
[i
], attrname
))
3264 print_attr(stdout
, _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
3273 usleep(repeat_interval
);
3275 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
3277 printf(" %-68.68s [", name
);
3282 while (repeat_test
);
3288 if (cupsArrayCount(errors
) > 0)
3289 prev_pass
= pass
= 0;
3296 if (Output
== _CUPS_OUTPUT_PLIST
)
3298 fputs("<key>Successful</key>\n", outfile
);
3299 fputs(prev_pass
? "<true />\n" : "<false />\n", outfile
);
3300 fputs("<key>StatusCode</key>\n", outfile
);
3301 print_xml_string(outfile
, "string", ippErrorString(cupsLastError()));
3302 fputs("<key>ResponseAttributes</key>\n", outfile
);
3303 fputs("<array>\n", outfile
);
3304 fputs("<dict>\n", outfile
);
3305 for (attrptr
= response
? response
->attrs
: NULL
,
3306 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
3308 attrptr
= attrptr
->next
)
3309 print_attr(outfile
, Output
, attrptr
, &group
);
3310 fputs("</dict>\n", outfile
);
3311 fputs("</array>\n", outfile
);
3314 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
3316 puts(prev_pass
? "PASS]" : "FAIL]");
3318 if (!prev_pass
|| (Verbosity
&& response
))
3320 printf(" RECEIVED: %lu bytes in response\n",
3321 (unsigned long)ippLength(response
));
3322 printf(" status-code = %s (%s)\n", ippErrorString(cupsLastError()),
3323 cupsLastErrorString());
3325 if (Verbosity
&& response
)
3327 for (attrptr
= response
->attrs
;
3329 attrptr
= attrptr
->next
)
3330 print_attr(stdout
, _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
3334 else if (!prev_pass
&& Output
!= _CUPS_OUTPUT_QUIET
)
3335 fprintf(stderr
, "%s\n", cupsLastErrorString());
3337 if (prev_pass
&& Output
>= _CUPS_OUTPUT_LIST
&& !Verbosity
&&
3340 size_t width
; /* Length of value */
3342 for (i
= 0; i
< num_displayed
; i
++)
3344 widths
[i
] = strlen(displayed
[i
]);
3346 for (attrptr
= ippFindAttribute(response
, displayed
[i
], IPP_TAG_ZERO
);
3348 attrptr
= ippFindNextAttribute(response
, displayed
[i
],
3351 width
= ippAttributeString(attrptr
, NULL
, 0);
3352 if (width
> widths
[i
])
3357 if (Output
== _CUPS_OUTPUT_CSV
)
3358 print_csv(outfile
, NULL
, num_displayed
, displayed
, widths
);
3360 print_line(outfile
, NULL
, num_displayed
, displayed
, widths
);
3362 attrptr
= response
->attrs
;
3366 while (attrptr
&& attrptr
->group_tag
<= IPP_TAG_OPERATION
)
3367 attrptr
= attrptr
->next
;
3371 if (Output
== _CUPS_OUTPUT_CSV
)
3372 print_csv(outfile
, attrptr
, num_displayed
, displayed
, widths
);
3374 print_line(outfile
, attrptr
, num_displayed
, displayed
, widths
);
3376 while (attrptr
&& attrptr
->group_tag
> IPP_TAG_OPERATION
)
3377 attrptr
= attrptr
->next
;
3381 else if (!prev_pass
)
3383 if (Output
== _CUPS_OUTPUT_PLIST
)
3385 fputs("<key>Errors</key>\n", outfile
);
3386 fputs("<array>\n", outfile
);
3388 for (error
= (char *)cupsArrayFirst(errors
);
3390 error
= (char *)cupsArrayNext(errors
))
3391 print_xml_string(outfile
, "string", error
);
3393 fputs("</array>\n", outfile
);
3396 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
3398 for (error
= (char *)cupsArrayFirst(errors
);
3400 error
= (char *)cupsArrayNext(errors
))
3401 printf(" %s\n", error
);
3405 if (num_displayed
> 0 && !Verbosity
&& response
&& (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
)))
3407 for (attrptr
= response
->attrs
;
3409 attrptr
= attrptr
->next
)
3413 for (i
= 0; i
< num_displayed
; i
++)
3415 if (!strcmp(displayed
[i
], attrptr
->name
))
3417 print_attr(outfile
, Output
, attrptr
, NULL
);
3427 if (Output
== _CUPS_OUTPUT_PLIST
)
3428 fputs("</dict>\n", outfile
);
3432 ippDelete(response
);
3435 for (i
= 0; i
< num_statuses
; i
++)
3437 if (statuses
[i
].if_defined
)
3438 free(statuses
[i
].if_defined
);
3439 if (statuses
[i
].if_not_defined
)
3440 free(statuses
[i
].if_not_defined
);
3441 if (statuses
[i
].define_match
)
3442 free(statuses
[i
].define_match
);
3443 if (statuses
[i
].define_no_match
)
3444 free(statuses
[i
].define_no_match
);
3448 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3451 if (expect
->of_type
)
3452 free(expect
->of_type
);
3453 if (expect
->same_count_as
)
3454 free(expect
->same_count_as
);
3455 if (expect
->if_defined
)
3456 free(expect
->if_defined
);
3457 if (expect
->if_not_defined
)
3458 free(expect
->if_not_defined
);
3459 if (expect
->with_value
)
3460 free(expect
->with_value
);
3461 if (expect
->define_match
)
3462 free(expect
->define_match
);
3463 if (expect
->define_no_match
)
3464 free(expect
->define_no_match
);
3465 if (expect
->define_value
)
3466 free(expect
->define_value
);
3470 for (i
= 0; i
< num_displayed
; i
++)
3474 if (!ignore_errors
&& !prev_pass
)
3480 cupsArrayDelete(errors
);
3487 ippDelete(response
);
3489 for (i
= 0; i
< num_statuses
; i
++)
3491 if (statuses
[i
].if_defined
)
3492 free(statuses
[i
].if_defined
);
3493 if (statuses
[i
].if_not_defined
)
3494 free(statuses
[i
].if_not_defined
);
3495 if (statuses
[i
].define_match
)
3496 free(statuses
[i
].define_match
);
3497 if (statuses
[i
].define_no_match
)
3498 free(statuses
[i
].define_no_match
);
3501 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3504 if (expect
->of_type
)
3505 free(expect
->of_type
);
3506 if (expect
->same_count_as
)
3507 free(expect
->same_count_as
);
3508 if (expect
->if_defined
)
3509 free(expect
->if_defined
);
3510 if (expect
->if_not_defined
)
3511 free(expect
->if_not_defined
);
3512 if (expect
->with_value
)
3513 free(expect
->with_value
);
3514 if (expect
->define_match
)
3515 free(expect
->define_match
);
3516 if (expect
->define_no_match
)
3517 free(expect
->define_no_match
);
3518 if (expect
->define_value
)
3519 free(expect
->define_value
);
3522 for (i
= 0; i
< num_displayed
; i
++)
3530 * 'expand_variables()' - Expand variables in a string.
3534 expand_variables(_cups_vars_t
*vars
, /* I - Variables */
3535 char *dst
, /* I - Destination string buffer */
3536 const char *src
, /* I - Source string */
3537 size_t dstsize
) /* I - Size of destination buffer */
3539 char *dstptr
, /* Pointer into destination */
3540 *dstend
, /* End of destination */
3541 temp
[256], /* Temporary string */
3542 *tempptr
; /* Pointer into temporary string */
3543 const char *value
; /* Value to substitute */
3547 dstend
= dst
+ dstsize
- 1;
3549 while (*src
&& dstptr
< dstend
)
3554 * Substitute a string/number...
3557 if (!strncmp(src
, "$$", 2))
3562 else if (!strncmp(src
, "$ENV[", 5))
3564 strlcpy(temp
, src
+ 5, sizeof(temp
));
3566 for (tempptr
= temp
; *tempptr
; tempptr
++)
3567 if (*tempptr
== ']')
3573 value
= getenv(temp
);
3574 src
+= tempptr
- temp
+ 5;
3581 strlcpy(temp
, src
, sizeof(temp
));
3582 if ((tempptr
= strchr(temp
, '}')) != NULL
)
3585 tempptr
= temp
+ strlen(temp
);
3589 strlcpy(temp
, src
+ 1, sizeof(temp
));
3591 for (tempptr
= temp
; *tempptr
; tempptr
++)
3592 if (!isalnum(*tempptr
& 255) && *tempptr
!= '-' && *tempptr
!= '_')
3599 if (!strcmp(temp
, "uri"))
3601 else if (!strcmp(temp
, "filename"))
3602 value
= vars
->filename
;
3603 else if (!strcmp(temp
, "scheme") || !strcmp(temp
, "method"))
3604 value
= vars
->scheme
;
3605 else if (!strcmp(temp
, "username"))
3606 value
= vars
->userpass
;
3607 else if (!strcmp(temp
, "hostname"))
3608 value
= vars
->hostname
;
3609 else if (!strcmp(temp
, "port"))
3611 snprintf(temp
, sizeof(temp
), "%d", vars
->port
);
3614 else if (!strcmp(temp
, "resource"))
3615 value
= vars
->resource
;
3616 else if (!strcmp(temp
, "user"))
3619 value
= get_variable(vars
, temp
);
3621 src
+= tempptr
- temp
+ 1;
3626 strlcpy(dstptr
, value
, (size_t)(dstend
- dstptr
+ 1));
3627 dstptr
+= strlen(dstptr
);
3639 * 'expect_matches()' - Return true if the tag matches the specification.
3642 static int /* O - 1 if matches, 0 otherwise */
3644 _cups_expect_t
*expect
, /* I - Expected attribute */
3645 ipp_tag_t value_tag
) /* I - Value tag for attribute */
3647 int match
; /* Match? */
3648 char *of_type
, /* Type name to match */
3649 *next
, /* Next name to match */
3650 sep
; /* Separator character */
3654 * If we don't expect a particular type, return immediately...
3657 if (!expect
->of_type
)
3661 * Parse the "of_type" value since the string can contain multiple attribute
3662 * types separated by "," or "|"...
3665 for (of_type
= expect
->of_type
, match
= 0; !match
&& *of_type
; of_type
= next
)
3668 * Find the next separator, and set it (temporarily) to nul if present.
3671 for (next
= of_type
; *next
&& *next
!= '|' && *next
!= ','; next
++);
3673 if ((sep
= *next
) != '\0')
3677 * Support some meta-types to make it easier to write the test file.
3680 if (!strcmp(of_type
, "text"))
3681 match
= value_tag
== IPP_TAG_TEXTLANG
|| value_tag
== IPP_TAG_TEXT
;
3682 else if (!strcmp(of_type
, "name"))
3683 match
= value_tag
== IPP_TAG_NAMELANG
|| value_tag
== IPP_TAG_NAME
;
3684 else if (!strcmp(of_type
, "collection"))
3685 match
= value_tag
== IPP_TAG_BEGIN_COLLECTION
;
3687 match
= value_tag
== ippTagValue(of_type
);
3690 * Restore the separator if we have one...
3702 * 'get_collection()' - Get a collection value from the current test file.
3705 static ipp_t
* /* O - Collection value */
3706 get_collection(FILE *outfile
, /* I - Output file */
3707 _cups_vars_t
*vars
, /* I - Variables */
3708 FILE *fp
, /* I - File to read from */
3709 int *linenum
) /* IO - Line number */
3711 char token
[1024], /* Token from file */
3712 temp
[1024], /* Temporary string */
3713 attr
[128]; /* Attribute name */
3714 ipp_tag_t value
; /* Current value type */
3715 ipp_t
*col
= ippNew(); /* Collection value */
3716 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
3719 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
3721 if (!strcmp(token
, "}"))
3723 else if (!strcmp(token
, "{") && lastcol
)
3726 * Another collection value
3729 ipp_t
*subcol
= get_collection(outfile
, vars
, fp
, linenum
);
3730 /* Collection value */
3733 ippSetCollection(col
, &lastcol
, ippGetCount(lastcol
), subcol
);
3737 else if (!_cups_strcasecmp(token
, "MEMBER"))
3745 if (!get_token(fp
, token
, sizeof(token
), linenum
))
3747 print_fatal_error(outfile
, "Missing MEMBER value tag on line %d.", *linenum
);
3751 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
3753 print_fatal_error(outfile
, "Bad MEMBER value tag \"%s\" on line %d.", token
,
3758 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
3760 print_fatal_error(outfile
, "Missing MEMBER name on line %d.", *linenum
);
3764 if (!get_token(fp
, temp
, sizeof(temp
), linenum
))
3766 print_fatal_error(outfile
, "Missing MEMBER value on line %d.", *linenum
);
3770 expand_variables(vars
, token
, temp
, sizeof(token
));
3774 case IPP_TAG_BOOLEAN
:
3775 if (!_cups_strcasecmp(token
, "true"))
3776 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
3778 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, (char)atoi(token
));
3781 case IPP_TAG_INTEGER
:
3783 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
3786 case IPP_TAG_RESOLUTION
:
3788 int xres
, /* X resolution */
3789 yres
; /* Y resolution */
3790 char units
[6]; /* Units */
3792 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
3793 (_cups_strcasecmp(units
, "dpi") &&
3794 _cups_strcasecmp(units
, "dpc") &&
3795 _cups_strcasecmp(units
, "dpcm") &&
3796 _cups_strcasecmp(units
, "other")))
3798 print_fatal_error(outfile
, "Bad resolution value \"%s\" on line %d.",
3803 if (!_cups_strcasecmp(units
, "dpi"))
3804 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
3805 else if (!_cups_strcasecmp(units
, "dpc") ||
3806 !_cups_strcasecmp(units
, "dpcm"))
3807 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_CM
, xres
, yres
);
3809 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, (ipp_res_t
)0, xres
, yres
);
3813 case IPP_TAG_RANGE
:
3815 int lowers
[4], /* Lower value */
3816 uppers
[4], /* Upper values */
3817 num_vals
; /* Number of values */
3820 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
3821 lowers
+ 0, uppers
+ 0,
3822 lowers
+ 1, uppers
+ 1,
3823 lowers
+ 2, uppers
+ 2,
3824 lowers
+ 3, uppers
+ 3);
3826 if ((num_vals
& 1) || num_vals
== 0)
3828 print_fatal_error(outfile
, "Bad rangeOfInteger value \"%s\" on line %d.",
3833 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
3838 case IPP_TAG_BEGIN_COLLECTION
:
3839 if (!strcmp(token
, "{"))
3841 ipp_t
*subcol
= get_collection(outfile
, vars
, fp
, linenum
);
3842 /* Collection value */
3846 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
3854 print_fatal_error(outfile
, "Bad collection value on line %d.", *linenum
);
3858 case IPP_TAG_STRING
:
3859 ippAddOctetString(col
, IPP_TAG_ZERO
, attr
, token
, (int)strlen(token
));
3863 if (!strchr(token
, ','))
3864 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
3868 * Multiple string values...
3871 int num_values
; /* Number of values */
3872 char *values
[100], /* Values */
3873 *ptr
; /* Pointer to next value */
3879 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
3882 values
[num_values
] = ptr
;
3886 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
3887 NULL
, (const char **)values
);
3894 print_fatal_error(outfile
, "Unexpected token %s seen on line %d.", token
, *linenum
);
3902 * If we get here there was a parse error; free memory and return.
3914 * 'get_filename()' - Get a filename based on the current test file.
3917 static char * /* O - Filename */
3918 get_filename(const char *testfile
, /* I - Current test file */
3919 char *dst
, /* I - Destination filename */
3920 const char *src
, /* I - Source filename */
3921 size_t dstsize
) /* I - Size of destination buffer */
3923 char *dstptr
; /* Pointer into destination */
3924 _cups_globals_t
*cg
= _cupsGlobals();
3928 if (*src
== '<' && src
[strlen(src
) - 1] == '>')
3931 * Map <filename> to CUPS_DATADIR/ipptool/filename...
3934 snprintf(dst
, dstsize
, "%s/ipptool/%s", cg
->cups_datadir
, src
+ 1);
3935 dstptr
= dst
+ strlen(dst
) - 1;
3939 else if (*src
== '/' || !strchr(testfile
, '/')
3941 || (isalpha(*src
& 255) && src
[1] == ':')
3946 * Use the path as-is...
3949 strlcpy(dst
, src
, dstsize
);
3954 * Make path relative to testfile...
3957 strlcpy(dst
, testfile
, dstsize
);
3958 if ((dstptr
= strrchr(dst
, '/')) != NULL
)
3961 dstptr
= dst
; /* Should never happen */
3963 strlcpy(dstptr
, src
, dstsize
- (size_t)(dstptr
- dst
));
3971 * 'get_string()' - Get a pointer to a string value or the portion of interest.
3974 static const char * /* O - Pointer to string */
3975 get_string(ipp_attribute_t
*attr
, /* I - IPP attribute */
3976 int element
, /* I - Element to fetch */
3977 int flags
, /* I - Value ("with") flags */
3978 char *buffer
, /* I - Temporary buffer */
3979 size_t bufsize
) /* I - Size of temporary buffer */
3981 const char *value
; /* Value */
3982 char *ptr
, /* Pointer into value */
3983 scheme
[256], /* URI scheme */
3984 userpass
[256], /* Username/password */
3985 hostname
[256], /* Hostname */
3986 resource
[1024]; /* Resource */
3987 int port
; /* Port number */
3990 value
= ippGetString(attr
, element
, NULL
);
3992 if (flags
& _CUPS_WITH_HOSTNAME
)
3994 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), buffer
, (int)bufsize
, &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
3997 ptr
= buffer
+ strlen(buffer
) - 1;
3998 if (ptr
>= buffer
&& *ptr
== '.')
3999 *ptr
= '\0'; /* Drop trailing "." */
4003 else if (flags
& _CUPS_WITH_RESOURCE
)
4005 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, buffer
, (int)bufsize
) < HTTP_URI_STATUS_OK
)
4010 else if (flags
& _CUPS_WITH_SCHEME
)
4012 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, buffer
, (int)bufsize
, userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
4017 else if (ippGetValueTag(attr
) == IPP_TAG_URI
&& (!strncmp(value
, "ipp://", 6) || !strncmp(value
, "http://", 7) || !strncmp(value
, "ipps://", 7) || !strncmp(value
, "https://", 8)))
4019 http_uri_status_t status
= httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
));
4021 if (status
< HTTP_URI_STATUS_OK
)
4032 * Normalize URI with no trailing dot...
4035 if ((ptr
= hostname
+ strlen(hostname
) - 1) >= hostname
&& *ptr
== '.')
4038 httpAssembleURI(HTTP_URI_CODING_ALL
, buffer
, (int)bufsize
, scheme
, userpass
, hostname
, port
, resource
);
4049 * 'get_token()' - Get a token from a file.
4052 static char * /* O - Token from file or NULL on EOF */
4053 get_token(FILE *fp
, /* I - File to read from */
4054 char *buf
, /* I - Buffer to read into */
4055 int buflen
, /* I - Length of buffer */
4056 int *linenum
) /* IO - Current line number */
4058 int ch
, /* Character from file */
4059 quote
; /* Quoting character */
4060 char *bufptr
, /* Pointer into buffer */
4061 *bufend
; /* End of buffer */
4067 * Skip whitespace...
4070 while (isspace(ch
= getc(fp
)))
4082 else if (ch
== '\'' || ch
== '\"')
4085 * Quoted text or regular expression...
4090 bufend
= buf
+ buflen
- 1;
4092 while ((ch
= getc(fp
)) != EOF
)
4097 * Escape next character...
4100 if (bufptr
< bufend
)
4101 *bufptr
++ = (char)ch
;
4103 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
4104 *bufptr
++ = (char)ch
;
4106 else if (ch
== quote
)
4108 else if (bufptr
< bufend
)
4109 *bufptr
++ = (char)ch
;
4122 while ((ch
= getc(fp
)) != EOF
)
4128 else if (ch
== '{' || ch
== '}' || ch
== ',')
4138 * Whitespace delimited text...
4144 bufend
= buf
+ buflen
- 1;
4146 while ((ch
= getc(fp
)) != EOF
)
4147 if (isspace(ch
) || ch
== '#')
4149 else if (bufptr
< bufend
)
4150 *bufptr
++ = (char)ch
;
4154 else if (ch
== '\n')
4166 * 'get_variable()' - Get the value of a variable.
4169 static char * /* O - Value or NULL */
4170 get_variable(_cups_vars_t
*vars
, /* I - Variables */
4171 const char *name
) /* I - Variable name */
4173 _cups_var_t key
, /* Search key */
4174 *match
; /* Matching variable, if any */
4177 key
.name
= (char *)name
;
4178 match
= cupsArrayFind(vars
->vars
, &key
);
4180 return (match
? match
->value
: NULL
);
4185 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
4189 static char * /* O - ISO 8601 date/time string */
4190 iso_date(ipp_uchar_t
*date
) /* I - IPP (RFC 1903) date/time value */
4192 time_t utctime
; /* UTC time since 1970 */
4193 struct tm
*utcdate
; /* UTC date/time */
4194 static char buffer
[255]; /* String buffer */
4197 utctime
= ippDateToTime(date
);
4198 utcdate
= gmtime(&utctime
);
4200 snprintf(buffer
, sizeof(buffer
), "%04d-%02d-%02dT%02d:%02d:%02dZ",
4201 utcdate
->tm_year
+ 1900, utcdate
->tm_mon
+ 1, utcdate
->tm_mday
,
4202 utcdate
->tm_hour
, utcdate
->tm_min
, utcdate
->tm_sec
);
4209 * 'password_cb()' - Password callback for authenticated tests.
4212 static const char * /* O - Password */
4213 password_cb(const char *prompt
) /* I - Prompt (unused) */
4217 if (PasswordTries
< 3)
4221 cupsSetUser(Username
);
4231 * 'pause_message()' - Display the message and pause until the user presses a key.
4235 pause_message(const char *message
) /* I - Message */
4238 HANDLE tty
; /* Console handle */
4239 DWORD mode
; /* Console mode */
4240 char key
; /* Key press */
4241 DWORD bytes
; /* Bytes read for key press */
4245 * Disable input echo and set raw input...
4248 if ((tty
= GetStdHandle(STD_INPUT_HANDLE
)) == INVALID_HANDLE_VALUE
)
4251 if (!GetConsoleMode(tty
, &mode
))
4254 if (!SetConsoleMode(tty
, 0))
4258 int tty
; /* /dev/tty - never read from stdin */
4259 struct termios original
, /* Original input mode */
4260 noecho
; /* No echo input mode */
4261 char key
; /* Current key press */
4265 * Disable input echo and set raw input...
4268 if ((tty
= open("/dev/tty", O_RDONLY
)) < 0)
4271 if (tcgetattr(tty
, &original
))
4278 noecho
.c_lflag
&= (tcflag_t
)~(ICANON
| ECHO
| ECHOE
| ISIG
);
4280 if (tcsetattr(tty
, TCSAFLUSH
, &noecho
))
4288 * Display the prompt...
4291 printf("%s\n---- PRESS ANY KEY ----", message
);
4299 ReadFile(tty
, &key
, 1, &bytes
, NULL
);
4305 SetConsoleMode(tty
, mode
);
4318 tcsetattr(tty
, TCSAFLUSH
, &original
);
4323 * Erase the "press any key" prompt...
4326 fputs("\r \r", stdout
);
4332 * 'print_attr()' - Print an attribute on the screen.
4336 print_attr(FILE *outfile
, /* I - Output file */
4337 int format
, /* I - Output format */
4338 ipp_attribute_t
*attr
, /* I - Attribute to print */
4339 ipp_tag_t
*group
) /* IO - Current group */
4341 int i
; /* Looping var */
4342 ipp_attribute_t
*colattr
; /* Collection attribute */
4345 if (format
== _CUPS_OUTPUT_PLIST
)
4347 if (!attr
->name
|| (group
&& *group
!= attr
->group_tag
))
4349 if (attr
->group_tag
!= IPP_TAG_ZERO
)
4351 fputs("</dict>\n", outfile
);
4352 fputs("<dict>\n", outfile
);
4356 *group
= attr
->group_tag
;
4362 print_xml_string(outfile
, "key", attr
->name
);
4363 if (attr
->num_values
> 1)
4364 fputs("<array>\n", outfile
);
4366 switch (attr
->value_tag
)
4368 case IPP_TAG_INTEGER
:
4370 for (i
= 0; i
< attr
->num_values
; i
++)
4371 fprintf(outfile
, "<integer>%d</integer>\n", attr
->values
[i
].integer
);
4374 case IPP_TAG_BOOLEAN
:
4375 for (i
= 0; i
< attr
->num_values
; i
++)
4376 fputs(attr
->values
[i
].boolean
? "<true />\n" : "<false />\n", outfile
);
4379 case IPP_TAG_RANGE
:
4380 for (i
= 0; i
< attr
->num_values
; i
++)
4381 fprintf(outfile
, "<dict><key>lower</key><integer>%d</integer>"
4382 "<key>upper</key><integer>%d</integer></dict>\n",
4383 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
4386 case IPP_TAG_RESOLUTION
:
4387 for (i
= 0; i
< attr
->num_values
; i
++)
4388 fprintf(outfile
, "<dict><key>xres</key><integer>%d</integer>"
4389 "<key>yres</key><integer>%d</integer>"
4390 "<key>units</key><string>%s</string></dict>\n",
4391 attr
->values
[i
].resolution
.xres
,
4392 attr
->values
[i
].resolution
.yres
,
4393 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
4398 for (i
= 0; i
< attr
->num_values
; i
++)
4399 fprintf(outfile
, "<date>%s</date>\n", iso_date(attr
->values
[i
].date
));
4402 case IPP_TAG_STRING
:
4403 for (i
= 0; i
< attr
->num_values
; i
++)
4405 char buffer
[IPP_MAX_LENGTH
* 5 / 4 + 1];
4408 fprintf(outfile
, "<data>%s</data>\n",
4409 httpEncode64_2(buffer
, sizeof(buffer
),
4410 attr
->values
[i
].unknown
.data
,
4411 attr
->values
[i
].unknown
.length
));
4417 case IPP_TAG_KEYWORD
:
4419 case IPP_TAG_URISCHEME
:
4420 case IPP_TAG_CHARSET
:
4421 case IPP_TAG_LANGUAGE
:
4422 case IPP_TAG_MIMETYPE
:
4423 for (i
= 0; i
< attr
->num_values
; i
++)
4424 print_xml_string(outfile
, "string", attr
->values
[i
].string
.text
);
4427 case IPP_TAG_TEXTLANG
:
4428 case IPP_TAG_NAMELANG
:
4429 for (i
= 0; i
< attr
->num_values
; i
++)
4431 fputs("<dict><key>language</key><string>", outfile
);
4432 print_xml_string(outfile
, NULL
, attr
->values
[i
].string
.language
);
4433 fputs("</string><key>string</key><string>", outfile
);
4434 print_xml_string(outfile
, NULL
, attr
->values
[i
].string
.text
);
4435 fputs("</string></dict>\n", outfile
);
4439 case IPP_TAG_BEGIN_COLLECTION
:
4440 for (i
= 0; i
< attr
->num_values
; i
++)
4442 fputs("<dict>\n", outfile
);
4443 for (colattr
= attr
->values
[i
].collection
->attrs
;
4445 colattr
= colattr
->next
)
4446 print_attr(outfile
, format
, colattr
, NULL
);
4447 fputs("</dict>\n", outfile
);
4452 fprintf(outfile
, "<string><<%s>></string>\n", ippTagString(attr
->value_tag
));
4456 if (attr
->num_values
> 1)
4457 fputs("</array>\n", outfile
);
4461 char buffer
[131072]; /* Value buffer */
4463 if (format
== _CUPS_OUTPUT_TEST
)
4467 fputs(" -- separator --\n", outfile
);
4471 fprintf(outfile
, " %s (%s%s) = ", attr
->name
, attr
->num_values
> 1 ? "1setOf " : "", ippTagString(attr
->value_tag
));
4474 ippAttributeString(attr
, buffer
, sizeof(buffer
));
4475 fprintf(outfile
, "%s\n", buffer
);
4481 * 'print_csv()' - Print a line of CSV text.
4486 FILE *outfile
, /* I - Output file */
4487 ipp_attribute_t
*attr
, /* I - First attribute for line */
4488 int num_displayed
, /* I - Number of attributes to display */
4489 char **displayed
, /* I - Attributes to display */
4490 size_t *widths
) /* I - Column widths */
4492 int i
; /* Looping var */
4493 size_t maxlength
; /* Max length of all columns */
4494 char *buffer
, /* String buffer */
4495 *bufptr
; /* Pointer into buffer */
4496 ipp_attribute_t
*current
; /* Current attribute */
4500 * Get the maximum string length we have to show and allocate...
4503 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4504 if (widths
[i
] > maxlength
)
4505 maxlength
= widths
[i
];
4509 if ((buffer
= malloc(maxlength
)) == NULL
)
4513 * Loop through the attributes to display...
4518 for (i
= 0; i
< num_displayed
; i
++)
4521 fputc(',', outfile
);
4525 for (current
= attr
; current
; current
= current
->next
)
4529 else if (!strcmp(current
->name
, displayed
[i
]))
4531 ippAttributeString(current
, buffer
, maxlength
);
4536 if (strchr(buffer
, ',') != NULL
|| strchr(buffer
, '\"') != NULL
||
4537 strchr(buffer
, '\\') != NULL
)
4539 putc('\"', outfile
);
4540 for (bufptr
= buffer
; *bufptr
; bufptr
++)
4542 if (*bufptr
== '\\' || *bufptr
== '\"')
4543 putc('\\', outfile
);
4544 putc(*bufptr
, outfile
);
4546 putc('\"', outfile
);
4549 fputs(buffer
, outfile
);
4551 putc('\n', outfile
);
4555 for (i
= 0; i
< num_displayed
; i
++)
4560 fputs(displayed
[i
], outfile
);
4562 putc('\n', outfile
);
4570 * 'print_fatal_error()' - Print a fatal error message.
4574 print_fatal_error(FILE *outfile
, /* I - Output file */
4575 const char *s
, /* I - Printf-style format string */
4576 ...) /* I - Additional arguments as needed */
4578 char buffer
[10240]; /* Format buffer */
4579 va_list ap
; /* Pointer to arguments */
4583 * Format the error message...
4587 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
4594 if (Output
== _CUPS_OUTPUT_PLIST
)
4596 print_xml_header(outfile
);
4597 print_xml_trailer(outfile
, 0, buffer
);
4600 _cupsLangPrintf(stderr
, "ipptool: %s", buffer
);
4605 * 'print_line()' - Print a line of formatted or CSV text.
4610 FILE *outfile
, /* I - Output file */
4611 ipp_attribute_t
*attr
, /* I - First attribute for line */
4612 int num_displayed
, /* I - Number of attributes to display */
4613 char **displayed
, /* I - Attributes to display */
4614 size_t *widths
) /* I - Column widths */
4616 int i
; /* Looping var */
4617 size_t maxlength
; /* Max length of all columns */
4618 char *buffer
; /* String buffer */
4619 ipp_attribute_t
*current
; /* Current attribute */
4623 * Get the maximum string length we have to show and allocate...
4626 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4627 if (widths
[i
] > maxlength
)
4628 maxlength
= widths
[i
];
4632 if ((buffer
= malloc(maxlength
)) == NULL
)
4636 * Loop through the attributes to display...
4641 for (i
= 0; i
< num_displayed
; i
++)
4648 for (current
= attr
; current
; current
= current
->next
)
4652 else if (!strcmp(current
->name
, displayed
[i
]))
4654 ippAttributeString(current
, buffer
, maxlength
);
4659 fprintf(outfile
, "%*s", (int)-widths
[i
], buffer
);
4661 putc('\n', outfile
);
4665 for (i
= 0; i
< num_displayed
; i
++)
4670 fprintf(outfile
, "%*s", (int)-widths
[i
], displayed
[i
]);
4672 putc('\n', outfile
);
4674 for (i
= 0; i
< num_displayed
; i
++)
4679 memset(buffer
, '-', widths
[i
]);
4680 buffer
[widths
[i
]] = '\0';
4681 fputs(buffer
, outfile
);
4683 putc('\n', outfile
);
4691 * 'print_xml_header()' - Print a standard XML plist header.
4695 print_xml_header(FILE *outfile
) /* I - Output file */
4699 fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", outfile
);
4700 fputs("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
4701 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n", outfile
);
4702 fputs("<plist version=\"1.0\">\n", outfile
);
4703 fputs("<dict>\n", outfile
);
4704 fputs("<key>ipptoolVersion</key>\n", outfile
);
4705 fputs("<string>" CUPS_SVERSION
"</string>\n", outfile
);
4706 fputs("<key>Transfer</key>\n", outfile
);
4707 fprintf(outfile
, "<string>%s</string>\n",
4708 Transfer
== _CUPS_TRANSFER_AUTO
? "auto" :
4709 Transfer
== _CUPS_TRANSFER_CHUNKED
? "chunked" : "length");
4710 fputs("<key>Tests</key>\n", outfile
);
4711 fputs("<array>\n", outfile
);
4719 * 'print_xml_string()' - Print an XML string with escaping.
4723 print_xml_string(FILE *outfile
, /* I - Output file */
4724 const char *element
, /* I - Element name or NULL */
4725 const char *s
) /* I - String to print */
4728 fprintf(outfile
, "<%s>", element
);
4733 fputs("&", outfile
);
4735 fputs("<", outfile
);
4737 fputs(">", outfile
);
4738 else if ((*s
& 0xe0) == 0xc0)
4741 * Validate UTF-8 two-byte sequence...
4744 if ((s
[1] & 0xc0) != 0x80)
4751 putc(*s
++, outfile
);
4755 else if ((*s
& 0xf0) == 0xe0)
4758 * Validate UTF-8 three-byte sequence...
4761 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80)
4768 putc(*s
++, outfile
);
4769 putc(*s
++, outfile
);
4773 else if ((*s
& 0xf8) == 0xf0)
4776 * Validate UTF-8 four-byte sequence...
4779 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80 ||
4780 (s
[3] & 0xc0) != 0x80)
4787 putc(*s
++, outfile
);
4788 putc(*s
++, outfile
);
4789 putc(*s
++, outfile
);
4793 else if ((*s
& 0x80) || (*s
< ' ' && !isspace(*s
& 255)))
4796 * Invalid control character...
4808 fprintf(outfile
, "</%s>\n", element
);
4813 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
4817 print_xml_trailer(FILE *outfile
, /* I - Output file */
4818 int success
, /* I - 1 on success, 0 on failure */
4819 const char *message
) /* I - Error message or NULL */
4823 fputs("</array>\n", outfile
);
4824 fputs("<key>Successful</key>\n", outfile
);
4825 fputs(success
? "<true />\n" : "<false />\n", outfile
);
4828 fputs("<key>ErrorMessage</key>\n", outfile
);
4829 print_xml_string(outfile
, "string", message
);
4831 fputs("</dict>\n", outfile
);
4832 fputs("</plist>\n", outfile
);
4840 * 'set_variable()' - Set a variable value.
4844 set_variable(FILE *outfile
, /* I - Output file */
4845 _cups_vars_t
*vars
, /* I - Variables */
4846 const char *name
, /* I - Variable name */
4847 const char *value
) /* I - Value string */
4849 _cups_var_t key
, /* Search key */
4850 *var
; /* New variable */
4853 if (!_cups_strcasecmp(name
, "filename"))
4856 free(vars
->filename
);
4858 vars
->filename
= strdup(value
);
4862 key
.name
= (char *)name
;
4863 if ((var
= cupsArrayFind(vars
->vars
, &key
)) != NULL
)
4866 var
->value
= strdup(value
);
4868 else if ((var
= malloc(sizeof(_cups_var_t
))) == NULL
)
4870 print_fatal_error(outfile
, "Unable to allocate memory for variable \"%s\".", name
);
4875 var
->name
= strdup(name
);
4876 var
->value
= strdup(value
);
4878 cupsArrayAdd(vars
->vars
, var
);
4885 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
4889 sigterm_handler(int sig
) /* I - Signal number (unused) */
4895 signal(SIGINT
, SIG_DFL
);
4896 signal(SIGTERM
, SIG_DFL
);
4902 * 'timeout_cb()' - Handle HTTP timeouts.
4905 static int /* O - 1 to continue, 0 to cancel */
4906 timeout_cb(http_t
*http
, /* I - Connection to server */
4907 void *user_data
) /* I - User data (unused) */
4909 int buffered
= 0; /* Bytes buffered but not yet sent */
4915 * If the socket still have data waiting to be sent to the printer (as can
4916 * happen if the printer runs out of paper), continue to wait until the output
4917 * buffer is empty...
4920 #ifdef SO_NWRITE /* macOS and some versions of Linux */
4921 socklen_t len
= sizeof(buffered
); /* Size of return value */
4923 if (getsockopt(httpGetFd(http
), SOL_SOCKET
, SO_NWRITE
, &buffered
, &len
))
4926 #elif defined(SIOCOUTQ) /* Others except Windows */
4927 if (ioctl(httpGetFd(http
), SIOCOUTQ
, &buffered
))
4930 #else /* Windows (not possible) */
4932 #endif /* SO_NWRITE */
4934 return (buffered
> 0);
4939 * 'usage()' - Show program usage.
4945 _cupsLangPuts(stderr
, _("Usage: ipptool [options] URI filename [ ... "
4947 _cupsLangPuts(stderr
, _("Options:"));
4948 _cupsLangPuts(stderr
, _(" --help Show help."));
4949 _cupsLangPuts(stderr
, _(" --stop-after-include-error\n"
4950 " Stop tests after a failed INCLUDE."));
4951 _cupsLangPuts(stderr
, _(" --version Show version."));
4952 _cupsLangPuts(stderr
, _(" -4 Connect using IPv4."));
4953 _cupsLangPuts(stderr
, _(" -6 Connect using IPv6."));
4954 _cupsLangPuts(stderr
, _(" -C Send requests using "
4955 "chunking (default)."));
4956 _cupsLangPuts(stdout
, _(" -E Test with HTTP Upgrade to "
4958 _cupsLangPuts(stderr
, _(" -I Ignore errors."));
4959 _cupsLangPuts(stderr
, _(" -L Send requests using "
4960 "content-length."));
4961 _cupsLangPuts(stderr
, _(" -P filename.plist Produce XML plist to a file and test report to standard output."));
4962 _cupsLangPuts(stderr
, _(" -S Test with SSL "
4964 _cupsLangPuts(stderr
, _(" -T seconds Set the receive/send "
4965 "timeout in seconds."));
4966 _cupsLangPuts(stderr
, _(" -V version Set default IPP "
4968 _cupsLangPuts(stderr
, _(" -X Produce XML plist instead "
4970 _cupsLangPuts(stderr
, _(" -c Produce CSV output."));
4971 _cupsLangPuts(stderr
, _(" -d name=value Set named variable to "
4973 _cupsLangPuts(stderr
, _(" -f filename Set default request "
4975 _cupsLangPuts(stderr
, _(" -i seconds Repeat the last file with "
4976 "the given time interval."));
4977 _cupsLangPuts(stderr
, _(" -l Produce plain text output."));
4978 _cupsLangPuts(stderr
, _(" -n count Repeat the last file the "
4979 "given number of times."));
4980 _cupsLangPuts(stderr
, _(" -q Run silently."));
4981 _cupsLangPuts(stderr
, _(" -t Produce a test report."));
4982 _cupsLangPuts(stderr
, _(" -v Be verbose."));
4989 * 'validate_attr()' - Determine whether an attribute is valid.
4992 static int /* O - 1 if valid, 0 otherwise */
4993 validate_attr(FILE *outfile
, /* I - Output file */
4994 cups_array_t
*errors
, /* I - Errors array */
4995 ipp_attribute_t
*attr
) /* I - Attribute to validate */
4997 int i
; /* Looping var */
4998 char scheme
[64], /* Scheme from URI */
4999 userpass
[256], /* Username/password from URI */
5000 hostname
[256], /* Hostname from URI */
5001 resource
[1024]; /* Resource from URI */
5002 int port
, /* Port number from URI */
5003 uri_status
, /* URI separation status */
5004 valid
= 1; /* Is the attribute valid? */
5005 const char *ptr
; /* Pointer into string */
5006 ipp_attribute_t
*colattr
; /* Collection attribute */
5007 regex_t re
; /* Regular expression */
5008 ipp_uchar_t
*date
; /* Current date value */
5019 * Validate the attribute name.
5022 for (ptr
= attr
->name
; *ptr
; ptr
++)
5023 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' && *ptr
!= '_')
5026 if (*ptr
|| ptr
== attr
->name
)
5031 "\"%s\": Bad attribute name - invalid character "
5032 "(RFC 2911 section 4.1.3).", attr
->name
);
5035 if ((ptr
- attr
->name
) > 255)
5040 "\"%s\": Bad attribute name - bad length "
5041 "(RFC 2911 section 4.1.3).", attr
->name
);
5044 switch (attr
->value_tag
)
5046 case IPP_TAG_INTEGER
:
5049 case IPP_TAG_BOOLEAN
:
5050 for (i
= 0; i
< attr
->num_values
; i
++)
5052 if (attr
->values
[i
].boolean
!= 0 &&
5053 attr
->values
[i
].boolean
!= 1)
5058 "\"%s\": Bad boolen value %d "
5059 "(RFC 2911 section 4.1.11).", attr
->name
,
5060 attr
->values
[i
].boolean
);
5066 for (i
= 0; i
< attr
->num_values
; i
++)
5068 if (attr
->values
[i
].integer
< 1)
5073 "\"%s\": Bad enum value %d - out of range "
5074 "(RFC 2911 section 4.1.4).", attr
->name
,
5075 attr
->values
[i
].integer
);
5080 case IPP_TAG_STRING
:
5081 for (i
= 0; i
< attr
->num_values
; i
++)
5083 if (attr
->values
[i
].unknown
.length
> IPP_MAX_OCTETSTRING
)
5088 "\"%s\": Bad octetString value - bad length %d "
5089 "(RFC 2911 section 4.1.10).", attr
->name
,
5090 attr
->values
[i
].unknown
.length
);
5096 for (i
= 0; i
< attr
->num_values
; i
++)
5098 date
= attr
->values
[i
].date
;
5100 if (date
[2] < 1 || date
[2] > 12)
5105 "\"%s\": Bad dateTime month %u "
5106 "(RFC 2911 section 4.1.14).", attr
->name
, date
[2]);
5109 if (date
[3] < 1 || date
[3] > 31)
5114 "\"%s\": Bad dateTime day %u "
5115 "(RFC 2911 section 4.1.14).", attr
->name
, date
[3]);
5123 "\"%s\": Bad dateTime hours %u "
5124 "(RFC 2911 section 4.1.14).", attr
->name
, date
[4]);
5132 "\"%s\": Bad dateTime minutes %u "
5133 "(RFC 2911 section 4.1.14).", attr
->name
, date
[5]);
5141 "\"%s\": Bad dateTime seconds %u "
5142 "(RFC 2911 section 4.1.14).", attr
->name
, date
[6]);
5150 "\"%s\": Bad dateTime deciseconds %u "
5151 "(RFC 2911 section 4.1.14).", attr
->name
, date
[7]);
5154 if (date
[8] != '-' && date
[8] != '+')
5159 "\"%s\": Bad dateTime UTC sign '%c' "
5160 "(RFC 2911 section 4.1.14).", attr
->name
, date
[8]);
5168 "\"%s\": Bad dateTime UTC hours %u "
5169 "(RFC 2911 section 4.1.14).", attr
->name
, date
[9]);
5177 "\"%s\": Bad dateTime UTC minutes %u "
5178 "(RFC 2911 section 4.1.14).", attr
->name
, date
[10]);
5183 case IPP_TAG_RESOLUTION
:
5184 for (i
= 0; i
< attr
->num_values
; i
++)
5186 if (attr
->values
[i
].resolution
.xres
<= 0)
5191 "\"%s\": Bad resolution value %dx%d%s - cross "
5192 "feed resolution must be positive "
5193 "(RFC 2911 section 4.1.15).", attr
->name
,
5194 attr
->values
[i
].resolution
.xres
,
5195 attr
->values
[i
].resolution
.yres
,
5196 attr
->values
[i
].resolution
.units
==
5197 IPP_RES_PER_INCH
? "dpi" :
5198 attr
->values
[i
].resolution
.units
==
5199 IPP_RES_PER_CM
? "dpcm" : "unknown");
5202 if (attr
->values
[i
].resolution
.yres
<= 0)
5207 "\"%s\": Bad resolution value %dx%d%s - feed "
5208 "resolution must be positive "
5209 "(RFC 2911 section 4.1.15).", attr
->name
,
5210 attr
->values
[i
].resolution
.xres
,
5211 attr
->values
[i
].resolution
.yres
,
5212 attr
->values
[i
].resolution
.units
==
5213 IPP_RES_PER_INCH
? "dpi" :
5214 attr
->values
[i
].resolution
.units
==
5215 IPP_RES_PER_CM
? "dpcm" : "unknown");
5218 if (attr
->values
[i
].resolution
.units
!= IPP_RES_PER_INCH
&&
5219 attr
->values
[i
].resolution
.units
!= IPP_RES_PER_CM
)
5224 "\"%s\": Bad resolution value %dx%d%s - bad "
5225 "units value (RFC 2911 section 4.1.15).",
5226 attr
->name
, attr
->values
[i
].resolution
.xres
,
5227 attr
->values
[i
].resolution
.yres
,
5228 attr
->values
[i
].resolution
.units
==
5229 IPP_RES_PER_INCH
? "dpi" :
5230 attr
->values
[i
].resolution
.units
==
5231 IPP_RES_PER_CM
? "dpcm" : "unknown");
5236 case IPP_TAG_RANGE
:
5237 for (i
= 0; i
< attr
->num_values
; i
++)
5239 if (attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
5244 "\"%s\": Bad rangeOfInteger value %d-%d - lower "
5245 "greater than upper (RFC 2911 section 4.1.13).",
5246 attr
->name
, attr
->values
[i
].range
.lower
,
5247 attr
->values
[i
].range
.upper
);
5252 case IPP_TAG_BEGIN_COLLECTION
:
5253 for (i
= 0; i
< attr
->num_values
; i
++)
5255 for (colattr
= attr
->values
[i
].collection
->attrs
;
5257 colattr
= colattr
->next
)
5259 if (!validate_attr(outfile
, NULL
, colattr
))
5266 if (colattr
&& errors
)
5268 add_stringf(errors
, "\"%s\": Bad collection value.", attr
->name
);
5272 validate_attr(outfile
, errors
, colattr
);
5273 colattr
= colattr
->next
;
5280 case IPP_TAG_TEXTLANG
:
5281 for (i
= 0; i
< attr
->num_values
; i
++)
5283 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5285 if ((*ptr
& 0xe0) == 0xc0)
5288 if ((*ptr
& 0xc0) != 0x80)
5291 else if ((*ptr
& 0xf0) == 0xe0)
5294 if ((*ptr
& 0xc0) != 0x80)
5297 if ((*ptr
& 0xc0) != 0x80)
5300 else if ((*ptr
& 0xf8) == 0xf0)
5303 if ((*ptr
& 0xc0) != 0x80)
5306 if ((*ptr
& 0xc0) != 0x80)
5309 if ((*ptr
& 0xc0) != 0x80)
5312 else if (*ptr
& 0x80)
5321 "\"%s\": Bad text value \"%s\" - bad UTF-8 "
5322 "sequence (RFC 2911 section 4.1.1).", attr
->name
,
5323 attr
->values
[i
].string
.text
);
5326 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_TEXT
- 1))
5331 "\"%s\": Bad text value \"%s\" - bad length %d "
5332 "(RFC 2911 section 4.1.1).", attr
->name
,
5333 attr
->values
[i
].string
.text
,
5334 (int)strlen(attr
->values
[i
].string
.text
));
5340 case IPP_TAG_NAMELANG
:
5341 for (i
= 0; i
< attr
->num_values
; i
++)
5343 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5345 if ((*ptr
& 0xe0) == 0xc0)
5348 if ((*ptr
& 0xc0) != 0x80)
5351 else if ((*ptr
& 0xf0) == 0xe0)
5354 if ((*ptr
& 0xc0) != 0x80)
5357 if ((*ptr
& 0xc0) != 0x80)
5360 else if ((*ptr
& 0xf8) == 0xf0)
5363 if ((*ptr
& 0xc0) != 0x80)
5366 if ((*ptr
& 0xc0) != 0x80)
5369 if ((*ptr
& 0xc0) != 0x80)
5372 else if (*ptr
& 0x80)
5381 "\"%s\": Bad name value \"%s\" - bad UTF-8 "
5382 "sequence (RFC 2911 section 4.1.2).", attr
->name
,
5383 attr
->values
[i
].string
.text
);
5386 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_NAME
- 1))
5391 "\"%s\": Bad name value \"%s\" - bad length %d "
5392 "(RFC 2911 section 4.1.2).", attr
->name
,
5393 attr
->values
[i
].string
.text
,
5394 (int)strlen(attr
->values
[i
].string
.text
));
5399 case IPP_TAG_KEYWORD
:
5400 for (i
= 0; i
< attr
->num_values
; i
++)
5402 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5403 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' &&
5407 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5412 "\"%s\": Bad keyword value \"%s\" - invalid "
5413 "character (RFC 2911 section 4.1.3).",
5414 attr
->name
, attr
->values
[i
].string
.text
);
5417 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_KEYWORD
- 1))
5422 "\"%s\": Bad keyword value \"%s\" - bad "
5423 "length %d (RFC 2911 section 4.1.3).",
5424 attr
->name
, attr
->values
[i
].string
.text
,
5425 (int)strlen(attr
->values
[i
].string
.text
));
5431 for (i
= 0; i
< attr
->num_values
; i
++)
5433 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
,
5434 attr
->values
[i
].string
.text
,
5435 scheme
, sizeof(scheme
),
5436 userpass
, sizeof(userpass
),
5437 hostname
, sizeof(hostname
),
5438 &port
, resource
, sizeof(resource
));
5440 if (uri_status
< HTTP_URI_OK
)
5445 "\"%s\": Bad URI value \"%s\" - %s "
5446 "(RFC 2911 section 4.1.5).", attr
->name
,
5447 attr
->values
[i
].string
.text
,
5448 httpURIStatusString(uri_status
));
5451 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_URI
- 1))
5456 "\"%s\": Bad URI value \"%s\" - bad length %d "
5457 "(RFC 2911 section 4.1.5).", attr
->name
,
5458 attr
->values
[i
].string
.text
,
5459 (int)strlen(attr
->values
[i
].string
.text
));
5464 case IPP_TAG_URISCHEME
:
5465 for (i
= 0; i
< attr
->num_values
; i
++)
5467 ptr
= attr
->values
[i
].string
.text
;
5468 if (islower(*ptr
& 255))
5470 for (ptr
++; *ptr
; ptr
++)
5471 if (!islower(*ptr
& 255) && !isdigit(*ptr
& 255) &&
5472 *ptr
!= '+' && *ptr
!= '-' && *ptr
!= '.')
5476 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5481 "\"%s\": Bad uriScheme value \"%s\" - bad "
5482 "characters (RFC 2911 section 4.1.6).",
5483 attr
->name
, attr
->values
[i
].string
.text
);
5486 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_URISCHEME
- 1))
5491 "\"%s\": Bad uriScheme value \"%s\" - bad "
5492 "length %d (RFC 2911 section 4.1.6).",
5493 attr
->name
, attr
->values
[i
].string
.text
,
5494 (int)strlen(attr
->values
[i
].string
.text
));
5499 case IPP_TAG_CHARSET
:
5500 for (i
= 0; i
< attr
->num_values
; i
++)
5502 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5503 if (!isprint(*ptr
& 255) || isupper(*ptr
& 255) ||
5504 isspace(*ptr
& 255))
5507 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5512 "\"%s\": Bad charset value \"%s\" - bad "
5513 "characters (RFC 2911 section 4.1.7).",
5514 attr
->name
, attr
->values
[i
].string
.text
);
5517 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_CHARSET
- 1))
5522 "\"%s\": Bad charset value \"%s\" - bad "
5523 "length %d (RFC 2911 section 4.1.7).",
5524 attr
->name
, attr
->values
[i
].string
.text
,
5525 (int)strlen(attr
->values
[i
].string
.text
));
5530 case IPP_TAG_LANGUAGE
:
5532 * The following regular expression is derived from the ABNF for
5533 * language tags in RFC 4646. All I can say is that this is the
5534 * easiest way to check the values...
5537 if ((i
= regcomp(&re
,
5539 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
5541 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
5542 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
5543 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
5544 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
5545 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
5547 "x(-[a-z0-9]{1,8})+" /* privateuse */
5549 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
5551 REG_NOSUB
| REG_EXTENDED
)) != 0)
5553 char temp
[256]; /* Temporary error string */
5555 regerror(i
, &re
, temp
, sizeof(temp
));
5556 print_fatal_error(outfile
, "Unable to compile naturalLanguage regular "
5557 "expression: %s.", temp
);
5561 for (i
= 0; i
< attr
->num_values
; i
++)
5563 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5568 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5569 "characters (RFC 2911 section 4.1.8).",
5570 attr
->name
, attr
->values
[i
].string
.text
);
5573 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_LANGUAGE
- 1))
5578 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5579 "length %d (RFC 2911 section 4.1.8).",
5580 attr
->name
, attr
->values
[i
].string
.text
,
5581 (int)strlen(attr
->values
[i
].string
.text
));
5588 case IPP_TAG_MIMETYPE
:
5590 * The following regular expression is derived from the ABNF for
5591 * language tags in RFC 2045 and 4288. All I can say is that this is
5592 * the easiest way to check the values...
5595 if ((i
= regcomp(&re
,
5597 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
5599 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
5600 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
5601 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
5604 REG_NOSUB
| REG_EXTENDED
)) != 0)
5606 char temp
[256]; /* Temporary error string */
5608 regerror(i
, &re
, temp
, sizeof(temp
));
5609 print_fatal_error(outfile
, "Unable to compile mimeMediaType regular "
5610 "expression: %s.", temp
);
5614 for (i
= 0; i
< attr
->num_values
; i
++)
5616 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5621 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5622 "characters (RFC 2911 section 4.1.9).",
5623 attr
->name
, attr
->values
[i
].string
.text
);
5626 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_MIMETYPE
- 1))
5631 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5632 "length %d (RFC 2911 section 4.1.9).",
5633 attr
->name
, attr
->values
[i
].string
.text
,
5634 (int)strlen(attr
->values
[i
].string
.text
));
5650 * 'with_value()' - Test a WITH-VALUE predicate.
5653 static int /* O - 1 on match, 0 on non-match */
5654 with_value(FILE *outfile
, /* I - Output file */
5655 cups_array_t
*errors
, /* I - Errors array */
5656 char *value
, /* I - Value string */
5657 int flags
, /* I - Flags for match */
5658 ipp_attribute_t
*attr
, /* I - Attribute to compare */
5659 char *matchbuf
, /* I - Buffer to hold matching value */
5660 size_t matchlen
) /* I - Length of match buffer */
5662 int i
, /* Looping var */
5664 char temp
[1024], /* Temporary value string */
5665 *valptr
; /* Pointer into value */
5669 match
= (flags
& _CUPS_WITH_ALL
) ? 1 : 0;
5672 * NULL matches everything.
5675 if (!value
|| !*value
)
5679 * Compare the value string to the attribute value.
5682 switch (attr
->value_tag
)
5684 case IPP_TAG_INTEGER
:
5686 for (i
= 0; i
< attr
->num_values
; i
++)
5688 char op
, /* Comparison operator */
5689 *nextptr
; /* Next pointer */
5690 int intvalue
, /* Integer value */
5691 valmatch
= 0; /* Does the current value match? */
5695 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5696 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5697 *valptr
== '=' || *valptr
== '>')
5700 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5702 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5710 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
5711 if (nextptr
== valptr
)
5715 if ((op
== '=' && attr
->values
[i
].integer
== intvalue
) ||
5716 (op
== '<' && attr
->values
[i
].integer
< intvalue
) ||
5717 (op
== '>' && attr
->values
[i
].integer
> intvalue
))
5720 snprintf(matchbuf
, matchlen
, "%d", attr
->values
[i
].integer
);
5727 if (flags
& _CUPS_WITH_ALL
)
5742 if (!match
&& errors
)
5744 for (i
= 0; i
< attr
->num_values
; i
++)
5745 add_stringf(errors
, "GOT: %s=%d", attr
->name
,
5746 attr
->values
[i
].integer
);
5750 case IPP_TAG_RANGE
:
5751 for (i
= 0; i
< attr
->num_values
; i
++)
5753 char op
, /* Comparison operator */
5754 *nextptr
; /* Next pointer */
5755 int intvalue
, /* Integer value */
5756 valmatch
= 0; /* Does the current value match? */
5760 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5761 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5762 *valptr
== '=' || *valptr
== '>')
5765 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5767 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5775 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
5776 if (nextptr
== valptr
)
5780 if ((op
== '=' && (attr
->values
[i
].range
.lower
== intvalue
||
5781 attr
->values
[i
].range
.upper
== intvalue
)) ||
5782 (op
== '<' && attr
->values
[i
].range
.upper
< intvalue
) ||
5783 (op
== '>' && attr
->values
[i
].range
.upper
> intvalue
))
5786 snprintf(matchbuf
, matchlen
, "%d-%d",
5787 attr
->values
[0].range
.lower
,
5788 attr
->values
[0].range
.upper
);
5795 if (flags
& _CUPS_WITH_ALL
)
5810 if (!match
&& errors
)
5812 for (i
= 0; i
< attr
->num_values
; i
++)
5813 add_stringf(errors
, "GOT: %s=%d-%d", attr
->name
,
5814 attr
->values
[i
].range
.lower
,
5815 attr
->values
[i
].range
.upper
);
5819 case IPP_TAG_BOOLEAN
:
5820 for (i
= 0; i
< attr
->num_values
; i
++)
5822 if ((!strcmp(value
, "true")) == attr
->values
[i
].boolean
)
5825 strlcpy(matchbuf
, value
, matchlen
);
5827 if (!(flags
& _CUPS_WITH_ALL
))
5833 else if (flags
& _CUPS_WITH_ALL
)
5840 if (!match
&& errors
)
5842 for (i
= 0; i
< attr
->num_values
; i
++)
5843 add_stringf(errors
, "GOT: %s=%s", attr
->name
,
5844 attr
->values
[i
].boolean
? "true" : "false");
5848 case IPP_TAG_RESOLUTION
:
5849 for (i
= 0; i
< attr
->num_values
; i
++)
5851 if (attr
->values
[i
].resolution
.xres
==
5852 attr
->values
[i
].resolution
.yres
)
5853 snprintf(temp
, sizeof(temp
), "%d%s",
5854 attr
->values
[i
].resolution
.xres
,
5855 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5858 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5859 attr
->values
[i
].resolution
.xres
,
5860 attr
->values
[i
].resolution
.yres
,
5861 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5864 if (!strcmp(value
, temp
))
5867 strlcpy(matchbuf
, value
, matchlen
);
5869 if (!(flags
& _CUPS_WITH_ALL
))
5875 else if (flags
& _CUPS_WITH_ALL
)
5882 if (!match
&& errors
)
5884 for (i
= 0; i
< attr
->num_values
; i
++)
5886 if (attr
->values
[i
].resolution
.xres
==
5887 attr
->values
[i
].resolution
.yres
)
5888 snprintf(temp
, sizeof(temp
), "%d%s",
5889 attr
->values
[i
].resolution
.xres
,
5890 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5893 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5894 attr
->values
[i
].resolution
.xres
,
5895 attr
->values
[i
].resolution
.yres
,
5896 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5899 if (strcmp(value
, temp
))
5900 add_stringf(errors
, "GOT: %s=%s", attr
->name
, temp
);
5905 case IPP_TAG_NOVALUE
:
5906 case IPP_TAG_UNKNOWN
:
5909 case IPP_TAG_CHARSET
:
5910 case IPP_TAG_KEYWORD
:
5911 case IPP_TAG_LANGUAGE
:
5912 case IPP_TAG_MIMETYPE
:
5914 case IPP_TAG_NAMELANG
:
5916 case IPP_TAG_TEXTLANG
:
5918 case IPP_TAG_URISCHEME
:
5919 if (flags
& _CUPS_WITH_REGEX
)
5922 * Value is an extended, case-sensitive POSIX regular expression...
5925 regex_t re
; /* Regular expression */
5927 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
5929 regerror(i
, &re
, temp
, sizeof(temp
));
5931 print_fatal_error(outfile
, "Unable to compile WITH-VALUE regular expression "
5932 "\"%s\" - %s", value
, temp
);
5937 * See if ALL of the values match the given regular expression.
5940 for (i
= 0; i
< attr
->num_values
; i
++)
5942 if (!regexec(&re
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5947 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5950 if (!(flags
& _CUPS_WITH_ALL
))
5956 else if (flags
& _CUPS_WITH_ALL
)
5965 else if (ippGetValueTag(attr
) == IPP_TAG_URI
)
5967 if (!strncmp(value
, "ipp://", 6) || !strncmp(value
, "http://", 7) || !strncmp(value
, "ipps://", 7) || !strncmp(value
, "https://", 8))
5969 char scheme
[256], /* URI scheme */
5970 userpass
[256], /* username:password, if any */
5971 hostname
[256], /* hostname */
5972 *hostptr
, /* Pointer into hostname */
5973 resource
[1024]; /* Resource path */
5974 int port
; /* Port number */
5976 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
)) >= HTTP_URI_STATUS_OK
&& (hostptr
= hostname
+ strlen(hostname
) - 1) > hostname
&& *hostptr
== '.')
5979 * Strip trailing "." in hostname of URI...
5983 httpAssembleURI(HTTP_URI_CODING_ALL
, temp
, sizeof(temp
), scheme
, userpass
, hostname
, port
, resource
);
5989 * Value is a literal URI string, see if the value(s) match...
5992 for (i
= 0; i
< attr
->num_values
; i
++)
5994 if (!strcmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
))))
5998 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
6001 if (!(flags
& _CUPS_WITH_ALL
))
6007 else if (flags
& _CUPS_WITH_ALL
)
6017 * Value is a literal string, see if the value(s) match...
6020 for (i
= 0; i
< attr
->num_values
; i
++)
6022 if (!strcmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
))))
6026 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
6029 if (!(flags
& _CUPS_WITH_ALL
))
6035 else if (flags
& _CUPS_WITH_ALL
)
6043 if (!match
&& errors
)
6045 for (i
= 0; i
< attr
->num_values
; i
++)
6046 add_stringf(errors
, "GOT: %s=\"%s\"", attr
->name
,
6047 attr
->values
[i
].string
.text
);
6060 * 'with_value_from()' - Test a WITH-VALUE-FROM predicate.
6063 static int /* O - 1 on match, 0 on non-match */
6065 cups_array_t
*errors
, /* I - Errors array */
6066 ipp_attribute_t
*fromattr
, /* I - "From" attribute */
6067 ipp_attribute_t
*attr
, /* I - Attribute to compare */
6068 char *matchbuf
, /* I - Buffer to hold matching value */
6069 size_t matchlen
) /* I - Length of match buffer */
6071 int i
, j
, /* Looping vars */
6072 count
= ippGetCount(attr
), /* Number of attribute values */
6073 match
= 1; /* Match? */
6079 * Compare the from value(s) to the attribute value(s)...
6082 switch (ippGetValueTag(attr
))
6084 case IPP_TAG_INTEGER
:
6085 if (ippGetValueTag(fromattr
) != IPP_TAG_INTEGER
&& ippGetValueTag(fromattr
) != IPP_TAG_RANGE
)
6086 goto wrong_value_tag
;
6088 for (i
= 0; i
< count
; i
++)
6090 int value
= ippGetInteger(attr
, i
);
6091 /* Current integer value */
6093 if (ippContainsInteger(fromattr
, value
))
6096 snprintf(matchbuf
, matchlen
, "%d", value
);
6100 add_stringf(errors
, "GOT: %s=%d", ippGetName(attr
), value
);
6107 if (ippGetValueTag(fromattr
) != IPP_TAG_ENUM
)
6108 goto wrong_value_tag
;
6110 for (i
= 0; i
< count
; i
++)
6112 int value
= ippGetInteger(attr
, i
);
6113 /* Current integer value */
6115 if (ippContainsInteger(fromattr
, value
))
6118 snprintf(matchbuf
, matchlen
, "%d", value
);
6122 add_stringf(errors
, "GOT: %s=%d", ippGetName(attr
), value
);
6128 case IPP_TAG_RESOLUTION
:
6129 if (ippGetValueTag(fromattr
) != IPP_TAG_RESOLUTION
)
6130 goto wrong_value_tag
;
6132 for (i
= 0; i
< count
; i
++)
6136 int fromcount
= ippGetCount(fromattr
);
6137 int fromxres
, fromyres
;
6138 ipp_res_t fromunits
;
6140 xres
= ippGetResolution(attr
, i
, &yres
, &units
);
6142 for (j
= 0; j
< fromcount
; j
++)
6144 fromxres
= ippGetResolution(fromattr
, j
, &fromyres
, &fromunits
);
6145 if (fromxres
== xres
&& fromyres
== yres
&& fromunits
== units
)
6154 snprintf(matchbuf
, matchlen
, "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6156 snprintf(matchbuf
, matchlen
, "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6162 add_stringf(errors
, "GOT: %s=%d%s", ippGetName(attr
), xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6164 add_stringf(errors
, "GOT: %s=%dx%d%s", ippGetName(attr
), xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6171 case IPP_TAG_NOVALUE
:
6172 case IPP_TAG_UNKNOWN
:
6175 case IPP_TAG_CHARSET
:
6176 case IPP_TAG_KEYWORD
:
6177 case IPP_TAG_LANGUAGE
:
6178 case IPP_TAG_MIMETYPE
:
6180 case IPP_TAG_NAMELANG
:
6182 case IPP_TAG_TEXTLANG
:
6184 case IPP_TAG_URISCHEME
:
6185 for (i
= 0; i
< count
; i
++)
6187 const char *value
= ippGetString(attr
, i
, NULL
);
6188 /* Current string value */
6190 if (ippContainsString(fromattr
, value
))
6193 strlcpy(matchbuf
, value
, matchlen
);
6197 add_stringf(errors
, "GOT: %s='%s'", ippGetName(attr
), value
);
6210 /* value tag mismatch between fromattr and attr */
6213 add_stringf(errors
, "GOT: %s OF-TYPE %s", ippGetName(attr
), ippTagString(ippGetValueTag(attr
)));