2 * "$Id: ipptool.c 12952 2015-10-28 17:22:39Z msweet $"
4 * ipptool command for CUPS.
6 * Copyright 2007-2015 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
15 * This file is subject to the Apple OS-Developed Software exception.
19 * Include necessary headers...
22 #include <cups/cups-private.h>
23 #include <cups/file-private.h>
37 #endif /* !O_BINARY */
44 typedef enum _cups_transfer_e
/**** How to send request data ****/
46 _CUPS_TRANSFER_AUTO
, /* Chunk for files, length for static */
47 _CUPS_TRANSFER_CHUNKED
, /* Chunk always */
48 _CUPS_TRANSFER_LENGTH
/* Length always */
51 typedef enum _cups_output_e
/**** Output mode ****/
53 _CUPS_OUTPUT_QUIET
, /* No output */
54 _CUPS_OUTPUT_TEST
, /* Traditional CUPS test output */
55 _CUPS_OUTPUT_PLIST
, /* XML plist test output */
56 _CUPS_OUTPUT_LIST
, /* Tabular list output */
57 _CUPS_OUTPUT_CSV
/* Comma-separated values output */
60 typedef enum _cups_with_e
/**** WITH flags ****/
62 _CUPS_WITH_LITERAL
= 0, /* Match string is a literal value */
63 _CUPS_WITH_ALL
= 1, /* Must match all values */
64 _CUPS_WITH_REGEX
= 2, /* Match string is a regular expression */
65 _CUPS_WITH_HOSTNAME
= 4, /* Match string is a URI hostname */
66 _CUPS_WITH_RESOURCE
= 8, /* Match string is a URI resource */
67 _CUPS_WITH_SCHEME
= 16 /* Match string is a URI scheme */
70 typedef struct _cups_expect_s
/**** Expected attribute info ****/
72 int optional
, /* Optional attribute? */
73 not_expect
, /* Don't expect attribute? */
74 expect_all
; /* Expect all attributes to match/not match */
75 char *name
, /* Attribute name */
76 *of_type
, /* Type name */
77 *same_count_as
, /* Parallel attribute name */
78 *if_defined
, /* Only required if variable defined */
79 *if_not_defined
, /* Only required if variable is not defined */
80 *with_value
, /* Attribute must include this value */
81 *with_value_from
, /* Attribute must have one of the values in this attribute */
82 *define_match
, /* Variable to define on match */
83 *define_no_match
, /* Variable to define on no-match */
84 *define_value
; /* Variable to define with value */
85 int repeat_limit
, /* Maximum number of times to repeat */
86 repeat_match
, /* Repeat test on match */
87 repeat_no_match
, /* Repeat test on no match */
88 with_flags
, /* WITH flags */
89 count
; /* Expected count if > 0 */
90 ipp_tag_t in_group
; /* IN-GROUP value */
93 typedef struct _cups_status_s
/**** Status info ****/
95 ipp_status_t status
; /* Expected status code */
96 char *if_defined
, /* Only if variable is defined */
97 *if_not_defined
, /* Only if variable is not defined */
98 *define_match
, /* Variable to define on match */
99 *define_no_match
, /* Variable to define on no-match */
100 *define_value
; /* Variable to define with value */
101 int repeat_limit
, /* Maximum number of times to repeat */
102 repeat_match
, /* Repeat the test when it does not match */
103 repeat_no_match
; /* Repeat the test when it matches */
106 typedef struct _cups_var_s
/**** Variable ****/
108 char *name
, /* Name of variable */
109 *value
; /* Value of variable */
112 typedef struct _cups_vars_s
/**** Set of variables ****/
114 char *uri
, /* URI for printer */
115 *filename
, /* Filename */
116 scheme
[64], /* Scheme from URI */
117 userpass
[256], /* Username/password from URI */
118 hostname
[256], /* Hostname from URI */
119 resource
[1024]; /* Resource path from URI */
120 int port
; /* Port number from URI */
121 http_encryption_t encryption
; /* Encryption for connection? */
122 double timeout
; /* Timeout for connection */
123 int family
; /* Address family */
124 cups_array_t
*vars
; /* Array of variables */
132 static _cups_transfer_t Transfer
= _CUPS_TRANSFER_AUTO
;
133 /* How to transfer requests */
134 static _cups_output_t Output
= _CUPS_OUTPUT_LIST
;
136 static int Cancel
= 0, /* Cancel test? */
137 IgnoreErrors
= 0, /* Ignore errors? */
138 StopAfterIncludeError
= 0,
139 /* Stop after include errors? */
140 Verbosity
= 0, /* Show all attributes? */
141 Version
= 11, /* Default IPP version */
142 XMLHeader
= 0, /* 1 if header is written */
143 TestCount
= 0, /* Number of tests run */
144 PassCount
= 0, /* Number of passing tests */
145 FailCount
= 0, /* Number of failing tests */
146 SkipCount
= 0; /* Number of skipped tests */
147 static char *Username
= NULL
, /* Username from URI */
148 *Password
= NULL
; /* Password from URI */
149 static int PasswordTries
= 0; /* Number of tries with password */
156 static void add_stringf(cups_array_t
*a
, const char *s
, ...) __attribute__ ((__format__ (__printf__
, 2, 3)));
157 static int compare_vars(_cups_var_t
*a
, _cups_var_t
*b
);
158 static int do_tests(FILE *outfile
, _cups_vars_t
*vars
, const char *testfile
);
159 static void expand_variables(_cups_vars_t
*vars
, char *dst
, const char *src
, size_t dstsize
) __attribute__((nonnull(1,2,3)));
160 static int expect_matches(_cups_expect_t
*expect
, ipp_tag_t value_tag
);
161 static ipp_t
*get_collection(FILE *outfile
, _cups_vars_t
*vars
, FILE *fp
, int *linenum
);
162 static char *get_filename(const char *testfile
, char *dst
, const char *src
, size_t dstsize
);
163 static const char *get_string(ipp_attribute_t
*attr
, int element
, int flags
, char *buffer
, size_t bufsize
);
164 static char *get_token(FILE *fp
, char *buf
, int buflen
, int *linenum
);
165 static char *get_variable(_cups_vars_t
*vars
, const char *name
);
166 static char *iso_date(ipp_uchar_t
*date
);
167 static const char *password_cb(const char *prompt
);
168 static void pause_message(const char *message
);
169 static void print_attr(FILE *outfile
, int format
, ipp_attribute_t
*attr
, ipp_tag_t
*group
);
170 static void print_csv(FILE *outfile
, ipp_attribute_t
*attr
, int num_displayed
, char **displayed
, size_t *widths
);
171 static void print_fatal_error(FILE *outfile
, const char *s
, ...) __attribute__ ((__format__ (__printf__
, 2, 3)));
172 static void print_line(FILE *outfile
, ipp_attribute_t
*attr
, int num_displayed
, char **displayed
, size_t *widths
);
173 static void print_xml_header(FILE *outfile
);
174 static void print_xml_string(FILE *outfile
, const char *element
, const char *s
);
175 static void print_xml_trailer(FILE *outfile
, int success
, const char *message
);
176 static void set_variable(FILE *outfile
, _cups_vars_t
*vars
, const char *name
, const char *value
);
178 static void sigterm_handler(int sig
);
180 static int timeout_cb(http_t
*http
, void *user_data
);
181 static void usage(void) __attribute__((noreturn
));
182 static int validate_attr(FILE *outfile
, cups_array_t
*errors
, ipp_attribute_t
*attr
);
183 static int with_value(FILE *outfile
, cups_array_t
*errors
, char *value
, int flags
, ipp_attribute_t
*attr
, char *matchbuf
, size_t matchlen
);
184 static int with_value_from(cups_array_t
*errors
, ipp_attribute_t
*fromattr
, ipp_attribute_t
*attr
, char *matchbuf
, size_t matchlen
);
188 * 'main()' - Parse options and do tests.
191 int /* O - Exit status */
192 main(int argc
, /* I - Number of command-line args */
193 char *argv
[]) /* I - Command-line arguments */
195 int i
; /* Looping var */
196 int status
; /* Status of tests... */
197 FILE *outfile
= stdout
;
199 char *opt
, /* Current option */
200 name
[1024], /* Name/value buffer */
201 *value
, /* Pointer to value */
202 filename
[1024], /* Real filename */
203 testname
[1024], /* Real test filename */
204 uri
[1024]; /* Copy of printer URI */
205 const char *ext
, /* Extension on filename */
206 *testfile
; /* Test file to use */
207 int interval
, /* Test interval in microseconds */
208 repeat
; /* Repeat count */
209 _cups_vars_t vars
; /* Variables */
210 http_uri_status_t uri_status
; /* URI separation status */
211 _cups_globals_t
*cg
= _cupsGlobals();
217 * Catch SIGINT and SIGTERM...
220 signal(SIGINT
, sigterm_handler
);
221 signal(SIGTERM
, sigterm_handler
);
225 * Initialize the locale and variables...
228 _cupsSetLocale(argv
);
230 memset(&vars
, 0, sizeof(vars
));
231 vars
.family
= AF_UNSPEC
;
232 vars
.vars
= cupsArrayNew((cups_array_func_t
)compare_vars
, NULL
);
237 * ipptool URI testfile
245 for (i
= 1; i
< argc
; i
++)
247 if (!strcmp(argv
[i
], "--help"))
251 else if (!strcmp(argv
[i
], "--stop-after-include-error"))
253 StopAfterIncludeError
= 1;
255 else if (!strcmp(argv
[i
], "--version"))
260 else if (argv
[i
][0] == '-')
262 for (opt
= argv
[i
] + 1; *opt
; opt
++)
266 case '4' : /* Connect using IPv4 only */
267 vars
.family
= AF_INET
;
271 case '6' : /* Connect using IPv6 only */
272 vars
.family
= AF_INET6
;
274 #endif /* AF_INET6 */
276 case 'C' : /* Enable HTTP chunking */
277 Transfer
= _CUPS_TRANSFER_CHUNKED
;
280 case 'E' : /* Encrypt with TLS */
282 vars
.encryption
= HTTP_ENCRYPT_REQUIRED
;
284 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
286 #endif /* HAVE_SSL */
289 case 'I' : /* Ignore errors */
293 case 'L' : /* Disable HTTP chunking */
294 Transfer
= _CUPS_TRANSFER_LENGTH
;
297 case 'P' : /* Output to plist file */
302 _cupsLangPrintf(stderr
, _("%s: Missing filename for \"-P\"."), "ipptool");
306 if (outfile
!= stdout
)
309 if ((outfile
= fopen(argv
[i
], "w")) == NULL
)
311 _cupsLangPrintf(stderr
, _("%s: Unable to open \"%s\": %s"), "ipptool", argv
[i
], strerror(errno
));
315 Output
= _CUPS_OUTPUT_PLIST
;
317 if (interval
|| repeat
)
319 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
324 case 'S' : /* Encrypt with SSL */
326 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
328 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
330 #endif /* HAVE_SSL */
333 case 'T' : /* Set timeout */
338 _cupsLangPrintf(stderr
,
339 _("%s: Missing timeout for \"-T\"."),
344 vars
.timeout
= _cupsStrScand(argv
[i
], NULL
, localeconv());
347 case 'V' : /* Set IPP version */
352 _cupsLangPrintf(stderr
,
353 _("%s: Missing version for \"-V\"."),
358 if (!strcmp(argv
[i
], "1.0"))
360 else if (!strcmp(argv
[i
], "1.1"))
362 else if (!strcmp(argv
[i
], "2.0"))
364 else if (!strcmp(argv
[i
], "2.1"))
366 else if (!strcmp(argv
[i
], "2.2"))
370 _cupsLangPrintf(stderr
,
371 _("%s: Bad version %s for \"-V\"."),
377 case 'X' : /* Produce XML output */
378 Output
= _CUPS_OUTPUT_PLIST
;
380 if (interval
|| repeat
)
382 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
387 case 'c' : /* CSV output */
388 Output
= _CUPS_OUTPUT_CSV
;
391 case 'd' : /* Define a variable */
396 _cupsLangPuts(stderr
,
397 _("ipptool: Missing name=value for \"-d\"."));
401 strlcpy(name
, argv
[i
], sizeof(name
));
402 if ((value
= strchr(name
, '=')) != NULL
)
405 value
= name
+ strlen(name
);
407 set_variable(outfile
, &vars
, name
, value
);
410 case 'f' : /* Set the default test filename */
415 _cupsLangPuts(stderr
,
416 _("ipptool: Missing filename for \"-f\"."));
423 vars
.filename
= NULL
;
426 if (access(argv
[i
], 0))
432 snprintf(filename
, sizeof(filename
), "%s.gz", argv
[i
]);
433 if (access(filename
, 0) && filename
[0] != '/'
435 && (!isalpha(filename
[0] & 255) || filename
[1] != ':')
439 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s",
440 cg
->cups_datadir
, argv
[i
]);
441 if (access(filename
, 0))
443 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s.gz",
444 cg
->cups_datadir
, argv
[i
]);
445 if (access(filename
, 0))
446 vars
.filename
= strdup(argv
[i
]);
448 vars
.filename
= strdup(filename
);
451 vars
.filename
= strdup(filename
);
454 vars
.filename
= strdup(filename
);
457 vars
.filename
= strdup(argv
[i
]);
459 if ((ext
= strrchr(vars
.filename
, '.')) != NULL
)
462 * Guess the MIME media type based on the extension...
465 if (!_cups_strcasecmp(ext
, ".gif"))
466 set_variable(outfile
, &vars
, "filetype", "image/gif");
467 else if (!_cups_strcasecmp(ext
, ".htm") ||
468 !_cups_strcasecmp(ext
, ".htm.gz") ||
469 !_cups_strcasecmp(ext
, ".html") ||
470 !_cups_strcasecmp(ext
, ".html.gz"))
471 set_variable(outfile
, &vars
, "filetype", "text/html");
472 else if (!_cups_strcasecmp(ext
, ".jpg") ||
473 !_cups_strcasecmp(ext
, ".jpeg"))
474 set_variable(outfile
, &vars
, "filetype", "image/jpeg");
475 else if (!_cups_strcasecmp(ext
, ".pcl") ||
476 !_cups_strcasecmp(ext
, ".pcl.gz"))
477 set_variable(outfile
, &vars
, "filetype", "application/vnd.hp-PCL");
478 else if (!_cups_strcasecmp(ext
, ".pdf"))
479 set_variable(outfile
, &vars
, "filetype", "application/pdf");
480 else if (!_cups_strcasecmp(ext
, ".png"))
481 set_variable(outfile
, &vars
, "filetype", "image/png");
482 else if (!_cups_strcasecmp(ext
, ".ps") ||
483 !_cups_strcasecmp(ext
, ".ps.gz"))
484 set_variable(outfile
, &vars
, "filetype", "application/postscript");
485 else if (!_cups_strcasecmp(ext
, ".pwg") ||
486 !_cups_strcasecmp(ext
, ".pwg.gz") ||
487 !_cups_strcasecmp(ext
, ".ras") ||
488 !_cups_strcasecmp(ext
, ".ras.gz"))
489 set_variable(outfile
, &vars
, "filetype", "image/pwg-raster");
490 else if (!_cups_strcasecmp(ext
, ".tif") ||
491 !_cups_strcasecmp(ext
, ".tiff"))
492 set_variable(outfile
, &vars
, "filetype", "image/tiff");
493 else if (!_cups_strcasecmp(ext
, ".txt") ||
494 !_cups_strcasecmp(ext
, ".txt.gz"))
495 set_variable(outfile
, &vars
, "filetype", "text/plain");
496 else if (!_cups_strcasecmp(ext
, ".urf") ||
497 !_cups_strcasecmp(ext
, ".urf.gz"))
498 set_variable(outfile
, &vars
, "filetype", "image/urf");
499 else if (!_cups_strcasecmp(ext
, ".xps"))
500 set_variable(outfile
, &vars
, "filetype", "application/openxps");
502 set_variable(outfile
, &vars
, "filetype", "application/octet-stream");
507 * Use the "auto-type" MIME media type...
510 set_variable(outfile
, &vars
, "filetype", "application/octet-stream");
514 case 'i' : /* Test every N seconds */
519 _cupsLangPuts(stderr
,
520 _("ipptool: Missing seconds for \"-i\"."));
525 interval
= (int)(_cupsStrScand(argv
[i
], NULL
, localeconv()) *
529 _cupsLangPuts(stderr
,
530 _("ipptool: Invalid seconds for \"-i\"."));
535 if (Output
== _CUPS_OUTPUT_PLIST
&& interval
)
537 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
542 case 'l' : /* List as a table */
543 Output
= _CUPS_OUTPUT_LIST
;
546 case 'n' : /* Repeat count */
551 _cupsLangPuts(stderr
,
552 _("ipptool: Missing count for \"-n\"."));
556 repeat
= atoi(argv
[i
]);
558 if (Output
== _CUPS_OUTPUT_PLIST
&& repeat
)
560 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
565 case 'q' : /* Be quiet */
566 Output
= _CUPS_OUTPUT_QUIET
;
569 case 't' : /* CUPS test output */
570 Output
= _CUPS_OUTPUT_TEST
;
573 case 'v' : /* Be verbose */
578 _cupsLangPrintf(stderr
, _("ipptool: Unknown option \"-%c\"."),
584 else if (!strncmp(argv
[i
], "ipp://", 6) || !strncmp(argv
[i
], "http://", 7)
586 || !strncmp(argv
[i
], "ipps://", 7)
587 || !strncmp(argv
[i
], "https://", 8)
588 #endif /* HAVE_SSL */
597 _cupsLangPuts(stderr
, _("ipptool: May only specify a single URI."));
602 if (!strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8))
603 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
604 #endif /* HAVE_SSL */
607 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, vars
.uri
,
608 vars
.scheme
, sizeof(vars
.scheme
),
609 vars
.userpass
, sizeof(vars
.userpass
),
610 vars
.hostname
, sizeof(vars
.hostname
),
612 vars
.resource
, sizeof(vars
.resource
));
614 if (uri_status
!= HTTP_URI_OK
)
616 _cupsLangPrintf(stderr
, _("ipptool: Bad URI - %s."), httpURIStatusString(uri_status
));
620 if (vars
.userpass
[0])
622 if ((Password
= strchr(vars
.userpass
, ':')) != NULL
)
625 Username
= vars
.userpass
;
626 cupsSetPasswordCB(password_cb
);
627 set_variable(outfile
, &vars
, "uriuser", vars
.userpass
);
630 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), vars
.scheme
, NULL
,
631 vars
.hostname
, vars
.port
, vars
.resource
);
642 _cupsLangPuts(stderr
, _("ipptool: URI required before test file."));
643 _cupsLangPuts(stderr
, argv
[i
]);
647 if (access(argv
[i
], 0) && argv
[i
][0] != '/'
649 && (!isalpha(argv
[i
][0] & 255) || argv
[i
][1] != ':')
653 snprintf(testname
, sizeof(testname
), "%s/ipptool/%s", cg
->cups_datadir
,
655 if (access(testname
, 0))
663 if (!do_tests(outfile
, &vars
, testfile
))
668 if (!vars
.uri
|| !testfile
)
672 * Loop if the interval is set...
675 if (Output
== _CUPS_OUTPUT_PLIST
)
676 print_xml_trailer(outfile
, !status
, NULL
);
677 else if (interval
> 0 && repeat
> 0)
681 usleep((useconds_t
)interval
);
682 do_tests(outfile
, &vars
, testfile
);
686 else if (interval
> 0)
690 usleep((useconds_t
)interval
);
691 do_tests(outfile
, &vars
, testfile
);
695 if ((Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
)) && TestCount
> 1)
698 * Show a summary report if there were multiple tests...
701 printf("\nSummary: %d tests, %d passed, %d failed, %d skipped\n"
702 "Score: %d%%\n", TestCount
, PassCount
, FailCount
, SkipCount
,
703 100 * (PassCount
+ SkipCount
) / TestCount
);
715 * 'add_stringf()' - Add a formatted string to an array.
719 add_stringf(cups_array_t
*a
, /* I - Array */
720 const char *s
, /* I - Printf-style format string */
721 ...) /* I - Additional args as needed */
723 char buffer
[10240]; /* Format buffer */
724 va_list ap
; /* Argument pointer */
728 * Don't bother is the array is NULL...
735 * Format the message...
739 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
743 * Add it to the array...
746 cupsArrayAdd(a
, buffer
);
751 * 'compare_vars()' - Compare two variables.
754 static int /* O - Result of comparison */
755 compare_vars(_cups_var_t
*a
, /* I - First variable */
756 _cups_var_t
*b
) /* I - Second variable */
758 return (_cups_strcasecmp(a
->name
, b
->name
));
763 * 'do_tests()' - Do tests as specified in the test file.
766 static int /* 1 = success, 0 = failure */
767 do_tests(FILE *outfile
, /* I - Output file */
768 _cups_vars_t
*vars
, /* I - Variables */
769 const char *testfile
) /* I - Test file to use */
771 int i
, /* Looping var */
772 linenum
, /* Current line number */
773 pass
, /* Did we pass the test? */
774 prev_pass
= 1, /* Did we pass the previous test? */
775 request_id
, /* Current request ID */
776 show_header
= 1, /* Show the test header? */
777 ignore_errors
, /* Ignore test failures? */
778 skip_previous
= 0, /* Skip on previous test failure? */
779 repeat_count
, /* Repeat count */
780 repeat_interval
, /* Repeat interval */
781 repeat_prev
, /* Previous repeat interval */
782 repeat_test
; /* Repeat a test? */
783 http_t
*http
= NULL
; /* HTTP connection to server */
784 FILE *fp
= NULL
; /* Test file */
785 char resource
[512], /* Resource for request */
786 token
[1024], /* Token from file */
787 *tokenptr
, /* Pointer into token */
788 temp
[1024], /* Temporary string */
789 buffer
[8192], /* Copy buffer */
790 compression
[16]; /* COMPRESSION value */
791 ipp_t
*request
= NULL
, /* IPP request */
792 *response
= NULL
; /* IPP response */
793 size_t length
; /* Length of IPP request */
794 http_status_t status
; /* HTTP status */
795 cups_file_t
*reqfile
; /* File to send */
796 ssize_t bytes
; /* Bytes read/written */
797 char attr
[128]; /* Attribute name */
798 ipp_op_t op
; /* Operation */
799 ipp_tag_t group
; /* Current group */
800 ipp_tag_t value
; /* Current value type */
801 ipp_attribute_t
*attrptr
, /* Attribute pointer */
802 *found
, /* Found attribute */
803 *lastcol
= NULL
; /* Last collection attribute */
804 char name
[1024], /* Name of test */
805 file_id
[1024], /* File identifier */
806 test_id
[1024]; /* Test identifier */
807 char filename
[1024]; /* Filename */
808 _cups_transfer_t transfer
; /* To chunk or not to chunk */
809 int version
, /* IPP version number to use */
810 skip_test
; /* Skip this test? */
811 int num_statuses
= 0; /* Number of valid status codes */
812 _cups_status_t statuses
[100], /* Valid status codes */
813 *last_status
; /* Last STATUS (for predicates) */
814 int num_expects
= 0; /* Number of expected attributes */
815 _cups_expect_t expects
[200], /* Expected attributes */
816 *expect
, /* Current expected attribute */
817 *last_expect
; /* Last EXPECT (for predicates) */
818 int num_displayed
= 0; /* Number of displayed attributes */
819 char *displayed
[200]; /* Displayed attributes */
820 size_t widths
[200]; /* Width of columns */
821 cups_array_t
*a
, /* Duplicate attribute array */
822 *errors
= NULL
; /* Errors array */
823 const char *error
; /* Current error */
827 * Open the test file...
830 if ((fp
= fopen(testfile
, "r")) == NULL
)
832 print_fatal_error(outfile
, "Unable to open test file %s - %s", testfile
,
839 * Connect to the server...
842 if ((http
= httpConnect2(vars
->hostname
, vars
->port
, NULL
, vars
->family
,
843 vars
->encryption
, 1, 30000, NULL
)) == NULL
)
845 print_fatal_error(outfile
, "Unable to connect to %s on port %d - %s", vars
->hostname
,
846 vars
->port
, cupsLastErrorString());
852 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
,
853 "deflate, gzip, identity");
855 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
, "identity");
856 #endif /* HAVE_LIBZ */
858 if (vars
->timeout
> 0.0)
859 httpSetTimeout(http
, vars
->timeout
, timeout_cb
, NULL
);
865 CUPS_SRAND(time(NULL
));
867 errors
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
,
868 (cups_afree_func_t
)free
);
872 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
874 while (!Cancel
&& get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
877 * Expect an open brace...
880 if (!strcmp(token
, "DEFINE"))
886 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
887 get_token(fp
, temp
, sizeof(temp
), &linenum
))
889 expand_variables(vars
, token
, temp
, sizeof(token
));
890 set_variable(outfile
, vars
, attr
, token
);
894 print_fatal_error(outfile
, "Missing DEFINE name and/or value on line %d.",
902 else if (!strcmp(token
, "DEFINE-DEFAULT"))
905 * DEFINE-DEFAULT name value
908 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
909 get_token(fp
, temp
, sizeof(temp
), &linenum
))
911 expand_variables(vars
, token
, temp
, sizeof(token
));
912 if (!get_variable(vars
, attr
))
913 set_variable(outfile
, vars
, attr
, token
);
917 print_fatal_error(outfile
, "Missing DEFINE-DEFAULT name and/or value on line "
925 else if (!strcmp(token
, "FILE-ID"))
931 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
933 expand_variables(vars
, file_id
, temp
, sizeof(file_id
));
937 print_fatal_error(outfile
, "Missing FILE-ID value on line %d.", linenum
);
944 else if (!strcmp(token
, "IGNORE-ERRORS"))
951 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
952 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
954 IgnoreErrors
= !_cups_strcasecmp(temp
, "yes");
958 print_fatal_error(outfile
, "Missing IGNORE-ERRORS value on line %d.", linenum
);
965 else if (!strcmp(token
, "INCLUDE"))
972 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
975 * Map the filename to and then run the tests...
978 if (!do_tests(outfile
, vars
, get_filename(testfile
, filename
, temp
, sizeof(filename
))))
982 if (StopAfterIncludeError
)
988 print_fatal_error(outfile
, "Missing INCLUDE filename on line %d.", linenum
);
996 else if (!strcmp(token
, "INCLUDE-IF-DEFINED"))
999 * INCLUDE-IF-DEFINED name "filename"
1000 * INCLUDE-IF-DEFINED name <filename>
1003 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1004 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1007 * Map the filename to and then run the tests...
1010 if (get_variable(vars
, attr
) &&
1011 !do_tests(outfile
, vars
, get_filename(testfile
, filename
, temp
, sizeof(filename
))))
1015 if (StopAfterIncludeError
)
1021 print_fatal_error(outfile
, "Missing INCLUDE-IF-DEFINED name or filename on line "
1030 else if (!strcmp(token
, "INCLUDE-IF-NOT-DEFINED"))
1033 * INCLUDE-IF-NOT-DEFINED name "filename"
1034 * INCLUDE-IF-NOT-DEFINED name <filename>
1037 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1038 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1041 * Map the filename to and then run the tests...
1044 if (!get_variable(vars
, attr
) &&
1045 !do_tests(outfile
, vars
, get_filename(testfile
, filename
, temp
, sizeof(filename
))))
1049 if (StopAfterIncludeError
)
1055 print_fatal_error(outfile
, "Missing INCLUDE-IF-NOT-DEFINED name or filename on "
1056 "line %d.", linenum
);
1064 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1067 * SKIP-IF-DEFINED variable
1070 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1072 if (get_variable(vars
, temp
))
1077 print_fatal_error(outfile
, "Missing SKIP-IF-DEFINED variable on line %d.",
1083 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1086 * SKIP-IF-NOT-DEFINED variable
1089 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1091 if (!get_variable(vars
, temp
))
1096 print_fatal_error(outfile
, "Missing SKIP-IF-NOT-DEFINED variable on line %d.",
1102 else if (!strcmp(token
, "STOP-AFTER-INCLUDE-ERROR"))
1105 * STOP-AFTER-INCLUDE-ERROR yes
1106 * STOP-AFTER-INCLUDE-ERROR no
1109 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1110 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1112 StopAfterIncludeError
= !_cups_strcasecmp(temp
, "yes");
1116 print_fatal_error(outfile
, "Missing STOP-AFTER-INCLUDE-ERROR value on line %d.",
1124 else if (!strcmp(token
, "TRANSFER"))
1132 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1134 if (!strcmp(temp
, "auto"))
1135 Transfer
= _CUPS_TRANSFER_AUTO
;
1136 else if (!strcmp(temp
, "chunked"))
1137 Transfer
= _CUPS_TRANSFER_CHUNKED
;
1138 else if (!strcmp(temp
, "length"))
1139 Transfer
= _CUPS_TRANSFER_LENGTH
;
1142 print_fatal_error(outfile
, "Bad TRANSFER value \"%s\" on line %d.", temp
,
1150 print_fatal_error(outfile
, "Missing TRANSFER value on line %d.", linenum
);
1157 else if (!strcmp(token
, "VERSION"))
1159 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1161 if (!strcmp(temp
, "1.0"))
1163 else if (!strcmp(temp
, "1.1"))
1165 else if (!strcmp(temp
, "2.0"))
1167 else if (!strcmp(temp
, "2.1"))
1169 else if (!strcmp(temp
, "2.2"))
1173 print_fatal_error(outfile
, "Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1180 print_fatal_error(outfile
, "Missing VERSION number on line %d.", linenum
);
1187 else if (strcmp(token
, "{"))
1189 print_fatal_error(outfile
, "Unexpected token %s seen on line %d.", token
, linenum
);
1195 * Initialize things...
1200 if (Output
== _CUPS_OUTPUT_PLIST
)
1201 print_xml_header(outfile
);
1202 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
1203 printf("\"%s\":\n", testfile
);
1208 strlcpy(resource
, vars
->resource
, sizeof(resource
));
1213 group
= IPP_TAG_ZERO
;
1214 ignore_errors
= IgnoreErrors
;
1222 transfer
= Transfer
;
1223 compression
[0] = '\0';
1225 strlcpy(name
, testfile
, sizeof(name
));
1226 if (strrchr(name
, '.') != NULL
)
1227 *strrchr(name
, '.') = '\0';
1230 * Parse until we see a close brace...
1233 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
1235 if (_cups_strcasecmp(token
, "COUNT") &&
1236 _cups_strcasecmp(token
, "DEFINE-MATCH") &&
1237 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1238 _cups_strcasecmp(token
, "DEFINE-VALUE") &&
1239 _cups_strcasecmp(token
, "IF-DEFINED") &&
1240 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1241 _cups_strcasecmp(token
, "IN-GROUP") &&
1242 _cups_strcasecmp(token
, "OF-TYPE") &&
1243 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1244 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1245 _cups_strcasecmp(token
, "REPEAT-NO-MATCH") &&
1246 _cups_strcasecmp(token
, "SAME-COUNT-AS") &&
1247 _cups_strcasecmp(token
, "WITH-ALL-VALUES") &&
1248 _cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") &&
1249 _cups_strcasecmp(token
, "WITH-ALL-RESOURCES") &&
1250 _cups_strcasecmp(token
, "WITH-ALL-SCHEMES") &&
1251 _cups_strcasecmp(token
, "WITH-HOSTNAME") &&
1252 _cups_strcasecmp(token
, "WITH-RESOURCE") &&
1253 _cups_strcasecmp(token
, "WITH-SCHEME") &&
1254 _cups_strcasecmp(token
, "WITH-VALUE") &&
1255 _cups_strcasecmp(token
, "WITH-VALUE-FROM"))
1258 if (_cups_strcasecmp(token
, "DEFINE-MATCH") &&
1259 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1260 _cups_strcasecmp(token
, "IF-DEFINED") &&
1261 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1262 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1263 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1264 _cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
1267 if (!strcmp(token
, "}"))
1269 else if (!strcmp(token
, "{") && lastcol
)
1272 * Another collection value
1275 ipp_t
*col
= get_collection(outfile
, vars
, fp
, &linenum
);
1276 /* Collection value */
1280 ippSetCollection(request
, &lastcol
, ippGetCount(lastcol
), col
);
1288 else if (!strcmp(token
, "COMPRESSION"))
1292 * COMPRESSION deflate
1296 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1298 expand_variables(vars
, compression
, temp
, sizeof(compression
));
1300 if (strcmp(compression
, "none") && strcmp(compression
, "deflate") &&
1301 strcmp(compression
, "gzip"))
1303 if (strcmp(compression
, "none"))
1304 #endif /* HAVE_LIBZ */
1306 print_fatal_error(outfile
, "Unsupported COMPRESSION value '%s' on line %d.",
1307 compression
, linenum
);
1312 if (!strcmp(compression
, "none"))
1313 compression
[0] = '\0';
1317 print_fatal_error(outfile
, "Missing COMPRESSION value on line %d.", linenum
);
1322 else if (!strcmp(token
, "DEFINE"))
1328 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1329 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1331 expand_variables(vars
, token
, temp
, sizeof(token
));
1332 set_variable(outfile
, vars
, attr
, token
);
1336 print_fatal_error(outfile
, "Missing DEFINE name and/or value on line %d.",
1342 else if (!strcmp(token
, "IGNORE-ERRORS"))
1349 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1350 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1352 ignore_errors
= !_cups_strcasecmp(temp
, "yes");
1356 print_fatal_error(outfile
, "Missing IGNORE-ERRORS value on line %d.", linenum
);
1363 else if (!_cups_strcasecmp(token
, "NAME"))
1369 get_token(fp
, temp
, sizeof(temp
), &linenum
);
1370 expand_variables(vars
, name
, temp
, sizeof(name
));
1372 else if (!_cups_strcasecmp(token
, "PAUSE"))
1375 * Pause with a message...
1378 get_token(fp
, token
, sizeof(token
), &linenum
);
1379 pause_message(token
);
1381 else if (!strcmp(token
, "REQUEST-ID"))
1388 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1390 if (isdigit(temp
[0] & 255))
1391 request_id
= atoi(temp
);
1392 else if (!_cups_strcasecmp(temp
, "random"))
1393 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
1396 print_fatal_error(outfile
, "Bad REQUEST-ID value \"%s\" on line %d.", temp
,
1404 print_fatal_error(outfile
, "Missing REQUEST-ID value on line %d.", linenum
);
1409 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1412 * SKIP-IF-DEFINED variable
1415 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1417 if (get_variable(vars
, temp
))
1422 print_fatal_error(outfile
, "Missing SKIP-IF-DEFINED value on line %d.",
1428 else if (!strcmp(token
, "SKIP-IF-MISSING"))
1431 * SKIP-IF-MISSING filename
1434 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1436 expand_variables(vars
, token
, temp
, sizeof(token
));
1437 get_filename(testfile
, filename
, token
, sizeof(filename
));
1439 if (access(filename
, R_OK
))
1444 print_fatal_error(outfile
, "Missing SKIP-IF-MISSING filename on line %d.",
1450 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1453 * SKIP-IF-NOT-DEFINED variable
1456 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1458 if (!get_variable(vars
, temp
))
1463 print_fatal_error(outfile
, "Missing SKIP-IF-NOT-DEFINED value on line %d.",
1469 else if (!strcmp(token
, "SKIP-PREVIOUS-ERROR"))
1472 * SKIP-PREVIOUS-ERROR yes
1473 * SKIP-PREVIOUS-ERROR no
1476 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1477 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1479 skip_previous
= !_cups_strcasecmp(temp
, "yes");
1483 print_fatal_error(outfile
, "Missing SKIP-PREVIOUS-ERROR value on line %d.", linenum
);
1490 else if (!strcmp(token
, "TEST-ID"))
1496 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1498 expand_variables(vars
, test_id
, temp
, sizeof(test_id
));
1502 print_fatal_error(outfile
, "Missing TEST-ID value on line %d.", linenum
);
1509 else if (!strcmp(token
, "TRANSFER"))
1517 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1519 if (!strcmp(temp
, "auto"))
1520 transfer
= _CUPS_TRANSFER_AUTO
;
1521 else if (!strcmp(temp
, "chunked"))
1522 transfer
= _CUPS_TRANSFER_CHUNKED
;
1523 else if (!strcmp(temp
, "length"))
1524 transfer
= _CUPS_TRANSFER_LENGTH
;
1527 print_fatal_error(outfile
, "Bad TRANSFER value \"%s\" on line %d.", temp
,
1535 print_fatal_error(outfile
, "Missing TRANSFER value on line %d.", linenum
);
1540 else if (!_cups_strcasecmp(token
, "VERSION"))
1542 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1544 if (!strcmp(temp
, "0.0"))
1546 else if (!strcmp(temp
, "1.0"))
1548 else if (!strcmp(temp
, "1.1"))
1550 else if (!strcmp(temp
, "2.0"))
1552 else if (!strcmp(temp
, "2.1"))
1554 else if (!strcmp(temp
, "2.2"))
1558 print_fatal_error(outfile
, "Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1565 print_fatal_error(outfile
, "Missing VERSION number on line %d.", linenum
);
1570 else if (!_cups_strcasecmp(token
, "RESOURCE"))
1576 if (!get_token(fp
, resource
, sizeof(resource
), &linenum
))
1578 print_fatal_error(outfile
, "Missing RESOURCE path on line %d.", linenum
);
1583 else if (!_cups_strcasecmp(token
, "OPERATION"))
1589 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1591 print_fatal_error(outfile
, "Missing OPERATION code on line %d.", linenum
);
1596 expand_variables(vars
, token
, temp
, sizeof(token
));
1598 if ((op
= ippOpValue(token
)) == (ipp_op_t
)-1 &&
1599 (op
= (ipp_op_t
)strtol(token
, NULL
, 0)) == 0)
1601 print_fatal_error(outfile
, "Bad OPERATION code \"%s\" on line %d.", token
,
1607 else if (!_cups_strcasecmp(token
, "GROUP"))
1610 * Attribute group...
1613 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1615 print_fatal_error(outfile
, "Missing GROUP tag on line %d.", linenum
);
1620 if ((value
= ippTagValue(token
)) < 0)
1622 print_fatal_error(outfile
, "Bad GROUP tag \"%s\" on line %d.", token
, linenum
);
1628 ippAddSeparator(request
);
1632 else if (!_cups_strcasecmp(token
, "DELAY"))
1635 * Delay before operation...
1640 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1642 print_fatal_error(outfile
, "Missing DELAY value on line %d.", linenum
);
1647 expand_variables(vars
, token
, temp
, sizeof(token
));
1649 if ((delay
= _cupsStrScand(token
, NULL
, localeconv())) <= 0.0)
1651 print_fatal_error(outfile
, "Bad DELAY value \"%s\" on line %d.", token
,
1658 if (Output
== _CUPS_OUTPUT_TEST
)
1659 printf(" [%g second delay]\n", delay
);
1661 usleep((useconds_t
)(1000000.0 * delay
));
1664 else if (!_cups_strcasecmp(token
, "ATTR"))
1670 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1672 print_fatal_error(outfile
, "Missing ATTR value tag on line %d.", linenum
);
1677 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
1679 print_fatal_error(outfile
, "Bad ATTR value tag \"%s\" on line %d.", token
,
1685 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
1687 print_fatal_error(outfile
, "Missing ATTR name on line %d.", linenum
);
1692 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1694 print_fatal_error(outfile
, "Missing ATTR value on line %d.", linenum
);
1699 expand_variables(vars
, token
, temp
, sizeof(token
));
1704 case IPP_TAG_BOOLEAN
:
1705 if (!_cups_strcasecmp(token
, "true"))
1706 attrptr
= ippAddBoolean(request
, group
, attr
, 1);
1708 attrptr
= ippAddBoolean(request
, group
, attr
, (char)atoi(token
));
1711 case IPP_TAG_INTEGER
:
1713 if (!strchr(token
, ','))
1715 if (isdigit(token
[0] & 255) || token
[0] == '-')
1716 attrptr
= ippAddInteger(request
, group
, value
, attr
, (int)strtol(token
, &tokenptr
, 0));
1720 if ((i
= ippEnumValue(attr
, tokenptr
)) >= 0)
1722 attrptr
= ippAddInteger(request
, group
, value
, attr
, i
);
1723 tokenptr
+= strlen(tokenptr
);
1729 int values
[100], /* Values */
1730 num_values
= 1; /* Number of values */
1732 if (!isdigit(token
[0] & 255) && token
[0] != '-' && value
== IPP_TAG_ENUM
)
1734 char *ptr
; /* Pointer to next terminator */
1736 if ((ptr
= strchr(token
, ',')) != NULL
)
1739 ptr
= token
+ strlen(token
);
1741 if ((i
= ippEnumValue(attr
, token
)) < 0)
1747 i
= (int)strtol(tokenptr
, &tokenptr
, 0);
1751 while (tokenptr
&& *tokenptr
&&
1752 num_values
< (int)(sizeof(values
) / sizeof(values
[0])))
1754 if (*tokenptr
== ',')
1757 if (!isdigit(*tokenptr
& 255) && *tokenptr
!= '-')
1759 char *ptr
; /* Pointer to next terminator */
1761 if (value
!= IPP_TAG_ENUM
)
1764 if ((ptr
= strchr(tokenptr
, ',')) != NULL
)
1767 ptr
= tokenptr
+ strlen(tokenptr
);
1769 if ((i
= ippEnumValue(attr
, tokenptr
)) < 0)
1775 i
= (int)strtol(tokenptr
, &tokenptr
, 0);
1777 values
[num_values
++] = i
;
1780 attrptr
= ippAddIntegers(request
, group
, value
, attr
, num_values
, values
);
1783 if (!tokenptr
|| *tokenptr
)
1785 print_fatal_error(outfile
, "Bad %s value \"%s\" on line %d.",
1786 ippTagString(value
), token
, linenum
);
1792 case IPP_TAG_RESOLUTION
:
1794 int xres
, /* X resolution */
1795 yres
; /* Y resolution */
1796 char *ptr
; /* Pointer into value */
1798 xres
= yres
= (int)strtol(token
, (char **)&ptr
, 10);
1799 if (ptr
> token
&& xres
> 0)
1802 yres
= (int)strtol(ptr
+ 1, (char **)&ptr
, 10);
1805 if (ptr
<= token
|| xres
<= 0 || yres
<= 0 || !ptr
||
1806 (_cups_strcasecmp(ptr
, "dpi") &&
1807 _cups_strcasecmp(ptr
, "dpc") &&
1808 _cups_strcasecmp(ptr
, "dpcm") &&
1809 _cups_strcasecmp(ptr
, "other")))
1811 print_fatal_error(outfile
, "Bad resolution value \"%s\" on line %d.",
1817 if (!_cups_strcasecmp(ptr
, "dpi"))
1818 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
1819 else if (!_cups_strcasecmp(ptr
, "dpc") ||
1820 !_cups_strcasecmp(ptr
, "dpcm"))
1821 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_CM
, xres
, yres
);
1823 attrptr
= ippAddResolution(request
, group
, attr
, (ipp_res_t
)0, xres
, yres
);
1827 case IPP_TAG_RANGE
:
1829 int lowers
[4], /* Lower value */
1830 uppers
[4], /* Upper values */
1831 num_vals
; /* Number of values */
1834 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
1835 lowers
+ 0, uppers
+ 0,
1836 lowers
+ 1, uppers
+ 1,
1837 lowers
+ 2, uppers
+ 2,
1838 lowers
+ 3, uppers
+ 3);
1840 if ((num_vals
& 1) || num_vals
== 0)
1842 print_fatal_error(outfile
, "Bad rangeOfInteger value \"%s\" on line "
1843 "%d.", token
, linenum
);
1848 attrptr
= ippAddRanges(request
, group
, attr
, num_vals
/ 2, lowers
,
1853 case IPP_TAG_BEGIN_COLLECTION
:
1854 if (!strcmp(token
, "{"))
1856 ipp_t
*col
= get_collection(outfile
, vars
, fp
, &linenum
);
1857 /* Collection value */
1861 attrptr
= lastcol
= ippAddCollection(request
, group
, attr
, col
);
1872 print_fatal_error(outfile
, "Bad ATTR collection value on line %d.",
1880 ipp_t
*col
; /* Collection value */
1881 long pos
= ftell(fp
); /* Save position of file */
1883 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1886 if (strcmp(token
, ","))
1888 fseek(fp
, pos
, SEEK_SET
);
1892 if (!get_token(fp
, token
, sizeof(token
), &linenum
) || strcmp(token
, "{"))
1894 print_fatal_error(outfile
, "Unexpected \"%s\" on line %d.", token
, linenum
);
1900 if ((col
= get_collection(outfile
, vars
, fp
, &linenum
)) == NULL
)
1903 ippSetCollection(request
, &attrptr
, ippGetCount(attrptr
), col
);
1906 while (!strcmp(token
, "{"));
1909 case IPP_TAG_STRING
:
1910 attrptr
= ippAddOctetString(request
, group
, attr
, token
, (int)strlen(token
));
1914 print_fatal_error(outfile
, "Unsupported ATTR value tag %s on line %d.",
1915 ippTagString(value
), linenum
);
1919 case IPP_TAG_TEXTLANG
:
1920 case IPP_TAG_NAMELANG
:
1923 case IPP_TAG_KEYWORD
:
1925 case IPP_TAG_URISCHEME
:
1926 case IPP_TAG_CHARSET
:
1927 case IPP_TAG_LANGUAGE
:
1928 case IPP_TAG_MIMETYPE
:
1929 if (!strchr(token
, ','))
1930 attrptr
= ippAddString(request
, group
, value
, attr
, NULL
, token
);
1934 * Multiple string values...
1937 int num_values
; /* Number of values */
1938 char *values
[100], /* Values */
1939 *ptr
; /* Pointer to next value */
1945 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
1947 if (ptr
> token
&& ptr
[-1] == '\\')
1948 _cups_strcpy(ptr
- 1, ptr
);
1952 values
[num_values
] = ptr
;
1957 attrptr
= ippAddStrings(request
, group
, value
, attr
, num_values
,
1958 NULL
, (const char **)values
);
1965 print_fatal_error(outfile
, "Unable to add attribute on line %d: %s", linenum
,
1966 cupsLastErrorString());
1971 else if (!_cups_strcasecmp(token
, "FILE"))
1977 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1979 print_fatal_error(outfile
, "Missing FILE filename on line %d.", linenum
);
1984 expand_variables(vars
, token
, temp
, sizeof(token
));
1985 get_filename(testfile
, filename
, token
, sizeof(filename
));
1987 if (access(filename
, R_OK
))
1989 print_fatal_error(outfile
, "Filename \"%s\" on line %d cannot be read.",
1991 print_fatal_error(outfile
, "Filename mapped to \"%s\".", filename
);
1996 else if (!_cups_strcasecmp(token
, "STATUS"))
2002 if (num_statuses
>= (int)(sizeof(statuses
) / sizeof(statuses
[0])))
2004 print_fatal_error(outfile
, "Too many STATUS's on line %d.", linenum
);
2009 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2011 print_fatal_error(outfile
, "Missing STATUS code on line %d.", linenum
);
2016 if ((statuses
[num_statuses
].status
= ippErrorValue(token
))
2017 == (ipp_status_t
)-1 &&
2018 (statuses
[num_statuses
].status
= (ipp_status_t
)strtol(token
, NULL
, 0)) == 0)
2020 print_fatal_error(outfile
, "Bad STATUS code \"%s\" on line %d.", token
,
2026 last_status
= statuses
+ num_statuses
;
2029 last_status
->define_match
= NULL
;
2030 last_status
->define_no_match
= NULL
;
2031 last_status
->if_defined
= NULL
;
2032 last_status
->if_not_defined
= NULL
;
2033 last_status
->repeat_limit
= 1000;
2034 last_status
->repeat_match
= 0;
2035 last_status
->repeat_no_match
= 0;
2037 else if (!_cups_strcasecmp(token
, "EXPECT") || !_cups_strcasecmp(token
, "EXPECT-ALL"))
2040 * Expected attributes...
2043 int expect_all
= !_cups_strcasecmp(token
, "EXPECT-ALL");
2045 if (num_expects
>= (int)(sizeof(expects
) / sizeof(expects
[0])))
2047 print_fatal_error(outfile
, "Too many EXPECT's on line %d.", linenum
);
2052 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2054 print_fatal_error(outfile
, "Missing EXPECT name on line %d.", linenum
);
2059 last_expect
= expects
+ num_expects
;
2062 memset(last_expect
, 0, sizeof(_cups_expect_t
));
2063 last_expect
->repeat_limit
= 1000;
2064 last_expect
->expect_all
= expect_all
;
2066 if (token
[0] == '!')
2068 last_expect
->not_expect
= 1;
2069 last_expect
->name
= strdup(token
+ 1);
2071 else if (token
[0] == '?')
2073 last_expect
->optional
= 1;
2074 last_expect
->name
= strdup(token
+ 1);
2077 last_expect
->name
= strdup(token
);
2079 else if (!_cups_strcasecmp(token
, "COUNT"))
2081 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2083 print_fatal_error(outfile
, "Missing COUNT number on line %d.", linenum
);
2088 if ((i
= atoi(token
)) <= 0)
2090 print_fatal_error(outfile
, "Bad COUNT \"%s\" on line %d.", token
, linenum
);
2096 last_expect
->count
= i
;
2099 print_fatal_error(outfile
, "COUNT without a preceding EXPECT on line %d.",
2105 else if (!_cups_strcasecmp(token
, "DEFINE-MATCH"))
2107 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2109 print_fatal_error(outfile
, "Missing DEFINE-MATCH variable on line %d.",
2116 last_expect
->define_match
= strdup(token
);
2117 else if (last_status
)
2118 last_status
->define_match
= strdup(token
);
2121 print_fatal_error(outfile
, "DEFINE-MATCH without a preceding EXPECT or STATUS "
2122 "on line %d.", linenum
);
2127 else if (!_cups_strcasecmp(token
, "DEFINE-NO-MATCH"))
2129 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2131 print_fatal_error(outfile
, "Missing DEFINE-NO-MATCH variable on line %d.",
2138 last_expect
->define_no_match
= strdup(token
);
2139 else if (last_status
)
2140 last_status
->define_no_match
= strdup(token
);
2143 print_fatal_error(outfile
, "DEFINE-NO-MATCH without a preceding EXPECT or "
2144 "STATUS on line %d.", linenum
);
2149 else if (!_cups_strcasecmp(token
, "DEFINE-VALUE"))
2151 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2153 print_fatal_error(outfile
, "Missing DEFINE-VALUE variable on line %d.",
2160 last_expect
->define_value
= strdup(token
);
2163 print_fatal_error(outfile
, "DEFINE-VALUE without a preceding EXPECT on "
2164 "line %d.", linenum
);
2169 else if (!_cups_strcasecmp(token
, "OF-TYPE"))
2171 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2173 print_fatal_error(outfile
, "Missing OF-TYPE value tag(s) on line %d.",
2180 last_expect
->of_type
= strdup(token
);
2183 print_fatal_error(outfile
, "OF-TYPE without a preceding EXPECT on line %d.",
2189 else if (!_cups_strcasecmp(token
, "IN-GROUP"))
2191 ipp_tag_t in_group
; /* IN-GROUP value */
2194 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2196 print_fatal_error(outfile
, "Missing IN-GROUP group tag on line %d.", linenum
);
2201 if ((in_group
= ippTagValue(token
)) == (ipp_tag_t
)-1)
2204 else if (last_expect
)
2205 last_expect
->in_group
= in_group
;
2208 print_fatal_error(outfile
, "IN-GROUP without a preceding EXPECT on line %d.",
2214 else if (!_cups_strcasecmp(token
, "REPEAT-LIMIT"))
2216 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2218 print_fatal_error(outfile
, "Missing REPEAT-LIMIT value on line %d.", linenum
);
2222 else if (atoi(token
) <= 0)
2224 print_fatal_error(outfile
, "Bad REPEAT-LIMIT value on line %d.", linenum
);
2230 last_status
->repeat_limit
= atoi(token
);
2231 else if (last_expect
)
2232 last_expect
->repeat_limit
= atoi(token
);
2235 print_fatal_error(outfile
, "REPEAT-LIMIT without a preceding EXPECT or STATUS "
2236 "on line %d.", linenum
);
2241 else if (!_cups_strcasecmp(token
, "REPEAT-MATCH"))
2244 last_status
->repeat_match
= 1;
2245 else if (last_expect
)
2246 last_expect
->repeat_match
= 1;
2249 print_fatal_error(outfile
, "REPEAT-MATCH without a preceding EXPECT or STATUS "
2250 "on line %d.", linenum
);
2255 else if (!_cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
2258 last_status
->repeat_no_match
= 1;
2259 else if (last_expect
)
2260 last_expect
->repeat_no_match
= 1;
2263 print_fatal_error(outfile
, "REPEAT-NO-MATCH without a preceding EXPECT or "
2264 "STATUS on ine %d.", linenum
);
2269 else if (!_cups_strcasecmp(token
, "SAME-COUNT-AS"))
2271 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2273 print_fatal_error(outfile
, "Missing SAME-COUNT-AS name on line %d.", linenum
);
2279 last_expect
->same_count_as
= strdup(token
);
2282 print_fatal_error(outfile
, "SAME-COUNT-AS without a preceding EXPECT on line "
2288 else if (!_cups_strcasecmp(token
, "IF-DEFINED"))
2290 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2292 print_fatal_error(outfile
, "Missing IF-DEFINED name on line %d.", linenum
);
2298 last_expect
->if_defined
= strdup(token
);
2299 else if (last_status
)
2300 last_status
->if_defined
= strdup(token
);
2303 print_fatal_error(outfile
, "IF-DEFINED without a preceding EXPECT or STATUS "
2304 "on line %d.", linenum
);
2309 else if (!_cups_strcasecmp(token
, "IF-NOT-DEFINED"))
2311 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2313 print_fatal_error(outfile
, "Missing IF-NOT-DEFINED name on line %d.", linenum
);
2319 last_expect
->if_not_defined
= strdup(token
);
2320 else if (last_status
)
2321 last_status
->if_not_defined
= strdup(token
);
2324 print_fatal_error(outfile
, "IF-NOT-DEFINED without a preceding EXPECT or STATUS "
2325 "on line %d.", linenum
);
2330 else if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") ||
2331 !_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
2332 !_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
2333 !_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
2334 !_cups_strcasecmp(token
, "WITH-HOSTNAME") ||
2335 !_cups_strcasecmp(token
, "WITH-RESOURCE") ||
2336 !_cups_strcasecmp(token
, "WITH-SCHEME") ||
2337 !_cups_strcasecmp(token
, "WITH-VALUE"))
2341 if (!_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
2342 !_cups_strcasecmp(token
, "WITH-HOSTNAME"))
2343 last_expect
->with_flags
= _CUPS_WITH_HOSTNAME
;
2344 else if (!_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
2345 !_cups_strcasecmp(token
, "WITH-RESOURCE"))
2346 last_expect
->with_flags
= _CUPS_WITH_RESOURCE
;
2347 else if (!_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
2348 !_cups_strcasecmp(token
, "WITH-SCHEME"))
2349 last_expect
->with_flags
= _CUPS_WITH_SCHEME
;
2351 if (!_cups_strncasecmp(token
, "WITH-ALL-", 9))
2352 last_expect
->with_flags
|= _CUPS_WITH_ALL
;
2355 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
2357 print_fatal_error(outfile
, "Missing %s value on line %d.", token
, linenum
);
2365 * Expand any variables in the value and then save it.
2368 expand_variables(vars
, token
, temp
, sizeof(token
));
2370 tokenptr
= token
+ strlen(token
) - 1;
2372 if (token
[0] == '/' && tokenptr
> token
&& *tokenptr
== '/')
2375 * WITH-VALUE is a POSIX extended regular expression.
2378 last_expect
->with_value
= calloc(1, (size_t)(tokenptr
- token
));
2379 last_expect
->with_flags
|= _CUPS_WITH_REGEX
;
2381 if (last_expect
->with_value
)
2382 memcpy(last_expect
->with_value
, token
+ 1, (size_t)(tokenptr
- token
- 1));
2387 * WITH-VALUE is a literal value...
2390 char *ptr
; /* Pointer into value */
2392 for (ptr
= token
; *ptr
; ptr
++)
2394 if (*ptr
== '\\' && ptr
[1])
2397 * Remove \ from \foo...
2400 _cups_strcpy(ptr
, ptr
+ 1);
2404 last_expect
->with_value
= strdup(token
);
2405 last_expect
->with_flags
|= _CUPS_WITH_LITERAL
;
2410 print_fatal_error(outfile
, "%s without a preceding EXPECT on line %d.", token
,
2416 else if (!_cups_strcasecmp(token
, "WITH-VALUE-FROM"))
2418 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
2420 print_fatal_error(outfile
, "Missing %s value on line %d.", token
, linenum
);
2428 * Expand any variables in the value and then save it.
2431 expand_variables(vars
, token
, temp
, sizeof(token
));
2433 last_expect
->with_value_from
= strdup(token
);
2434 last_expect
->with_flags
= _CUPS_WITH_LITERAL
;
2438 print_fatal_error(outfile
, "%s without a preceding EXPECT on line %d.", token
,
2444 else if (!_cups_strcasecmp(token
, "DISPLAY"))
2447 * Display attributes...
2450 if (num_displayed
>= (int)(sizeof(displayed
) / sizeof(displayed
[0])))
2452 print_fatal_error(outfile
, "Too many DISPLAY's on line %d", linenum
);
2457 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2459 print_fatal_error(outfile
, "Missing DISPLAY name on line %d.", linenum
);
2464 displayed
[num_displayed
] = strdup(token
);
2469 print_fatal_error(outfile
, "Unexpected token %s seen on line %d.", token
,
2477 * Submit the IPP request...
2482 ippSetVersion(request
, version
/ 10, version
% 10);
2483 ippSetOperation(request
, op
);
2484 ippSetRequestId(request
, request_id
);
2486 if (Output
== _CUPS_OUTPUT_PLIST
)
2488 fputs("<dict>\n", outfile
);
2489 fputs("<key>Name</key>\n", outfile
);
2490 print_xml_string(outfile
, "string", name
);
2493 fputs("<key>FileId</key>\n", outfile
);
2494 print_xml_string(outfile
, "string", file_id
);
2498 fputs("<key>TestId</key>\n", outfile
);
2499 print_xml_string(outfile
, "string", test_id
);
2501 fputs("<key>Version</key>\n", outfile
);
2502 fprintf(outfile
, "<string>%d.%d</string>\n", version
/ 10, version
% 10);
2503 fputs("<key>Operation</key>\n", outfile
);
2504 print_xml_string(outfile
, "string", ippOpString(op
));
2505 fputs("<key>RequestId</key>\n", outfile
);
2506 fprintf(outfile
, "<integer>%d</integer>\n", request_id
);
2507 fputs("<key>RequestAttributes</key>\n", outfile
);
2508 fputs("<array>\n", outfile
);
2511 fputs("<dict>\n", outfile
);
2512 for (attrptr
= request
->attrs
,
2513 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2515 attrptr
= attrptr
->next
)
2516 print_attr(outfile
, Output
, attrptr
, &group
);
2517 fputs("</dict>\n", outfile
);
2519 fputs("</array>\n", outfile
);
2522 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
2526 printf(" %s:\n", ippOpString(op
));
2528 for (attrptr
= request
->attrs
; attrptr
; attrptr
= attrptr
->next
)
2529 print_attr(stdout
, _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
2532 printf(" %-68.68s [", name
);
2536 if ((skip_previous
&& !prev_pass
) || skip_test
)
2543 if (Output
== _CUPS_OUTPUT_PLIST
)
2545 fputs("<key>Successful</key>\n", outfile
);
2546 fputs("<true />\n", outfile
);
2547 fputs("<key>Skipped</key>\n", outfile
);
2548 fputs("<true />\n", outfile
);
2549 fputs("<key>StatusCode</key>\n", outfile
);
2550 print_xml_string(outfile
, "string", "skip");
2551 fputs("<key>ResponseAttributes</key>\n", outfile
);
2552 fputs("<dict />\n", outfile
);
2555 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
2563 repeat_interval
= 1;
2570 status
= HTTP_STATUS_OK
;
2572 if (transfer
== _CUPS_TRANSFER_CHUNKED
||
2573 (transfer
== _CUPS_TRANSFER_AUTO
&& filename
[0]))
2576 * Send request using chunking - a 0 length means "chunk".
2584 * Send request using content length...
2587 length
= ippLength(request
);
2589 if (filename
[0] && (reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2592 * Read the file to get the uncompressed file size...
2595 while ((bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
2596 length
+= (size_t)bytes
;
2598 cupsFileClose(reqfile
);
2603 * Send the request...
2610 if (status
!= HTTP_STATUS_ERROR
)
2612 while (!response
&& !Cancel
&& prev_pass
)
2614 status
= cupsSendRequest(http
, request
, resource
, length
);
2618 httpSetField(http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
2619 #endif /* HAVE_LIBZ */
2621 if (!Cancel
&& status
== HTTP_STATUS_CONTINUE
&&
2622 request
->state
== IPP_DATA
&& filename
[0])
2624 if ((reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2627 (bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
2628 if ((status
= cupsWriteRequestData(http
, buffer
, (size_t)bytes
)) != HTTP_STATUS_CONTINUE
)
2631 cupsFileClose(reqfile
);
2635 snprintf(buffer
, sizeof(buffer
), "%s: %s", filename
,
2637 _cupsSetError(IPP_INTERNAL_ERROR
, buffer
, 0);
2639 status
= HTTP_STATUS_ERROR
;
2644 * Get the server's response...
2647 if (!Cancel
&& status
!= HTTP_STATUS_ERROR
)
2649 response
= cupsGetResponse(http
, resource
);
2650 status
= httpGetStatus(http
);
2653 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& http
->error
!= EINVAL
&&
2655 http
->error
!= WSAETIMEDOUT
)
2657 http
->error
!= ETIMEDOUT
)
2660 if (httpReconnect(http
))
2663 else if (status
== HTTP_STATUS_ERROR
|| status
== HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED
)
2668 else if (status
!= HTTP_STATUS_OK
)
2672 if (status
== HTTP_STATUS_UNAUTHORIZED
)
2680 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& http
->error
!= EINVAL
&&
2682 http
->error
!= WSAETIMEDOUT
)
2684 http
->error
!= ETIMEDOUT
)
2687 if (httpReconnect(http
))
2690 else if (status
== HTTP_STATUS_ERROR
)
2693 httpReconnect(http
);
2697 else if (status
!= HTTP_STATUS_OK
)
2704 * Check results of request...
2707 cupsArrayClear(errors
);
2709 if (http
->version
!= HTTP_1_1
)
2710 add_stringf(errors
, "Bad HTTP version (%d.%d)", http
->version
/ 100,
2711 http
->version
% 100);
2716 * No response, log error...
2719 add_stringf(errors
, "IPP request failed with status %s (%s)",
2720 ippErrorString(cupsLastError()),
2721 cupsLastErrorString());
2726 * Collect common attribute values...
2729 if ((attrptr
= ippFindAttribute(response
, "job-id",
2730 IPP_TAG_INTEGER
)) != NULL
)
2732 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2733 set_variable(outfile
, vars
, "job-id", temp
);
2736 if ((attrptr
= ippFindAttribute(response
, "job-uri",
2737 IPP_TAG_URI
)) != NULL
)
2738 set_variable(outfile
, vars
, "job-uri", attrptr
->values
[0].string
.text
);
2740 if ((attrptr
= ippFindAttribute(response
, "notify-subscription-id",
2741 IPP_TAG_INTEGER
)) != NULL
)
2743 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2744 set_variable(outfile
, vars
, "notify-subscription-id", temp
);
2748 * Check response, validating groups and attributes and logging errors
2752 if (response
->state
!= IPP_DATA
)
2754 "Missing end-of-attributes-tag in response "
2755 "(RFC 2910 section 3.5.1)");
2758 (response
->request
.status
.version
[0] != (version
/ 10) ||
2759 response
->request
.status
.version
[1] != (version
% 10)))
2761 "Bad version %d.%d in response - expected %d.%d "
2762 "(RFC 2911 section 3.1.8).",
2763 response
->request
.status
.version
[0],
2764 response
->request
.status
.version
[1],
2765 version
/ 10, version
% 10);
2767 if (response
->request
.status
.request_id
!= request_id
)
2769 "Bad request ID %d in response - expected %d "
2770 "(RFC 2911 section 3.1.1)",
2771 response
->request
.status
.request_id
, request_id
);
2773 attrptr
= response
->attrs
;
2776 "Missing first attribute \"attributes-charset "
2777 "(charset)\" in group operation-attributes-tag "
2778 "(RFC 2911 section 3.1.4).");
2781 if (!attrptr
->name
||
2782 attrptr
->value_tag
!= IPP_TAG_CHARSET
||
2783 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2784 attrptr
->num_values
!= 1 ||
2785 strcmp(attrptr
->name
, "attributes-charset"))
2787 "Bad first attribute \"%s (%s%s)\" in group %s, "
2788 "expected \"attributes-charset (charset)\" in "
2789 "group operation-attributes-tag (RFC 2911 section "
2791 attrptr
->name
? attrptr
->name
: "(null)",
2792 attrptr
->num_values
> 1 ? "1setOf " : "",
2793 ippTagString(attrptr
->value_tag
),
2794 ippTagString(attrptr
->group_tag
));
2796 attrptr
= attrptr
->next
;
2799 "Missing second attribute \"attributes-natural-"
2800 "language (naturalLanguage)\" in group "
2801 "operation-attributes-tag (RFC 2911 section "
2803 else if (!attrptr
->name
||
2804 attrptr
->value_tag
!= IPP_TAG_LANGUAGE
||
2805 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2806 attrptr
->num_values
!= 1 ||
2807 strcmp(attrptr
->name
, "attributes-natural-language"))
2809 "Bad first attribute \"%s (%s%s)\" in group %s, "
2810 "expected \"attributes-natural-language "
2811 "(naturalLanguage)\" in group "
2812 "operation-attributes-tag (RFC 2911 section "
2814 attrptr
->name
? attrptr
->name
: "(null)",
2815 attrptr
->num_values
> 1 ? "1setOf " : "",
2816 ippTagString(attrptr
->value_tag
),
2817 ippTagString(attrptr
->group_tag
));
2820 if ((attrptr
= ippFindAttribute(response
, "status-message",
2821 IPP_TAG_ZERO
)) != NULL
)
2823 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2825 "status-message (text(255)) has wrong value tag "
2826 "%s (RFC 2911 section 3.1.6.2).",
2827 ippTagString(attrptr
->value_tag
));
2828 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2830 "status-message (text(255)) has wrong group tag "
2831 "%s (RFC 2911 section 3.1.6.2).",
2832 ippTagString(attrptr
->group_tag
));
2833 if (attrptr
->num_values
!= 1)
2835 "status-message (text(255)) has %d values "
2836 "(RFC 2911 section 3.1.6.2).",
2837 attrptr
->num_values
);
2838 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2839 strlen(attrptr
->values
[0].string
.text
) > 255)
2841 "status-message (text(255)) has bad length %d"
2842 " (RFC 2911 section 3.1.6.2).",
2843 (int)strlen(attrptr
->values
[0].string
.text
));
2846 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
2847 IPP_TAG_ZERO
)) != NULL
)
2849 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2851 "detailed-status-message (text(MAX)) has wrong "
2852 "value tag %s (RFC 2911 section 3.1.6.3).",
2853 ippTagString(attrptr
->value_tag
));
2854 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2856 "detailed-status-message (text(MAX)) has wrong "
2857 "group tag %s (RFC 2911 section 3.1.6.3).",
2858 ippTagString(attrptr
->group_tag
));
2859 if (attrptr
->num_values
!= 1)
2861 "detailed-status-message (text(MAX)) has %d values"
2862 " (RFC 2911 section 3.1.6.3).",
2863 attrptr
->num_values
);
2864 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2865 strlen(attrptr
->values
[0].string
.text
) > 1023)
2867 "detailed-status-message (text(MAX)) has bad "
2868 "length %d (RFC 2911 section 3.1.6.3).",
2869 (int)strlen(attrptr
->values
[0].string
.text
));
2872 a
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2874 for (attrptr
= response
->attrs
,
2875 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2877 attrptr
= attrptr
->next
)
2879 if (attrptr
->group_tag
!= group
)
2881 int out_of_order
= 0; /* Are attribute groups out-of-order? */
2884 switch (attrptr
->group_tag
)
2889 case IPP_TAG_OPERATION
:
2893 case IPP_TAG_UNSUPPORTED_GROUP
:
2894 if (group
!= IPP_TAG_OPERATION
)
2899 case IPP_TAG_PRINTER
:
2900 if (group
!= IPP_TAG_OPERATION
&&
2901 group
!= IPP_TAG_UNSUPPORTED_GROUP
)
2905 case IPP_TAG_SUBSCRIPTION
:
2906 if (group
> attrptr
->group_tag
&&
2907 group
!= IPP_TAG_DOCUMENT
)
2912 if (group
> attrptr
->group_tag
)
2918 add_stringf(errors
, "Attribute groups out of order (%s < %s)",
2919 ippTagString(attrptr
->group_tag
),
2920 ippTagString(group
));
2922 if (attrptr
->group_tag
!= IPP_TAG_ZERO
)
2923 group
= attrptr
->group_tag
;
2926 validate_attr(outfile
, errors
, attrptr
);
2930 if (cupsArrayFind(a
, attrptr
->name
))
2931 add_stringf(errors
, "Duplicate \"%s\" attribute in %s group",
2932 attrptr
->name
, ippTagString(group
));
2934 cupsArrayAdd(a
, attrptr
->name
);
2941 * Now check the test-defined expected status-code and attribute
2945 for (i
= 0; i
< num_statuses
; i
++)
2947 if (statuses
[i
].if_defined
&&
2948 !get_variable(vars
, statuses
[i
].if_defined
))
2951 if (statuses
[i
].if_not_defined
&&
2952 get_variable(vars
, statuses
[i
].if_not_defined
))
2955 if (response
->request
.status
.status_code
== statuses
[i
].status
)
2957 if (statuses
[i
].repeat_match
&&
2958 repeat_count
< statuses
[i
].repeat_limit
)
2961 if (statuses
[i
].define_match
)
2962 set_variable(outfile
, vars
, statuses
[i
].define_match
, "1");
2968 if (statuses
[i
].repeat_no_match
&&
2969 repeat_count
< statuses
[i
].repeat_limit
)
2972 if (statuses
[i
].define_no_match
)
2974 set_variable(outfile
, vars
, statuses
[i
].define_no_match
, "1");
2980 if (i
== num_statuses
&& num_statuses
> 0)
2982 for (i
= 0; i
< num_statuses
; i
++)
2984 if (statuses
[i
].if_defined
&&
2985 !get_variable(vars
, statuses
[i
].if_defined
))
2988 if (statuses
[i
].if_not_defined
&&
2989 get_variable(vars
, statuses
[i
].if_not_defined
))
2992 if (!statuses
[i
].repeat_match
)
2993 add_stringf(errors
, "EXPECTED: STATUS %s (got %s)",
2994 ippErrorString(statuses
[i
].status
),
2995 ippErrorString(cupsLastError()));
2998 if ((attrptr
= ippFindAttribute(response
, "status-message",
2999 IPP_TAG_TEXT
)) != NULL
)
3000 add_stringf(errors
, "status-message=\"%s\"",
3001 attrptr
->values
[0].string
.text
);
3004 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3006 if (expect
->if_defined
&& !get_variable(vars
, expect
->if_defined
))
3009 if (expect
->if_not_defined
&&
3010 get_variable(vars
, expect
->if_not_defined
))
3013 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
3017 if ((found
&& expect
->not_expect
) ||
3018 (!found
&& !(expect
->not_expect
|| expect
->optional
)) ||
3019 (found
&& !expect_matches(expect
, found
->value_tag
)) ||
3020 (found
&& expect
->in_group
&&
3021 found
->group_tag
!= expect
->in_group
))
3023 if (expect
->define_no_match
)
3024 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3025 else if (!expect
->define_match
&& !expect
->define_value
)
3027 if (found
&& expect
->not_expect
)
3028 add_stringf(errors
, "NOT EXPECTED: %s", expect
->name
);
3029 else if (!found
&& !(expect
->not_expect
|| expect
->optional
))
3030 add_stringf(errors
, "EXPECTED: %s", expect
->name
);
3033 if (!expect_matches(expect
, found
->value_tag
))
3034 add_stringf(errors
, "EXPECTED: %s OF-TYPE %s (got %s)",
3035 expect
->name
, expect
->of_type
,
3036 ippTagString(found
->value_tag
));
3038 if (expect
->in_group
&& found
->group_tag
!= expect
->in_group
)
3039 add_stringf(errors
, "EXPECTED: %s IN-GROUP %s (got %s).",
3040 expect
->name
, ippTagString(expect
->in_group
),
3041 ippTagString(found
->group_tag
));
3045 if (expect
->repeat_no_match
&&
3046 repeat_count
< expect
->repeat_limit
)
3053 ippAttributeString(found
, buffer
, sizeof(buffer
));
3055 if (found
&& expect
->with_value_from
&& !with_value_from(NULL
, ippFindAttribute(response
, expect
->with_value_from
, IPP_TAG_ZERO
), found
, buffer
, sizeof(buffer
)))
3057 if (expect
->define_no_match
)
3058 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3059 else if (!expect
->define_match
&& !expect
->define_value
&& !expect
->repeat_match
&& !expect
->repeat_no_match
)
3061 add_stringf(errors
, "EXPECTED: %s WITH-VALUES-FROM %s", expect
->name
, expect
->with_value_from
);
3063 with_value_from(errors
, ippFindAttribute(response
, expect
->with_value_from
, IPP_TAG_ZERO
), found
, buffer
, sizeof(buffer
));
3066 if (expect
->repeat_no_match
&& repeat_count
< expect
->repeat_limit
)
3071 else if (found
&& !with_value(outfile
, NULL
, expect
->with_value
, expect
->with_flags
, found
, buffer
, sizeof(buffer
)))
3073 if (expect
->define_no_match
)
3074 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3075 else if (!expect
->define_match
&& !expect
->define_value
&&
3076 !expect
->repeat_match
&& !expect
->repeat_no_match
)
3078 if (expect
->with_flags
& _CUPS_WITH_REGEX
)
3079 add_stringf(errors
, "EXPECTED: %s %s /%s/",
3081 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
3082 "WITH-ALL-VALUES" : "WITH-VALUE",
3083 expect
->with_value
);
3085 add_stringf(errors
, "EXPECTED: %s %s \"%s\"",
3087 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
3088 "WITH-ALL-VALUES" : "WITH-VALUE",
3089 expect
->with_value
);
3091 with_value(outfile
, errors
, expect
->with_value
, expect
->with_flags
, found
,
3092 buffer
, sizeof(buffer
));
3095 if (expect
->repeat_no_match
&&
3096 repeat_count
< expect
->repeat_limit
)
3102 if (found
&& expect
->count
> 0 &&
3103 found
->num_values
!= expect
->count
)
3105 if (expect
->define_no_match
)
3106 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3107 else if (!expect
->define_match
&& !expect
->define_value
)
3109 add_stringf(errors
, "EXPECTED: %s COUNT %d (got %d)", expect
->name
,
3110 expect
->count
, found
->num_values
);
3113 if (expect
->repeat_no_match
&&
3114 repeat_count
< expect
->repeat_limit
)
3120 if (found
&& expect
->same_count_as
)
3122 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
3125 if (!attrptr
|| attrptr
->num_values
!= found
->num_values
)
3127 if (expect
->define_no_match
)
3128 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3129 else if (!expect
->define_match
&& !expect
->define_value
)
3133 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
3134 "(not returned)", expect
->name
,
3135 found
->num_values
, expect
->same_count_as
);
3136 else if (attrptr
->num_values
!= found
->num_values
)
3138 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
3139 "(%d values)", expect
->name
, found
->num_values
,
3140 expect
->same_count_as
, attrptr
->num_values
);
3143 if (expect
->repeat_no_match
&&
3144 repeat_count
< expect
->repeat_limit
)
3151 if (found
&& expect
->define_match
)
3152 set_variable(outfile
, vars
, expect
->define_match
, "1");
3154 if (found
&& expect
->define_value
)
3156 if (!expect
->with_value
)
3158 int last
= ippGetCount(found
) - 1;
3159 /* Last element in attribute */
3161 switch (ippGetValueTag(found
))
3164 case IPP_TAG_INTEGER
:
3165 snprintf(buffer
, sizeof(buffer
), "%d", ippGetInteger(found
, last
));
3168 case IPP_TAG_BOOLEAN
:
3169 if (ippGetBoolean(found
, last
))
3170 strlcpy(buffer
, "true", sizeof(buffer
));
3172 strlcpy(buffer
, "false", sizeof(buffer
));
3175 case IPP_TAG_RESOLUTION
:
3177 int xres
, /* Horizontal resolution */
3178 yres
; /* Vertical resolution */
3179 ipp_res_t units
; /* Resolution units */
3181 xres
= ippGetResolution(found
, last
, &yres
, &units
);
3184 snprintf(buffer
, sizeof(buffer
), "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
3186 snprintf(buffer
, sizeof(buffer
), "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
3190 case IPP_TAG_CHARSET
:
3191 case IPP_TAG_KEYWORD
:
3192 case IPP_TAG_LANGUAGE
:
3193 case IPP_TAG_MIMETYPE
:
3195 case IPP_TAG_NAMELANG
:
3197 case IPP_TAG_TEXTLANG
:
3199 case IPP_TAG_URISCHEME
:
3200 strlcpy(buffer
, ippGetString(found
, last
, NULL
), sizeof(buffer
));
3204 ippAttributeString(found
, buffer
, sizeof(buffer
));
3209 set_variable(outfile
, vars
, expect
->define_value
, buffer
);
3212 if (found
&& expect
->repeat_match
&&
3213 repeat_count
< expect
->repeat_limit
)
3216 while (expect
->expect_all
&& (found
= ippFindNextAttribute(response
, expect
->name
, IPP_TAG_ZERO
)) != NULL
);
3221 * If we are going to repeat this test, sleep 1 second so we don't flood
3222 * the printer with requests...
3227 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
3229 printf("%04d]\n", repeat_count
);
3232 if (num_displayed
> 0)
3234 for (attrptr
= ippFirstAttribute(response
); attrptr
; attrptr
= ippNextAttribute(response
))
3236 const char *attrname
= ippGetName(attrptr
);
3239 for (i
= 0; i
< num_displayed
; i
++)
3241 if (!strcmp(displayed
[i
], attrname
))
3243 print_attr(stdout
, _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
3252 sleep((unsigned)repeat_interval
);
3253 repeat_interval
= _cupsNextDelay(repeat_interval
, &repeat_prev
);
3255 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
3257 printf(" %-68.68s [", name
);
3262 while (repeat_test
);
3268 if (cupsArrayCount(errors
) > 0)
3269 prev_pass
= pass
= 0;
3276 if (Output
== _CUPS_OUTPUT_PLIST
)
3278 fputs("<key>Successful</key>\n", outfile
);
3279 fputs(prev_pass
? "<true />\n" : "<false />\n", outfile
);
3280 fputs("<key>StatusCode</key>\n", outfile
);
3281 print_xml_string(outfile
, "string", ippErrorString(cupsLastError()));
3282 fputs("<key>ResponseAttributes</key>\n", outfile
);
3283 fputs("<array>\n", outfile
);
3284 fputs("<dict>\n", outfile
);
3285 for (attrptr
= response
? response
->attrs
: NULL
,
3286 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
3288 attrptr
= attrptr
->next
)
3289 print_attr(outfile
, Output
, attrptr
, &group
);
3290 fputs("</dict>\n", outfile
);
3291 fputs("</array>\n", outfile
);
3294 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
3296 puts(prev_pass
? "PASS]" : "FAIL]");
3298 if (!prev_pass
|| (Verbosity
&& response
))
3300 printf(" RECEIVED: %lu bytes in response\n",
3301 (unsigned long)ippLength(response
));
3302 printf(" status-code = %s (%s)\n", ippErrorString(cupsLastError()),
3303 cupsLastErrorString());
3305 if (Verbosity
&& response
)
3307 for (attrptr
= response
->attrs
;
3309 attrptr
= attrptr
->next
)
3310 print_attr(stdout
, _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
3314 else if (!prev_pass
&& Output
!= _CUPS_OUTPUT_QUIET
)
3315 fprintf(stderr
, "%s\n", cupsLastErrorString());
3317 if (prev_pass
&& Output
>= _CUPS_OUTPUT_LIST
&& !Verbosity
&&
3320 size_t width
; /* Length of value */
3322 for (i
= 0; i
< num_displayed
; i
++)
3324 widths
[i
] = strlen(displayed
[i
]);
3326 for (attrptr
= ippFindAttribute(response
, displayed
[i
], IPP_TAG_ZERO
);
3328 attrptr
= ippFindNextAttribute(response
, displayed
[i
],
3331 width
= ippAttributeString(attrptr
, NULL
, 0);
3332 if (width
> widths
[i
])
3337 if (Output
== _CUPS_OUTPUT_CSV
)
3338 print_csv(outfile
, NULL
, num_displayed
, displayed
, widths
);
3340 print_line(outfile
, NULL
, num_displayed
, displayed
, widths
);
3342 attrptr
= response
->attrs
;
3346 while (attrptr
&& attrptr
->group_tag
<= IPP_TAG_OPERATION
)
3347 attrptr
= attrptr
->next
;
3351 if (Output
== _CUPS_OUTPUT_CSV
)
3352 print_csv(outfile
, attrptr
, num_displayed
, displayed
, widths
);
3354 print_line(outfile
, attrptr
, num_displayed
, displayed
, widths
);
3356 while (attrptr
&& attrptr
->group_tag
> IPP_TAG_OPERATION
)
3357 attrptr
= attrptr
->next
;
3361 else if (!prev_pass
)
3363 if (Output
== _CUPS_OUTPUT_PLIST
)
3365 fputs("<key>Errors</key>\n", outfile
);
3366 fputs("<array>\n", outfile
);
3368 for (error
= (char *)cupsArrayFirst(errors
);
3370 error
= (char *)cupsArrayNext(errors
))
3371 print_xml_string(outfile
, "string", error
);
3373 fputs("</array>\n", outfile
);
3376 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
3378 for (error
= (char *)cupsArrayFirst(errors
);
3380 error
= (char *)cupsArrayNext(errors
))
3381 printf(" %s\n", error
);
3385 if (num_displayed
> 0 && !Verbosity
&& response
&& (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
)))
3387 for (attrptr
= response
->attrs
;
3389 attrptr
= attrptr
->next
)
3393 for (i
= 0; i
< num_displayed
; i
++)
3395 if (!strcmp(displayed
[i
], attrptr
->name
))
3397 print_attr(outfile
, Output
, attrptr
, NULL
);
3407 if (Output
== _CUPS_OUTPUT_PLIST
)
3408 fputs("</dict>\n", outfile
);
3412 ippDelete(response
);
3415 for (i
= 0; i
< num_statuses
; i
++)
3417 if (statuses
[i
].if_defined
)
3418 free(statuses
[i
].if_defined
);
3419 if (statuses
[i
].if_not_defined
)
3420 free(statuses
[i
].if_not_defined
);
3421 if (statuses
[i
].define_match
)
3422 free(statuses
[i
].define_match
);
3423 if (statuses
[i
].define_no_match
)
3424 free(statuses
[i
].define_no_match
);
3428 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3431 if (expect
->of_type
)
3432 free(expect
->of_type
);
3433 if (expect
->same_count_as
)
3434 free(expect
->same_count_as
);
3435 if (expect
->if_defined
)
3436 free(expect
->if_defined
);
3437 if (expect
->if_not_defined
)
3438 free(expect
->if_not_defined
);
3439 if (expect
->with_value
)
3440 free(expect
->with_value
);
3441 if (expect
->define_match
)
3442 free(expect
->define_match
);
3443 if (expect
->define_no_match
)
3444 free(expect
->define_no_match
);
3445 if (expect
->define_value
)
3446 free(expect
->define_value
);
3450 for (i
= 0; i
< num_displayed
; i
++)
3454 if (!ignore_errors
&& !prev_pass
)
3460 cupsArrayDelete(errors
);
3467 ippDelete(response
);
3469 for (i
= 0; i
< num_statuses
; i
++)
3471 if (statuses
[i
].if_defined
)
3472 free(statuses
[i
].if_defined
);
3473 if (statuses
[i
].if_not_defined
)
3474 free(statuses
[i
].if_not_defined
);
3475 if (statuses
[i
].define_match
)
3476 free(statuses
[i
].define_match
);
3477 if (statuses
[i
].define_no_match
)
3478 free(statuses
[i
].define_no_match
);
3481 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3484 if (expect
->of_type
)
3485 free(expect
->of_type
);
3486 if (expect
->same_count_as
)
3487 free(expect
->same_count_as
);
3488 if (expect
->if_defined
)
3489 free(expect
->if_defined
);
3490 if (expect
->if_not_defined
)
3491 free(expect
->if_not_defined
);
3492 if (expect
->with_value
)
3493 free(expect
->with_value
);
3494 if (expect
->define_match
)
3495 free(expect
->define_match
);
3496 if (expect
->define_no_match
)
3497 free(expect
->define_no_match
);
3498 if (expect
->define_value
)
3499 free(expect
->define_value
);
3502 for (i
= 0; i
< num_displayed
; i
++)
3510 * 'expand_variables()' - Expand variables in a string.
3514 expand_variables(_cups_vars_t
*vars
, /* I - Variables */
3515 char *dst
, /* I - Destination string buffer */
3516 const char *src
, /* I - Source string */
3517 size_t dstsize
) /* I - Size of destination buffer */
3519 char *dstptr
, /* Pointer into destination */
3520 *dstend
, /* End of destination */
3521 temp
[256], /* Temporary string */
3522 *tempptr
; /* Pointer into temporary string */
3523 const char *value
; /* Value to substitute */
3527 dstend
= dst
+ dstsize
- 1;
3529 while (*src
&& dstptr
< dstend
)
3534 * Substitute a string/number...
3537 if (!strncmp(src
, "$$", 2))
3542 else if (!strncmp(src
, "$ENV[", 5))
3544 strlcpy(temp
, src
+ 5, sizeof(temp
));
3546 for (tempptr
= temp
; *tempptr
; tempptr
++)
3547 if (*tempptr
== ']')
3553 value
= getenv(temp
);
3554 src
+= tempptr
- temp
+ 5;
3561 strlcpy(temp
, src
, sizeof(temp
));
3562 if ((tempptr
= strchr(temp
, '}')) != NULL
)
3565 tempptr
= temp
+ strlen(temp
);
3569 strlcpy(temp
, src
+ 1, sizeof(temp
));
3571 for (tempptr
= temp
; *tempptr
; tempptr
++)
3572 if (!isalnum(*tempptr
& 255) && *tempptr
!= '-' && *tempptr
!= '_')
3579 if (!strcmp(temp
, "uri"))
3581 else if (!strcmp(temp
, "filename"))
3582 value
= vars
->filename
;
3583 else if (!strcmp(temp
, "scheme") || !strcmp(temp
, "method"))
3584 value
= vars
->scheme
;
3585 else if (!strcmp(temp
, "username"))
3586 value
= vars
->userpass
;
3587 else if (!strcmp(temp
, "hostname"))
3588 value
= vars
->hostname
;
3589 else if (!strcmp(temp
, "port"))
3591 snprintf(temp
, sizeof(temp
), "%d", vars
->port
);
3594 else if (!strcmp(temp
, "resource"))
3595 value
= vars
->resource
;
3596 else if (!strcmp(temp
, "user"))
3599 value
= get_variable(vars
, temp
);
3601 src
+= tempptr
- temp
+ 1;
3606 strlcpy(dstptr
, value
, (size_t)(dstend
- dstptr
+ 1));
3607 dstptr
+= strlen(dstptr
);
3619 * 'expect_matches()' - Return true if the tag matches the specification.
3622 static int /* O - 1 if matches, 0 otherwise */
3624 _cups_expect_t
*expect
, /* I - Expected attribute */
3625 ipp_tag_t value_tag
) /* I - Value tag for attribute */
3627 int match
; /* Match? */
3628 char *of_type
, /* Type name to match */
3629 *next
, /* Next name to match */
3630 sep
; /* Separator character */
3634 * If we don't expect a particular type, return immediately...
3637 if (!expect
->of_type
)
3641 * Parse the "of_type" value since the string can contain multiple attribute
3642 * types separated by "," or "|"...
3645 for (of_type
= expect
->of_type
, match
= 0; !match
&& *of_type
; of_type
= next
)
3648 * Find the next separator, and set it (temporarily) to nul if present.
3651 for (next
= of_type
; *next
&& *next
!= '|' && *next
!= ','; next
++);
3653 if ((sep
= *next
) != '\0')
3657 * Support some meta-types to make it easier to write the test file.
3660 if (!strcmp(of_type
, "text"))
3661 match
= value_tag
== IPP_TAG_TEXTLANG
|| value_tag
== IPP_TAG_TEXT
;
3662 else if (!strcmp(of_type
, "name"))
3663 match
= value_tag
== IPP_TAG_NAMELANG
|| value_tag
== IPP_TAG_NAME
;
3664 else if (!strcmp(of_type
, "collection"))
3665 match
= value_tag
== IPP_TAG_BEGIN_COLLECTION
;
3667 match
= value_tag
== ippTagValue(of_type
);
3670 * Restore the separator if we have one...
3682 * 'get_collection()' - Get a collection value from the current test file.
3685 static ipp_t
* /* O - Collection value */
3686 get_collection(FILE *outfile
, /* I - Output file */
3687 _cups_vars_t
*vars
, /* I - Variables */
3688 FILE *fp
, /* I - File to read from */
3689 int *linenum
) /* IO - Line number */
3691 char token
[1024], /* Token from file */
3692 temp
[1024], /* Temporary string */
3693 attr
[128]; /* Attribute name */
3694 ipp_tag_t value
; /* Current value type */
3695 ipp_t
*col
= ippNew(); /* Collection value */
3696 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
3699 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
3701 if (!strcmp(token
, "}"))
3703 else if (!strcmp(token
, "{") && lastcol
)
3706 * Another collection value
3709 ipp_t
*subcol
= get_collection(outfile
, vars
, fp
, linenum
);
3710 /* Collection value */
3713 ippSetCollection(col
, &lastcol
, ippGetCount(lastcol
), subcol
);
3717 else if (!_cups_strcasecmp(token
, "MEMBER"))
3725 if (!get_token(fp
, token
, sizeof(token
), linenum
))
3727 print_fatal_error(outfile
, "Missing MEMBER value tag on line %d.", *linenum
);
3731 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
3733 print_fatal_error(outfile
, "Bad MEMBER value tag \"%s\" on line %d.", token
,
3738 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
3740 print_fatal_error(outfile
, "Missing MEMBER name on line %d.", *linenum
);
3744 if (!get_token(fp
, temp
, sizeof(temp
), linenum
))
3746 print_fatal_error(outfile
, "Missing MEMBER value on line %d.", *linenum
);
3750 expand_variables(vars
, token
, temp
, sizeof(token
));
3754 case IPP_TAG_BOOLEAN
:
3755 if (!_cups_strcasecmp(token
, "true"))
3756 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
3758 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, (char)atoi(token
));
3761 case IPP_TAG_INTEGER
:
3763 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
3766 case IPP_TAG_RESOLUTION
:
3768 int xres
, /* X resolution */
3769 yres
; /* Y resolution */
3770 char units
[6]; /* Units */
3772 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
3773 (_cups_strcasecmp(units
, "dpi") &&
3774 _cups_strcasecmp(units
, "dpc") &&
3775 _cups_strcasecmp(units
, "dpcm") &&
3776 _cups_strcasecmp(units
, "other")))
3778 print_fatal_error(outfile
, "Bad resolution value \"%s\" on line %d.",
3783 if (!_cups_strcasecmp(units
, "dpi"))
3784 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
3785 else if (!_cups_strcasecmp(units
, "dpc") ||
3786 !_cups_strcasecmp(units
, "dpcm"))
3787 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_CM
, xres
, yres
);
3789 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, (ipp_res_t
)0, xres
, yres
);
3793 case IPP_TAG_RANGE
:
3795 int lowers
[4], /* Lower value */
3796 uppers
[4], /* Upper values */
3797 num_vals
; /* Number of values */
3800 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
3801 lowers
+ 0, uppers
+ 0,
3802 lowers
+ 1, uppers
+ 1,
3803 lowers
+ 2, uppers
+ 2,
3804 lowers
+ 3, uppers
+ 3);
3806 if ((num_vals
& 1) || num_vals
== 0)
3808 print_fatal_error(outfile
, "Bad rangeOfInteger value \"%s\" on line %d.",
3813 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
3818 case IPP_TAG_BEGIN_COLLECTION
:
3819 if (!strcmp(token
, "{"))
3821 ipp_t
*subcol
= get_collection(outfile
, vars
, fp
, linenum
);
3822 /* Collection value */
3826 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
3834 print_fatal_error(outfile
, "Bad collection value on line %d.", *linenum
);
3838 case IPP_TAG_STRING
:
3839 ippAddOctetString(col
, IPP_TAG_ZERO
, attr
, token
, (int)strlen(token
));
3843 if (!strchr(token
, ','))
3844 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
3848 * Multiple string values...
3851 int num_values
; /* Number of values */
3852 char *values
[100], /* Values */
3853 *ptr
; /* Pointer to next value */
3859 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
3862 values
[num_values
] = ptr
;
3866 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
3867 NULL
, (const char **)values
);
3877 * If we get here there was a parse error; free memory and return.
3889 * 'get_filename()' - Get a filename based on the current test file.
3892 static char * /* O - Filename */
3893 get_filename(const char *testfile
, /* I - Current test file */
3894 char *dst
, /* I - Destination filename */
3895 const char *src
, /* I - Source filename */
3896 size_t dstsize
) /* I - Size of destination buffer */
3898 char *dstptr
; /* Pointer into destination */
3899 _cups_globals_t
*cg
= _cupsGlobals();
3903 if (*src
== '<' && src
[strlen(src
) - 1] == '>')
3906 * Map <filename> to CUPS_DATADIR/ipptool/filename...
3909 snprintf(dst
, dstsize
, "%s/ipptool/%s", cg
->cups_datadir
, src
+ 1);
3910 dstptr
= dst
+ strlen(dst
) - 1;
3914 else if (*src
== '/' || !strchr(testfile
, '/')
3916 || (isalpha(*src
& 255) && src
[1] == ':')
3921 * Use the path as-is...
3924 strlcpy(dst
, src
, dstsize
);
3929 * Make path relative to testfile...
3932 strlcpy(dst
, testfile
, dstsize
);
3933 if ((dstptr
= strrchr(dst
, '/')) != NULL
)
3936 dstptr
= dst
; /* Should never happen */
3938 strlcpy(dstptr
, src
, dstsize
- (size_t)(dstptr
- dst
));
3946 * 'get_string()' - Get a pointer to a string value or the portion of interest.
3949 static const char * /* O - Pointer to string */
3950 get_string(ipp_attribute_t
*attr
, /* I - IPP attribute */
3951 int element
, /* I - Element to fetch */
3952 int flags
, /* I - Value ("with") flags */
3953 char *buffer
, /* I - Temporary buffer */
3954 size_t bufsize
) /* I - Size of temporary buffer */
3956 const char *value
; /* Value */
3957 char *ptr
, /* Pointer into value */
3958 scheme
[256], /* URI scheme */
3959 userpass
[256], /* Username/password */
3960 hostname
[256], /* Hostname */
3961 resource
[1024]; /* Resource */
3962 int port
; /* Port number */
3965 value
= ippGetString(attr
, element
, NULL
);
3967 if (flags
& _CUPS_WITH_HOSTNAME
)
3969 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), buffer
, (int)bufsize
, &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
3972 ptr
= buffer
+ strlen(buffer
) - 1;
3973 if (ptr
>= buffer
&& *ptr
== '.')
3974 *ptr
= '\0'; /* Drop trailing "." */
3978 else if (flags
& _CUPS_WITH_RESOURCE
)
3980 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, buffer
, (int)bufsize
) < HTTP_URI_STATUS_OK
)
3985 else if (flags
& _CUPS_WITH_SCHEME
)
3987 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, buffer
, (int)bufsize
, userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
3992 else if (ippGetValueTag(attr
) == IPP_TAG_URI
&& (!strncmp(value
, "ipp://", 6) || !strncmp(value
, "http://", 7) || !strncmp(value
, "ipps://", 7) || !strncmp(value
, "https://", 8)))
3994 http_uri_status_t status
= httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
));
3996 if (status
< HTTP_URI_STATUS_OK
)
4007 * Normalize URI with no trailing dot...
4010 if ((ptr
= hostname
+ strlen(hostname
) - 1) >= hostname
&& *ptr
== '.')
4013 httpAssembleURI(HTTP_URI_CODING_ALL
, buffer
, (int)bufsize
, scheme
, userpass
, hostname
, port
, resource
);
4024 * 'get_token()' - Get a token from a file.
4027 static char * /* O - Token from file or NULL on EOF */
4028 get_token(FILE *fp
, /* I - File to read from */
4029 char *buf
, /* I - Buffer to read into */
4030 int buflen
, /* I - Length of buffer */
4031 int *linenum
) /* IO - Current line number */
4033 int ch
, /* Character from file */
4034 quote
; /* Quoting character */
4035 char *bufptr
, /* Pointer into buffer */
4036 *bufend
; /* End of buffer */
4042 * Skip whitespace...
4045 while (isspace(ch
= getc(fp
)))
4057 else if (ch
== '\'' || ch
== '\"')
4060 * Quoted text or regular expression...
4065 bufend
= buf
+ buflen
- 1;
4067 while ((ch
= getc(fp
)) != EOF
)
4072 * Escape next character...
4075 if (bufptr
< bufend
)
4076 *bufptr
++ = (char)ch
;
4078 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
4079 *bufptr
++ = (char)ch
;
4081 else if (ch
== quote
)
4083 else if (bufptr
< bufend
)
4084 *bufptr
++ = (char)ch
;
4097 while ((ch
= getc(fp
)) != EOF
)
4103 else if (ch
== '{' || ch
== '}' || ch
== ',')
4113 * Whitespace delimited text...
4119 bufend
= buf
+ buflen
- 1;
4121 while ((ch
= getc(fp
)) != EOF
)
4122 if (isspace(ch
) || ch
== '#')
4124 else if (bufptr
< bufend
)
4125 *bufptr
++ = (char)ch
;
4129 else if (ch
== '\n')
4141 * 'get_variable()' - Get the value of a variable.
4144 static char * /* O - Value or NULL */
4145 get_variable(_cups_vars_t
*vars
, /* I - Variables */
4146 const char *name
) /* I - Variable name */
4148 _cups_var_t key
, /* Search key */
4149 *match
; /* Matching variable, if any */
4152 key
.name
= (char *)name
;
4153 match
= cupsArrayFind(vars
->vars
, &key
);
4155 return (match
? match
->value
: NULL
);
4160 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
4164 static char * /* O - ISO 8601 date/time string */
4165 iso_date(ipp_uchar_t
*date
) /* I - IPP (RFC 1903) date/time value */
4167 time_t utctime
; /* UTC time since 1970 */
4168 struct tm
*utcdate
; /* UTC date/time */
4169 static char buffer
[255]; /* String buffer */
4172 utctime
= ippDateToTime(date
);
4173 utcdate
= gmtime(&utctime
);
4175 snprintf(buffer
, sizeof(buffer
), "%04d-%02d-%02dT%02d:%02d:%02dZ",
4176 utcdate
->tm_year
+ 1900, utcdate
->tm_mon
+ 1, utcdate
->tm_mday
,
4177 utcdate
->tm_hour
, utcdate
->tm_min
, utcdate
->tm_sec
);
4184 * 'password_cb()' - Password callback for authenticated tests.
4187 static const char * /* O - Password */
4188 password_cb(const char *prompt
) /* I - Prompt (unused) */
4192 if (PasswordTries
< 3)
4196 cupsSetUser(Username
);
4206 * 'pause_message()' - Display the message and pause until the user presses a key.
4210 pause_message(const char *message
) /* I - Message */
4213 HANDLE tty
; /* Console handle */
4214 DWORD mode
; /* Console mode */
4215 char key
; /* Key press */
4216 DWORD bytes
; /* Bytes read for key press */
4220 * Disable input echo and set raw input...
4223 if ((tty
= GetStdHandle(STD_INPUT_HANDLE
)) == INVALID_HANDLE_VALUE
)
4226 if (!GetConsoleMode(tty
, &mode
))
4229 if (!SetConsoleMode(tty
, 0))
4233 int tty
; /* /dev/tty - never read from stdin */
4234 struct termios original
, /* Original input mode */
4235 noecho
; /* No echo input mode */
4236 char key
; /* Current key press */
4240 * Disable input echo and set raw input...
4243 if ((tty
= open("/dev/tty", O_RDONLY
)) < 0)
4246 if (tcgetattr(tty
, &original
))
4253 noecho
.c_lflag
&= (tcflag_t
)~(ICANON
| ECHO
| ECHOE
| ISIG
);
4255 if (tcsetattr(tty
, TCSAFLUSH
, &noecho
))
4263 * Display the prompt...
4266 printf("%s\n---- PRESS ANY KEY ----", message
);
4274 ReadFile(tty
, &key
, 1, &bytes
, NULL
);
4280 SetConsoleMode(tty
, mode
);
4293 tcsetattr(tty
, TCSAFLUSH
, &original
);
4298 * Erase the "press any key" prompt...
4301 fputs("\r \r", stdout
);
4307 * 'print_attr()' - Print an attribute on the screen.
4311 print_attr(FILE *outfile
, /* I - Output file */
4312 int format
, /* I - Output format */
4313 ipp_attribute_t
*attr
, /* I - Attribute to print */
4314 ipp_tag_t
*group
) /* IO - Current group */
4316 int i
; /* Looping var */
4317 ipp_attribute_t
*colattr
; /* Collection attribute */
4320 if (format
== _CUPS_OUTPUT_PLIST
)
4322 if (!attr
->name
|| (group
&& *group
!= attr
->group_tag
))
4324 if (attr
->group_tag
!= IPP_TAG_ZERO
)
4326 fputs("</dict>\n", outfile
);
4327 fputs("<dict>\n", outfile
);
4331 *group
= attr
->group_tag
;
4337 print_xml_string(outfile
, "key", attr
->name
);
4338 if (attr
->num_values
> 1)
4339 fputs("<array>\n", outfile
);
4341 switch (attr
->value_tag
)
4343 case IPP_TAG_INTEGER
:
4345 for (i
= 0; i
< attr
->num_values
; i
++)
4346 fprintf(outfile
, "<integer>%d</integer>\n", attr
->values
[i
].integer
);
4349 case IPP_TAG_BOOLEAN
:
4350 for (i
= 0; i
< attr
->num_values
; i
++)
4351 fputs(attr
->values
[i
].boolean
? "<true />\n" : "<false />\n", outfile
);
4354 case IPP_TAG_RANGE
:
4355 for (i
= 0; i
< attr
->num_values
; i
++)
4356 fprintf(outfile
, "<dict><key>lower</key><integer>%d</integer>"
4357 "<key>upper</key><integer>%d</integer></dict>\n",
4358 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
4361 case IPP_TAG_RESOLUTION
:
4362 for (i
= 0; i
< attr
->num_values
; i
++)
4363 fprintf(outfile
, "<dict><key>xres</key><integer>%d</integer>"
4364 "<key>yres</key><integer>%d</integer>"
4365 "<key>units</key><string>%s</string></dict>\n",
4366 attr
->values
[i
].resolution
.xres
,
4367 attr
->values
[i
].resolution
.yres
,
4368 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
4373 for (i
= 0; i
< attr
->num_values
; i
++)
4374 fprintf(outfile
, "<date>%s</date>\n", iso_date(attr
->values
[i
].date
));
4377 case IPP_TAG_STRING
:
4378 for (i
= 0; i
< attr
->num_values
; i
++)
4380 char buffer
[IPP_MAX_LENGTH
* 5 / 4 + 1];
4383 fprintf(outfile
, "<data>%s</data>\n",
4384 httpEncode64_2(buffer
, sizeof(buffer
),
4385 attr
->values
[i
].unknown
.data
,
4386 attr
->values
[i
].unknown
.length
));
4392 case IPP_TAG_KEYWORD
:
4393 case IPP_TAG_CHARSET
:
4395 case IPP_TAG_MIMETYPE
:
4396 case IPP_TAG_LANGUAGE
:
4397 for (i
= 0; i
< attr
->num_values
; i
++)
4398 print_xml_string(outfile
, "string", attr
->values
[i
].string
.text
);
4401 case IPP_TAG_TEXTLANG
:
4402 case IPP_TAG_NAMELANG
:
4403 for (i
= 0; i
< attr
->num_values
; i
++)
4405 fputs("<dict><key>language</key><string>", outfile
);
4406 print_xml_string(outfile
, NULL
, attr
->values
[i
].string
.language
);
4407 fputs("</string><key>string</key><string>", outfile
);
4408 print_xml_string(outfile
, NULL
, attr
->values
[i
].string
.text
);
4409 fputs("</string></dict>\n", outfile
);
4413 case IPP_TAG_BEGIN_COLLECTION
:
4414 for (i
= 0; i
< attr
->num_values
; i
++)
4416 fputs("<dict>\n", outfile
);
4417 for (colattr
= attr
->values
[i
].collection
->attrs
;
4419 colattr
= colattr
->next
)
4420 print_attr(outfile
, format
, colattr
, NULL
);
4421 fputs("</dict>\n", outfile
);
4426 fprintf(outfile
, "<string><<%s>></string>\n", ippTagString(attr
->value_tag
));
4430 if (attr
->num_values
> 1)
4431 fputs("</array>\n", outfile
);
4435 char buffer
[8192]; /* Value buffer */
4437 if (format
== _CUPS_OUTPUT_TEST
)
4441 fputs(" -- separator --\n", outfile
);
4445 fprintf(outfile
, " %s (%s%s) = ", attr
->name
, attr
->num_values
> 1 ? "1setOf " : "", ippTagString(attr
->value_tag
));
4448 ippAttributeString(attr
, buffer
, sizeof(buffer
));
4449 fprintf(outfile
, "%s\n", buffer
);
4455 * 'print_csv()' - Print a line of CSV text.
4460 FILE *outfile
, /* I - Output file */
4461 ipp_attribute_t
*attr
, /* I - First attribute for line */
4462 int num_displayed
, /* I - Number of attributes to display */
4463 char **displayed
, /* I - Attributes to display */
4464 size_t *widths
) /* I - Column widths */
4466 int i
; /* Looping var */
4467 size_t maxlength
; /* Max length of all columns */
4468 char *buffer
, /* String buffer */
4469 *bufptr
; /* Pointer into buffer */
4470 ipp_attribute_t
*current
; /* Current attribute */
4474 * Get the maximum string length we have to show and allocate...
4477 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4478 if (widths
[i
] > maxlength
)
4479 maxlength
= widths
[i
];
4483 if ((buffer
= malloc(maxlength
)) == NULL
)
4487 * Loop through the attributes to display...
4492 for (i
= 0; i
< num_displayed
; i
++)
4495 fputc(',', outfile
);
4499 for (current
= attr
; current
; current
= current
->next
)
4503 else if (!strcmp(current
->name
, displayed
[i
]))
4505 ippAttributeString(current
, buffer
, maxlength
);
4510 if (strchr(buffer
, ',') != NULL
|| strchr(buffer
, '\"') != NULL
||
4511 strchr(buffer
, '\\') != NULL
)
4513 putc('\"', outfile
);
4514 for (bufptr
= buffer
; *bufptr
; bufptr
++)
4516 if (*bufptr
== '\\' || *bufptr
== '\"')
4517 putc('\\', outfile
);
4518 putc(*bufptr
, outfile
);
4520 putc('\"', outfile
);
4523 fputs(buffer
, outfile
);
4525 putc('\n', outfile
);
4529 for (i
= 0; i
< num_displayed
; i
++)
4534 fputs(displayed
[i
], outfile
);
4536 putc('\n', outfile
);
4544 * 'print_fatal_error()' - Print a fatal error message.
4548 print_fatal_error(FILE *outfile
, /* I - Output file */
4549 const char *s
, /* I - Printf-style format string */
4550 ...) /* I - Additional arguments as needed */
4552 char buffer
[10240]; /* Format buffer */
4553 va_list ap
; /* Pointer to arguments */
4557 * Format the error message...
4561 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
4568 if (Output
== _CUPS_OUTPUT_PLIST
)
4570 print_xml_header(outfile
);
4571 print_xml_trailer(outfile
, 0, buffer
);
4574 _cupsLangPrintf(stderr
, "ipptool: %s", buffer
);
4579 * 'print_line()' - Print a line of formatted or CSV text.
4584 FILE *outfile
, /* I - Output file */
4585 ipp_attribute_t
*attr
, /* I - First attribute for line */
4586 int num_displayed
, /* I - Number of attributes to display */
4587 char **displayed
, /* I - Attributes to display */
4588 size_t *widths
) /* I - Column widths */
4590 int i
; /* Looping var */
4591 size_t maxlength
; /* Max length of all columns */
4592 char *buffer
; /* String buffer */
4593 ipp_attribute_t
*current
; /* Current attribute */
4597 * Get the maximum string length we have to show and allocate...
4600 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4601 if (widths
[i
] > maxlength
)
4602 maxlength
= widths
[i
];
4606 if ((buffer
= malloc(maxlength
)) == NULL
)
4610 * Loop through the attributes to display...
4615 for (i
= 0; i
< num_displayed
; i
++)
4622 for (current
= attr
; current
; current
= current
->next
)
4626 else if (!strcmp(current
->name
, displayed
[i
]))
4628 ippAttributeString(current
, buffer
, maxlength
);
4633 fprintf(outfile
, "%*s", (int)-widths
[i
], buffer
);
4635 putc('\n', outfile
);
4639 for (i
= 0; i
< num_displayed
; i
++)
4644 fprintf(outfile
, "%*s", (int)-widths
[i
], displayed
[i
]);
4646 putc('\n', outfile
);
4648 for (i
= 0; i
< num_displayed
; i
++)
4653 memset(buffer
, '-', widths
[i
]);
4654 buffer
[widths
[i
]] = '\0';
4655 fputs(buffer
, outfile
);
4657 putc('\n', outfile
);
4665 * 'print_xml_header()' - Print a standard XML plist header.
4669 print_xml_header(FILE *outfile
) /* I - Output file */
4673 fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", outfile
);
4674 fputs("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
4675 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n", outfile
);
4676 fputs("<plist version=\"1.0\">\n", outfile
);
4677 fputs("<dict>\n", outfile
);
4678 fputs("<key>ipptoolVersion</key>\n", outfile
);
4679 fputs("<string>" CUPS_SVERSION
"</string>\n", outfile
);
4680 fputs("<key>Transfer</key>\n", outfile
);
4681 fprintf(outfile
, "<string>%s</string>\n",
4682 Transfer
== _CUPS_TRANSFER_AUTO
? "auto" :
4683 Transfer
== _CUPS_TRANSFER_CHUNKED
? "chunked" : "length");
4684 fputs("<key>Tests</key>\n", outfile
);
4685 fputs("<array>\n", outfile
);
4693 * 'print_xml_string()' - Print an XML string with escaping.
4697 print_xml_string(FILE *outfile
, /* I - Output file */
4698 const char *element
, /* I - Element name or NULL */
4699 const char *s
) /* I - String to print */
4702 fprintf(outfile
, "<%s>", element
);
4707 fputs("&", outfile
);
4709 fputs("<", outfile
);
4711 fputs(">", outfile
);
4712 else if ((*s
& 0xe0) == 0xc0)
4715 * Validate UTF-8 two-byte sequence...
4718 if ((s
[1] & 0xc0) != 0x80)
4725 putc(*s
++, outfile
);
4729 else if ((*s
& 0xf0) == 0xe0)
4732 * Validate UTF-8 three-byte sequence...
4735 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80)
4742 putc(*s
++, outfile
);
4743 putc(*s
++, outfile
);
4747 else if ((*s
& 0xf8) == 0xf0)
4750 * Validate UTF-8 four-byte sequence...
4753 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80 ||
4754 (s
[3] & 0xc0) != 0x80)
4761 putc(*s
++, outfile
);
4762 putc(*s
++, outfile
);
4763 putc(*s
++, outfile
);
4767 else if ((*s
& 0x80) || (*s
< ' ' && !isspace(*s
& 255)))
4770 * Invalid control character...
4782 fprintf(outfile
, "</%s>\n", element
);
4787 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
4791 print_xml_trailer(FILE *outfile
, /* I - Output file */
4792 int success
, /* I - 1 on success, 0 on failure */
4793 const char *message
) /* I - Error message or NULL */
4797 fputs("</array>\n", outfile
);
4798 fputs("<key>Successful</key>\n", outfile
);
4799 fputs(success
? "<true />\n" : "<false />\n", outfile
);
4802 fputs("<key>ErrorMessage</key>\n", outfile
);
4803 print_xml_string(outfile
, "string", message
);
4805 fputs("</dict>\n", outfile
);
4806 fputs("</plist>\n", outfile
);
4814 * 'set_variable()' - Set a variable value.
4818 set_variable(FILE *outfile
, /* I - Output file */
4819 _cups_vars_t
*vars
, /* I - Variables */
4820 const char *name
, /* I - Variable name */
4821 const char *value
) /* I - Value string */
4823 _cups_var_t key
, /* Search key */
4824 *var
; /* New variable */
4827 if (!_cups_strcasecmp(name
, "filename"))
4830 free(vars
->filename
);
4832 vars
->filename
= strdup(value
);
4836 key
.name
= (char *)name
;
4837 if ((var
= cupsArrayFind(vars
->vars
, &key
)) != NULL
)
4840 var
->value
= strdup(value
);
4842 else if ((var
= malloc(sizeof(_cups_var_t
))) == NULL
)
4844 print_fatal_error(outfile
, "Unable to allocate memory for variable \"%s\".", name
);
4849 var
->name
= strdup(name
);
4850 var
->value
= strdup(value
);
4852 cupsArrayAdd(vars
->vars
, var
);
4859 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
4863 sigterm_handler(int sig
) /* I - Signal number (unused) */
4869 signal(SIGINT
, SIG_DFL
);
4870 signal(SIGTERM
, SIG_DFL
);
4876 * 'timeout_cb()' - Handle HTTP timeouts.
4879 static int /* O - 1 to continue, 0 to cancel */
4880 timeout_cb(http_t
*http
, /* I - Connection to server */
4881 void *user_data
) /* I - User data (unused) */
4883 int buffered
= 0; /* Bytes buffered but not yet sent */
4889 * If the socket still have data waiting to be sent to the printer (as can
4890 * happen if the printer runs out of paper), continue to wait until the output
4891 * buffer is empty...
4894 #ifdef SO_NWRITE /* OS X and some versions of Linux */
4895 socklen_t len
= sizeof(buffered
); /* Size of return value */
4897 if (getsockopt(httpGetFd(http
), SOL_SOCKET
, SO_NWRITE
, &buffered
, &len
))
4900 #elif defined(SIOCOUTQ) /* Others except Windows */
4901 if (ioctl(httpGetFd(http
), SIOCOUTQ
, &buffered
))
4904 #else /* Windows (not possible) */
4906 #endif /* SO_NWRITE */
4908 return (buffered
> 0);
4913 * 'usage()' - Show program usage.
4919 _cupsLangPuts(stderr
, _("Usage: ipptool [options] URI filename [ ... "
4921 _cupsLangPuts(stderr
, _("Options:"));
4922 _cupsLangPuts(stderr
, _(" --help Show help."));
4923 _cupsLangPuts(stderr
, _(" --stop-after-include-error\n"
4924 " Stop tests after a failed INCLUDE."));
4925 _cupsLangPuts(stderr
, _(" --version Show version."));
4926 _cupsLangPuts(stderr
, _(" -4 Connect using IPv4."));
4927 _cupsLangPuts(stderr
, _(" -6 Connect using IPv6."));
4928 _cupsLangPuts(stderr
, _(" -C Send requests using "
4929 "chunking (default)."));
4930 _cupsLangPuts(stdout
, _(" -E Test with HTTP Upgrade to "
4932 _cupsLangPuts(stderr
, _(" -I Ignore errors."));
4933 _cupsLangPuts(stderr
, _(" -L Send requests using "
4934 "content-length."));
4935 _cupsLangPuts(stderr
, _(" -P filename.plist Produce XML plist to a file and test report to standard output."));
4936 _cupsLangPuts(stderr
, _(" -S Test with SSL "
4938 _cupsLangPuts(stderr
, _(" -T seconds Set the receive/send "
4939 "timeout in seconds."));
4940 _cupsLangPuts(stderr
, _(" -V version Set default IPP "
4942 _cupsLangPuts(stderr
, _(" -X Produce XML plist instead "
4944 _cupsLangPuts(stderr
, _(" -c Produce CSV output."));
4945 _cupsLangPuts(stderr
, _(" -d name=value Set named variable to "
4947 _cupsLangPuts(stderr
, _(" -f filename Set default request "
4949 _cupsLangPuts(stderr
, _(" -i seconds Repeat the last file with "
4950 "the given time interval."));
4951 _cupsLangPuts(stderr
, _(" -l Produce plain text output."));
4952 _cupsLangPuts(stderr
, _(" -n count Repeat the last file the "
4953 "given number of times."));
4954 _cupsLangPuts(stderr
, _(" -q Run silently."));
4955 _cupsLangPuts(stderr
, _(" -t Produce a test report."));
4956 _cupsLangPuts(stderr
, _(" -v Be verbose."));
4963 * 'validate_attr()' - Determine whether an attribute is valid.
4966 static int /* O - 1 if valid, 0 otherwise */
4967 validate_attr(FILE *outfile
, /* I - Output file */
4968 cups_array_t
*errors
, /* I - Errors array */
4969 ipp_attribute_t
*attr
) /* I - Attribute to validate */
4971 int i
; /* Looping var */
4972 char scheme
[64], /* Scheme from URI */
4973 userpass
[256], /* Username/password from URI */
4974 hostname
[256], /* Hostname from URI */
4975 resource
[1024]; /* Resource from URI */
4976 int port
, /* Port number from URI */
4977 uri_status
, /* URI separation status */
4978 valid
= 1; /* Is the attribute valid? */
4979 const char *ptr
; /* Pointer into string */
4980 ipp_attribute_t
*colattr
; /* Collection attribute */
4981 regex_t re
; /* Regular expression */
4982 ipp_uchar_t
*date
; /* Current date value */
4993 * Validate the attribute name.
4996 for (ptr
= attr
->name
; *ptr
; ptr
++)
4997 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' && *ptr
!= '_')
5000 if (*ptr
|| ptr
== attr
->name
)
5005 "\"%s\": Bad attribute name - invalid character "
5006 "(RFC 2911 section 4.1.3).", attr
->name
);
5009 if ((ptr
- attr
->name
) > 255)
5014 "\"%s\": Bad attribute name - bad length "
5015 "(RFC 2911 section 4.1.3).", attr
->name
);
5018 switch (attr
->value_tag
)
5020 case IPP_TAG_INTEGER
:
5023 case IPP_TAG_BOOLEAN
:
5024 for (i
= 0; i
< attr
->num_values
; i
++)
5026 if (attr
->values
[i
].boolean
!= 0 &&
5027 attr
->values
[i
].boolean
!= 1)
5032 "\"%s\": Bad boolen value %d "
5033 "(RFC 2911 section 4.1.11).", attr
->name
,
5034 attr
->values
[i
].boolean
);
5040 for (i
= 0; i
< attr
->num_values
; i
++)
5042 if (attr
->values
[i
].integer
< 1)
5047 "\"%s\": Bad enum value %d - out of range "
5048 "(RFC 2911 section 4.1.4).", attr
->name
,
5049 attr
->values
[i
].integer
);
5054 case IPP_TAG_STRING
:
5055 for (i
= 0; i
< attr
->num_values
; i
++)
5057 if (attr
->values
[i
].unknown
.length
> IPP_MAX_OCTETSTRING
)
5062 "\"%s\": Bad octetString value - bad length %d "
5063 "(RFC 2911 section 4.1.10).", attr
->name
,
5064 attr
->values
[i
].unknown
.length
);
5070 for (i
= 0; i
< attr
->num_values
; i
++)
5072 date
= attr
->values
[i
].date
;
5074 if (date
[2] < 1 || date
[2] > 12)
5079 "\"%s\": Bad dateTime month %u "
5080 "(RFC 2911 section 4.1.14).", attr
->name
, date
[2]);
5083 if (date
[3] < 1 || date
[3] > 31)
5088 "\"%s\": Bad dateTime day %u "
5089 "(RFC 2911 section 4.1.14).", attr
->name
, date
[3]);
5097 "\"%s\": Bad dateTime hours %u "
5098 "(RFC 2911 section 4.1.14).", attr
->name
, date
[4]);
5106 "\"%s\": Bad dateTime minutes %u "
5107 "(RFC 2911 section 4.1.14).", attr
->name
, date
[5]);
5115 "\"%s\": Bad dateTime seconds %u "
5116 "(RFC 2911 section 4.1.14).", attr
->name
, date
[6]);
5124 "\"%s\": Bad dateTime deciseconds %u "
5125 "(RFC 2911 section 4.1.14).", attr
->name
, date
[7]);
5128 if (date
[8] != '-' && date
[8] != '+')
5133 "\"%s\": Bad dateTime UTC sign '%c' "
5134 "(RFC 2911 section 4.1.14).", attr
->name
, date
[8]);
5142 "\"%s\": Bad dateTime UTC hours %u "
5143 "(RFC 2911 section 4.1.14).", attr
->name
, date
[9]);
5151 "\"%s\": Bad dateTime UTC minutes %u "
5152 "(RFC 2911 section 4.1.14).", attr
->name
, date
[10]);
5157 case IPP_TAG_RESOLUTION
:
5158 for (i
= 0; i
< attr
->num_values
; i
++)
5160 if (attr
->values
[i
].resolution
.xres
<= 0)
5165 "\"%s\": Bad resolution value %dx%d%s - cross "
5166 "feed resolution must be positive "
5167 "(RFC 2911 section 4.1.15).", attr
->name
,
5168 attr
->values
[i
].resolution
.xres
,
5169 attr
->values
[i
].resolution
.yres
,
5170 attr
->values
[i
].resolution
.units
==
5171 IPP_RES_PER_INCH
? "dpi" :
5172 attr
->values
[i
].resolution
.units
==
5173 IPP_RES_PER_CM
? "dpcm" : "unknown");
5176 if (attr
->values
[i
].resolution
.yres
<= 0)
5181 "\"%s\": Bad resolution value %dx%d%s - feed "
5182 "resolution must be positive "
5183 "(RFC 2911 section 4.1.15).", attr
->name
,
5184 attr
->values
[i
].resolution
.xres
,
5185 attr
->values
[i
].resolution
.yres
,
5186 attr
->values
[i
].resolution
.units
==
5187 IPP_RES_PER_INCH
? "dpi" :
5188 attr
->values
[i
].resolution
.units
==
5189 IPP_RES_PER_CM
? "dpcm" : "unknown");
5192 if (attr
->values
[i
].resolution
.units
!= IPP_RES_PER_INCH
&&
5193 attr
->values
[i
].resolution
.units
!= IPP_RES_PER_CM
)
5198 "\"%s\": Bad resolution value %dx%d%s - bad "
5199 "units value (RFC 2911 section 4.1.15).",
5200 attr
->name
, attr
->values
[i
].resolution
.xres
,
5201 attr
->values
[i
].resolution
.yres
,
5202 attr
->values
[i
].resolution
.units
==
5203 IPP_RES_PER_INCH
? "dpi" :
5204 attr
->values
[i
].resolution
.units
==
5205 IPP_RES_PER_CM
? "dpcm" : "unknown");
5210 case IPP_TAG_RANGE
:
5211 for (i
= 0; i
< attr
->num_values
; i
++)
5213 if (attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
5218 "\"%s\": Bad rangeOfInteger value %d-%d - lower "
5219 "greater than upper (RFC 2911 section 4.1.13).",
5220 attr
->name
, attr
->values
[i
].range
.lower
,
5221 attr
->values
[i
].range
.upper
);
5226 case IPP_TAG_BEGIN_COLLECTION
:
5227 for (i
= 0; i
< attr
->num_values
; i
++)
5229 for (colattr
= attr
->values
[i
].collection
->attrs
;
5231 colattr
= colattr
->next
)
5233 if (!validate_attr(outfile
, NULL
, colattr
))
5240 if (colattr
&& errors
)
5242 add_stringf(errors
, "\"%s\": Bad collection value.", attr
->name
);
5246 validate_attr(outfile
, errors
, colattr
);
5247 colattr
= colattr
->next
;
5254 case IPP_TAG_TEXTLANG
:
5255 for (i
= 0; i
< attr
->num_values
; i
++)
5257 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5259 if ((*ptr
& 0xe0) == 0xc0)
5262 if ((*ptr
& 0xc0) != 0x80)
5265 else if ((*ptr
& 0xf0) == 0xe0)
5268 if ((*ptr
& 0xc0) != 0x80)
5271 if ((*ptr
& 0xc0) != 0x80)
5274 else if ((*ptr
& 0xf8) == 0xf0)
5277 if ((*ptr
& 0xc0) != 0x80)
5280 if ((*ptr
& 0xc0) != 0x80)
5283 if ((*ptr
& 0xc0) != 0x80)
5286 else if (*ptr
& 0x80)
5295 "\"%s\": Bad text value \"%s\" - bad UTF-8 "
5296 "sequence (RFC 2911 section 4.1.1).", attr
->name
,
5297 attr
->values
[i
].string
.text
);
5300 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_TEXT
- 1))
5305 "\"%s\": Bad text value \"%s\" - bad length %d "
5306 "(RFC 2911 section 4.1.1).", attr
->name
,
5307 attr
->values
[i
].string
.text
,
5308 (int)strlen(attr
->values
[i
].string
.text
));
5314 case IPP_TAG_NAMELANG
:
5315 for (i
= 0; i
< attr
->num_values
; i
++)
5317 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5319 if ((*ptr
& 0xe0) == 0xc0)
5322 if ((*ptr
& 0xc0) != 0x80)
5325 else if ((*ptr
& 0xf0) == 0xe0)
5328 if ((*ptr
& 0xc0) != 0x80)
5331 if ((*ptr
& 0xc0) != 0x80)
5334 else if ((*ptr
& 0xf8) == 0xf0)
5337 if ((*ptr
& 0xc0) != 0x80)
5340 if ((*ptr
& 0xc0) != 0x80)
5343 if ((*ptr
& 0xc0) != 0x80)
5346 else if (*ptr
& 0x80)
5355 "\"%s\": Bad name value \"%s\" - bad UTF-8 "
5356 "sequence (RFC 2911 section 4.1.2).", attr
->name
,
5357 attr
->values
[i
].string
.text
);
5360 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_NAME
- 1))
5365 "\"%s\": Bad name value \"%s\" - bad length %d "
5366 "(RFC 2911 section 4.1.2).", attr
->name
,
5367 attr
->values
[i
].string
.text
,
5368 (int)strlen(attr
->values
[i
].string
.text
));
5373 case IPP_TAG_KEYWORD
:
5374 for (i
= 0; i
< attr
->num_values
; i
++)
5376 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5377 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' &&
5381 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5386 "\"%s\": Bad keyword value \"%s\" - invalid "
5387 "character (RFC 2911 section 4.1.3).",
5388 attr
->name
, attr
->values
[i
].string
.text
);
5391 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_KEYWORD
- 1))
5396 "\"%s\": Bad keyword value \"%s\" - bad "
5397 "length %d (RFC 2911 section 4.1.3).",
5398 attr
->name
, attr
->values
[i
].string
.text
,
5399 (int)strlen(attr
->values
[i
].string
.text
));
5405 for (i
= 0; i
< attr
->num_values
; i
++)
5407 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
,
5408 attr
->values
[i
].string
.text
,
5409 scheme
, sizeof(scheme
),
5410 userpass
, sizeof(userpass
),
5411 hostname
, sizeof(hostname
),
5412 &port
, resource
, sizeof(resource
));
5414 if (uri_status
< HTTP_URI_OK
)
5419 "\"%s\": Bad URI value \"%s\" - %s "
5420 "(RFC 2911 section 4.1.5).", attr
->name
,
5421 attr
->values
[i
].string
.text
,
5422 httpURIStatusString(uri_status
));
5425 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_URI
- 1))
5430 "\"%s\": Bad URI value \"%s\" - bad length %d "
5431 "(RFC 2911 section 4.1.5).", attr
->name
,
5432 attr
->values
[i
].string
.text
,
5433 (int)strlen(attr
->values
[i
].string
.text
));
5438 case IPP_TAG_URISCHEME
:
5439 for (i
= 0; i
< attr
->num_values
; i
++)
5441 ptr
= attr
->values
[i
].string
.text
;
5442 if (islower(*ptr
& 255))
5444 for (ptr
++; *ptr
; ptr
++)
5445 if (!islower(*ptr
& 255) && !isdigit(*ptr
& 255) &&
5446 *ptr
!= '+' && *ptr
!= '-' && *ptr
!= '.')
5450 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5455 "\"%s\": Bad uriScheme value \"%s\" - bad "
5456 "characters (RFC 2911 section 4.1.6).",
5457 attr
->name
, attr
->values
[i
].string
.text
);
5460 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_URISCHEME
- 1))
5465 "\"%s\": Bad uriScheme value \"%s\" - bad "
5466 "length %d (RFC 2911 section 4.1.6).",
5467 attr
->name
, attr
->values
[i
].string
.text
,
5468 (int)strlen(attr
->values
[i
].string
.text
));
5473 case IPP_TAG_CHARSET
:
5474 for (i
= 0; i
< attr
->num_values
; i
++)
5476 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5477 if (!isprint(*ptr
& 255) || isupper(*ptr
& 255) ||
5478 isspace(*ptr
& 255))
5481 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5486 "\"%s\": Bad charset value \"%s\" - bad "
5487 "characters (RFC 2911 section 4.1.7).",
5488 attr
->name
, attr
->values
[i
].string
.text
);
5491 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_CHARSET
- 1))
5496 "\"%s\": Bad charset value \"%s\" - bad "
5497 "length %d (RFC 2911 section 4.1.7).",
5498 attr
->name
, attr
->values
[i
].string
.text
,
5499 (int)strlen(attr
->values
[i
].string
.text
));
5504 case IPP_TAG_LANGUAGE
:
5506 * The following regular expression is derived from the ABNF for
5507 * language tags in RFC 4646. All I can say is that this is the
5508 * easiest way to check the values...
5511 if ((i
= regcomp(&re
,
5513 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
5515 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
5516 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
5517 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
5518 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
5519 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
5521 "x(-[a-z0-9]{1,8})+" /* privateuse */
5523 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
5525 REG_NOSUB
| REG_EXTENDED
)) != 0)
5527 char temp
[256]; /* Temporary error string */
5529 regerror(i
, &re
, temp
, sizeof(temp
));
5530 print_fatal_error(outfile
, "Unable to compile naturalLanguage regular "
5531 "expression: %s.", temp
);
5535 for (i
= 0; i
< attr
->num_values
; i
++)
5537 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5542 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5543 "characters (RFC 2911 section 4.1.8).",
5544 attr
->name
, attr
->values
[i
].string
.text
);
5547 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_LANGUAGE
- 1))
5552 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5553 "length %d (RFC 2911 section 4.1.8).",
5554 attr
->name
, attr
->values
[i
].string
.text
,
5555 (int)strlen(attr
->values
[i
].string
.text
));
5562 case IPP_TAG_MIMETYPE
:
5564 * The following regular expression is derived from the ABNF for
5565 * language tags in RFC 2045 and 4288. All I can say is that this is
5566 * the easiest way to check the values...
5569 if ((i
= regcomp(&re
,
5571 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
5573 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
5574 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
5575 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
5578 REG_NOSUB
| REG_EXTENDED
)) != 0)
5580 char temp
[256]; /* Temporary error string */
5582 regerror(i
, &re
, temp
, sizeof(temp
));
5583 print_fatal_error(outfile
, "Unable to compile mimeMediaType regular "
5584 "expression: %s.", temp
);
5588 for (i
= 0; i
< attr
->num_values
; i
++)
5590 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5595 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5596 "characters (RFC 2911 section 4.1.9).",
5597 attr
->name
, attr
->values
[i
].string
.text
);
5600 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_MIMETYPE
- 1))
5605 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5606 "length %d (RFC 2911 section 4.1.9).",
5607 attr
->name
, attr
->values
[i
].string
.text
,
5608 (int)strlen(attr
->values
[i
].string
.text
));
5624 * 'with_value()' - Test a WITH-VALUE predicate.
5627 static int /* O - 1 on match, 0 on non-match */
5628 with_value(FILE *outfile
, /* I - Output file */
5629 cups_array_t
*errors
, /* I - Errors array */
5630 char *value
, /* I - Value string */
5631 int flags
, /* I - Flags for match */
5632 ipp_attribute_t
*attr
, /* I - Attribute to compare */
5633 char *matchbuf
, /* I - Buffer to hold matching value */
5634 size_t matchlen
) /* I - Length of match buffer */
5636 int i
, /* Looping var */
5638 char temp
[1024], /* Temporary value string */
5639 *valptr
; /* Pointer into value */
5643 match
= (flags
& _CUPS_WITH_ALL
) ? 1 : 0;
5646 * NULL matches everything.
5649 if (!value
|| !*value
)
5653 * Compare the value string to the attribute value.
5656 switch (attr
->value_tag
)
5658 case IPP_TAG_INTEGER
:
5660 for (i
= 0; i
< attr
->num_values
; i
++)
5662 char op
, /* Comparison operator */
5663 *nextptr
; /* Next pointer */
5664 int intvalue
, /* Integer value */
5665 valmatch
= 0; /* Does the current value match? */
5669 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5670 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5671 *valptr
== '=' || *valptr
== '>')
5674 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5676 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5684 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
5685 if (nextptr
== valptr
)
5689 if ((op
== '=' && attr
->values
[i
].integer
== intvalue
) ||
5690 (op
== '<' && attr
->values
[i
].integer
< intvalue
) ||
5691 (op
== '>' && attr
->values
[i
].integer
> intvalue
))
5694 snprintf(matchbuf
, matchlen
, "%d", attr
->values
[i
].integer
);
5701 if (flags
& _CUPS_WITH_ALL
)
5716 if (!match
&& errors
)
5718 for (i
= 0; i
< attr
->num_values
; i
++)
5719 add_stringf(errors
, "GOT: %s=%d", attr
->name
,
5720 attr
->values
[i
].integer
);
5724 case IPP_TAG_RANGE
:
5725 for (i
= 0; i
< attr
->num_values
; i
++)
5727 char op
, /* Comparison operator */
5728 *nextptr
; /* Next pointer */
5729 int intvalue
, /* Integer value */
5730 valmatch
= 0; /* Does the current value match? */
5734 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5735 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5736 *valptr
== '=' || *valptr
== '>')
5739 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5741 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5749 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
5750 if (nextptr
== valptr
)
5754 if ((op
== '=' && (attr
->values
[i
].range
.lower
== intvalue
||
5755 attr
->values
[i
].range
.upper
== intvalue
)) ||
5756 (op
== '<' && attr
->values
[i
].range
.upper
< intvalue
) ||
5757 (op
== '>' && attr
->values
[i
].range
.upper
> intvalue
))
5760 snprintf(matchbuf
, matchlen
, "%d-%d",
5761 attr
->values
[0].range
.lower
,
5762 attr
->values
[0].range
.upper
);
5769 if (flags
& _CUPS_WITH_ALL
)
5784 if (!match
&& errors
)
5786 for (i
= 0; i
< attr
->num_values
; i
++)
5787 add_stringf(errors
, "GOT: %s=%d-%d", attr
->name
,
5788 attr
->values
[i
].range
.lower
,
5789 attr
->values
[i
].range
.upper
);
5793 case IPP_TAG_BOOLEAN
:
5794 for (i
= 0; i
< attr
->num_values
; i
++)
5796 if (!strcmp(value
, "true") == attr
->values
[i
].boolean
)
5799 strlcpy(matchbuf
, value
, matchlen
);
5801 if (!(flags
& _CUPS_WITH_ALL
))
5807 else if (flags
& _CUPS_WITH_ALL
)
5814 if (!match
&& errors
)
5816 for (i
= 0; i
< attr
->num_values
; i
++)
5817 add_stringf(errors
, "GOT: %s=%s", attr
->name
,
5818 attr
->values
[i
].boolean
? "true" : "false");
5822 case IPP_TAG_RESOLUTION
:
5823 for (i
= 0; i
< attr
->num_values
; i
++)
5825 if (attr
->values
[i
].resolution
.xres
==
5826 attr
->values
[i
].resolution
.yres
)
5827 snprintf(temp
, sizeof(temp
), "%d%s",
5828 attr
->values
[i
].resolution
.xres
,
5829 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5832 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5833 attr
->values
[i
].resolution
.xres
,
5834 attr
->values
[i
].resolution
.yres
,
5835 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5838 if (!strcmp(value
, temp
))
5841 strlcpy(matchbuf
, value
, matchlen
);
5843 if (!(flags
& _CUPS_WITH_ALL
))
5849 else if (flags
& _CUPS_WITH_ALL
)
5856 if (!match
&& errors
)
5858 for (i
= 0; i
< attr
->num_values
; i
++)
5860 if (attr
->values
[i
].resolution
.xres
==
5861 attr
->values
[i
].resolution
.yres
)
5862 snprintf(temp
, sizeof(temp
), "%d%s",
5863 attr
->values
[i
].resolution
.xres
,
5864 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5867 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5868 attr
->values
[i
].resolution
.xres
,
5869 attr
->values
[i
].resolution
.yres
,
5870 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5873 if (strcmp(value
, temp
))
5874 add_stringf(errors
, "GOT: %s=%s", attr
->name
, temp
);
5879 case IPP_TAG_NOVALUE
:
5880 case IPP_TAG_UNKNOWN
:
5883 case IPP_TAG_CHARSET
:
5884 case IPP_TAG_KEYWORD
:
5885 case IPP_TAG_LANGUAGE
:
5886 case IPP_TAG_MIMETYPE
:
5888 case IPP_TAG_NAMELANG
:
5890 case IPP_TAG_TEXTLANG
:
5892 case IPP_TAG_URISCHEME
:
5893 if (flags
& _CUPS_WITH_REGEX
)
5896 * Value is an extended, case-sensitive POSIX regular expression...
5899 regex_t re
; /* Regular expression */
5901 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
5903 regerror(i
, &re
, temp
, sizeof(temp
));
5905 print_fatal_error(outfile
, "Unable to compile WITH-VALUE regular expression "
5906 "\"%s\" - %s", value
, temp
);
5911 * See if ALL of the values match the given regular expression.
5914 for (i
= 0; i
< attr
->num_values
; i
++)
5916 if (!regexec(&re
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5921 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5924 if (!(flags
& _CUPS_WITH_ALL
))
5930 else if (flags
& _CUPS_WITH_ALL
)
5939 else if (ippGetValueTag(attr
) == IPP_TAG_URI
)
5941 if (!strncmp(value
, "ipp://", 6) || !strncmp(value
, "http://", 7) || !strncmp(value
, "ipps://", 7) || !strncmp(value
, "https://", 8))
5943 char scheme
[256], /* URI scheme */
5944 userpass
[256], /* username:password, if any */
5945 hostname
[256], /* hostname */
5946 *hostptr
, /* Pointer into hostname */
5947 resource
[1024]; /* Resource path */
5948 int port
; /* Port number */
5950 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
== '.')
5953 * Strip trailing "." in hostname of URI...
5957 httpAssembleURI(HTTP_URI_CODING_ALL
, temp
, sizeof(temp
), scheme
, userpass
, hostname
, port
, resource
);
5963 * Value is a literal URI string, see if the value(s) match...
5966 for (i
= 0; i
< attr
->num_values
; i
++)
5968 if (!strcmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
))))
5972 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5975 if (!(flags
& _CUPS_WITH_ALL
))
5981 else if (flags
& _CUPS_WITH_ALL
)
5991 * Value is a literal string, see if the value(s) match...
5994 for (i
= 0; i
< attr
->num_values
; i
++)
5996 if (!strcmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
))))
6000 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
6003 if (!(flags
& _CUPS_WITH_ALL
))
6009 else if (flags
& _CUPS_WITH_ALL
)
6017 if (!match
&& errors
)
6019 for (i
= 0; i
< attr
->num_values
; i
++)
6020 add_stringf(errors
, "GOT: %s=\"%s\"", attr
->name
,
6021 attr
->values
[i
].string
.text
);
6034 * 'with_value_from()' - Test a WITH-VALUE-FROM predicate.
6037 static int /* O - 1 on match, 0 on non-match */
6039 cups_array_t
*errors
, /* I - Errors array */
6040 ipp_attribute_t
*fromattr
, /* I - "From" attribute */
6041 ipp_attribute_t
*attr
, /* I - Attribute to compare */
6042 char *matchbuf
, /* I - Buffer to hold matching value */
6043 size_t matchlen
) /* I - Length of match buffer */
6045 int i
, j
, /* Looping vars */
6046 count
= ippGetCount(attr
), /* Number of attribute values */
6047 match
= 1; /* Match? */
6053 * Compare the from value(s) to the attribute value(s)...
6056 switch (ippGetValueTag(attr
))
6058 case IPP_TAG_INTEGER
:
6059 if (ippGetValueTag(fromattr
) != IPP_TAG_INTEGER
&& ippGetValueTag(fromattr
) != IPP_TAG_RANGE
)
6060 goto wrong_value_tag
;
6062 for (i
= 0; i
< count
; i
++)
6064 int value
= ippGetInteger(attr
, i
);
6065 /* Current integer value */
6067 if (ippContainsInteger(fromattr
, value
))
6070 snprintf(matchbuf
, matchlen
, "%d", value
);
6074 add_stringf(errors
, "GOT: %s=%d", ippGetName(attr
), value
);
6081 if (ippGetValueTag(fromattr
) != IPP_TAG_ENUM
)
6082 goto wrong_value_tag
;
6084 for (i
= 0; i
< count
; i
++)
6086 int value
= ippGetInteger(attr
, i
);
6087 /* Current integer value */
6089 if (ippContainsInteger(fromattr
, value
))
6092 snprintf(matchbuf
, matchlen
, "%d", value
);
6096 add_stringf(errors
, "GOT: %s=%d", ippGetName(attr
), value
);
6102 case IPP_TAG_RESOLUTION
:
6103 if (ippGetValueTag(fromattr
) != IPP_TAG_RESOLUTION
)
6104 goto wrong_value_tag
;
6106 for (i
= 0; i
< count
; i
++)
6110 int fromcount
= ippGetCount(fromattr
);
6111 int fromxres
, fromyres
;
6112 ipp_res_t fromunits
;
6114 xres
= ippGetResolution(attr
, i
, &yres
, &units
);
6116 for (j
= 0; j
< fromcount
; j
++)
6118 fromxres
= ippGetResolution(fromattr
, j
, &fromyres
, &fromunits
);
6119 if (fromxres
== xres
&& fromyres
== yres
&& fromunits
== units
)
6128 snprintf(matchbuf
, matchlen
, "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6130 snprintf(matchbuf
, matchlen
, "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6136 add_stringf(errors
, "GOT: %s=%d%s", ippGetName(attr
), xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6138 add_stringf(errors
, "GOT: %s=%dx%d%s", ippGetName(attr
), xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6145 case IPP_TAG_NOVALUE
:
6146 case IPP_TAG_UNKNOWN
:
6149 case IPP_TAG_CHARSET
:
6150 case IPP_TAG_KEYWORD
:
6151 case IPP_TAG_LANGUAGE
:
6152 case IPP_TAG_MIMETYPE
:
6154 case IPP_TAG_NAMELANG
:
6156 case IPP_TAG_TEXTLANG
:
6158 case IPP_TAG_URISCHEME
:
6159 for (i
= 0; i
< count
; i
++)
6161 const char *value
= ippGetString(attr
, i
, NULL
);
6162 /* Current string value */
6164 if (ippContainsString(fromattr
, value
))
6167 strlcpy(matchbuf
, value
, matchlen
);
6171 add_stringf(errors
, "GOT: %s='%s'", ippGetName(attr
), value
);
6184 /* value tag mismatch between fromattr and attr */
6187 add_stringf(errors
, "GOT: %s OF-TYPE %s", ippGetName(attr
), ippTagString(ippGetValueTag(attr
)));
6194 * End of "$Id: ipptool.c 12952 2015-10-28 17:22:39Z msweet $".