2 * ipptool command for CUPS.
4 * Copyright 2007-2017 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products.
7 * These coded instructions, statements, and computer programs are the
8 * property of Apple Inc. and are protected by Federal copyright
9 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
10 * which should have been included with this file. If this file is
11 * missing or damaged, see the license at "http://www.cups.org/".
13 * This file is subject to the Apple OS-Developed Software exception.
17 * Include necessary headers...
20 #include <cups/cups-private.h>
21 #include <cups/file-private.h>
35 #endif /* !O_BINARY */
42 typedef enum _cups_transfer_e
/**** How to send request data ****/
44 _CUPS_TRANSFER_AUTO
, /* Chunk for files, length for static */
45 _CUPS_TRANSFER_CHUNKED
, /* Chunk always */
46 _CUPS_TRANSFER_LENGTH
/* Length always */
49 typedef enum _cups_output_e
/**** Output mode ****/
51 _CUPS_OUTPUT_QUIET
, /* No output */
52 _CUPS_OUTPUT_TEST
, /* Traditional CUPS test output */
53 _CUPS_OUTPUT_PLIST
, /* XML plist test output */
54 _CUPS_OUTPUT_IPPSERVER
, /* ippserver attribute file output */
55 _CUPS_OUTPUT_LIST
, /* Tabular list output */
56 _CUPS_OUTPUT_CSV
/* Comma-separated values output */
59 typedef enum _cups_with_e
/**** WITH flags ****/
61 _CUPS_WITH_LITERAL
= 0, /* Match string is a literal value */
62 _CUPS_WITH_ALL
= 1, /* Must match all values */
63 _CUPS_WITH_REGEX
= 2, /* Match string is a regular expression */
64 _CUPS_WITH_HOSTNAME
= 4, /* Match string is a URI hostname */
65 _CUPS_WITH_RESOURCE
= 8, /* Match string is a URI resource */
66 _CUPS_WITH_SCHEME
= 16 /* Match string is a URI scheme */
69 typedef struct _cups_expect_s
/**** Expected attribute info ****/
71 int optional
, /* Optional attribute? */
72 not_expect
, /* Don't expect attribute? */
73 expect_all
; /* Expect all attributes to match/not match */
74 char *name
, /* Attribute name */
75 *of_type
, /* Type name */
76 *same_count_as
, /* Parallel attribute name */
77 *if_defined
, /* Only required if variable defined */
78 *if_not_defined
, /* Only required if variable is not defined */
79 *with_value
, /* Attribute must include this value */
80 *with_value_from
, /* Attribute must have one of the values in this attribute */
81 *define_match
, /* Variable to define on match */
82 *define_no_match
, /* Variable to define on no-match */
83 *define_value
; /* Variable to define with value */
84 int repeat_limit
, /* Maximum number of times to repeat */
85 repeat_match
, /* Repeat test on match */
86 repeat_no_match
, /* Repeat test on no match */
87 with_flags
, /* WITH flags */
88 count
; /* Expected count if > 0 */
89 ipp_tag_t in_group
; /* IN-GROUP value */
92 typedef struct _cups_status_s
/**** Status info ****/
94 ipp_status_t status
; /* Expected status code */
95 char *if_defined
, /* Only if variable is defined */
96 *if_not_defined
, /* Only if variable is not defined */
97 *define_match
, /* Variable to define on match */
98 *define_no_match
, /* Variable to define on no-match */
99 *define_value
; /* Variable to define with value */
100 int repeat_limit
, /* Maximum number of times to repeat */
101 repeat_match
, /* Repeat the test when it does not match */
102 repeat_no_match
; /* Repeat the test when it matches */
105 typedef struct _cups_var_s
/**** Variable ****/
107 char *name
, /* Name of variable */
108 *value
; /* Value of variable */
111 typedef struct _cups_vars_s
/**** Set of variables ****/
113 char *uri
, /* URI for printer */
114 *filename
, /* Filename */
115 scheme
[64], /* Scheme from URI */
116 userpass
[256], /* Username/password from URI */
117 hostname
[256], /* Hostname from URI */
118 resource
[1024]; /* Resource path from URI */
119 int port
; /* Port number from URI */
120 http_encryption_t encryption
; /* Encryption for connection? */
121 double timeout
; /* Timeout for connection */
122 int family
; /* Address family */
123 cups_array_t
*vars
; /* Array of variables */
131 static _cups_transfer_t Transfer
= _CUPS_TRANSFER_AUTO
;
132 /* How to transfer requests */
133 static _cups_output_t Output
= _CUPS_OUTPUT_LIST
;
135 static int Cancel
= 0, /* Cancel test? */
136 IgnoreErrors
= 0, /* Ignore errors? */
137 StopAfterIncludeError
= 0,
138 /* Stop after include errors? */
139 ValidateHeaders
= 0, /* Validate HTTP headers in response? */
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_uris(const char *a
, const char *b
);
158 static int compare_vars(_cups_var_t
*a
, _cups_var_t
*b
);
159 static int do_tests(cups_file_t
*outfile
, _cups_vars_t
*vars
, const char *testfile
);
160 static void expand_variables(_cups_vars_t
*vars
, char *dst
, const char *src
, size_t dstsize
) __attribute__((nonnull(1,2,3)));
161 static int expect_matches(_cups_expect_t
*expect
, ipp_tag_t value_tag
);
162 static ipp_t
*get_collection(cups_file_t
*outfile
, _cups_vars_t
*vars
, cups_file_t
*fp
, int *linenum
);
163 static char *get_filename(const char *testfile
, char *dst
, const char *src
, size_t dstsize
);
164 static const char *get_string(ipp_attribute_t
*attr
, int element
, int flags
, char *buffer
, size_t bufsize
);
165 static char *get_token(cups_file_t
*fp
, char *buf
, int buflen
, int *linenum
);
166 static char *get_variable(_cups_vars_t
*vars
, const char *name
);
167 static char *iso_date(const ipp_uchar_t
*date
);
168 static const char *password_cb(const char *prompt
);
169 static void pause_message(const char *message
);
170 static void print_attr(cups_file_t
*outfile
, int format
, ipp_attribute_t
*attr
, ipp_tag_t
*group
);
171 static void print_csv(cups_file_t
*outfile
, ipp_attribute_t
*attr
, int num_displayed
, char **displayed
, size_t *widths
);
172 static void print_fatal_error(cups_file_t
*outfile
, const char *s
, ...) __attribute__ ((__format__ (__printf__
, 2, 3)));
173 static void print_ippserver_attr(cups_file_t
*outfile
, ipp_attribute_t
*attr
, int indent
);
174 static void print_ippserver_string(cups_file_t
*outfile
, const char *s
, size_t len
);
175 static void print_line(cups_file_t
*outfile
, ipp_attribute_t
*attr
, int num_displayed
, char **displayed
, size_t *widths
);
176 static void print_xml_header(cups_file_t
*outfile
);
177 static void print_xml_string(cups_file_t
*outfile
, const char *element
, const char *s
);
178 static void print_xml_trailer(cups_file_t
*outfile
, int success
, const char *message
);
179 static void set_variable(cups_file_t
*outfile
, _cups_vars_t
*vars
, const char *name
, const char *value
);
181 static void sigterm_handler(int sig
);
183 static int timeout_cb(http_t
*http
, void *user_data
);
184 static void usage(void) __attribute__((noreturn
));
185 static int validate_attr(cups_file_t
*outfile
, cups_array_t
*errors
, ipp_attribute_t
*attr
);
186 static const char *with_flags_string(int flags
);
187 static int with_value(cups_file_t
*outfile
, cups_array_t
*errors
, char *value
, int flags
, ipp_attribute_t
*attr
, char *matchbuf
, size_t matchlen
);
188 static int with_value_from(cups_array_t
*errors
, ipp_attribute_t
*fromattr
, ipp_attribute_t
*attr
, char *matchbuf
, size_t matchlen
);
192 * 'main()' - Parse options and do tests.
195 int /* O - Exit status */
196 main(int argc
, /* I - Number of command-line args */
197 char *argv
[]) /* I - Command-line arguments */
199 int i
; /* Looping var */
200 int status
; /* Status of tests... */
201 cups_file_t
*outfile
= cupsFileStdout();
203 char *opt
, /* Current option */
204 name
[1024], /* Name/value buffer */
205 *value
, /* Pointer to value */
206 filename
[1024], /* Real filename */
207 testname
[1024], /* Real test filename */
208 uri
[1024]; /* Copy of printer URI */
209 const char *ext
, /* Extension on filename */
210 *testfile
; /* Test file to use */
211 int interval
, /* Test interval in microseconds */
212 repeat
; /* Repeat count */
213 _cups_vars_t vars
; /* Variables */
214 http_uri_status_t uri_status
; /* URI separation status */
215 _cups_globals_t
*cg
= _cupsGlobals();
221 * Catch SIGINT and SIGTERM...
224 signal(SIGINT
, sigterm_handler
);
225 signal(SIGTERM
, sigterm_handler
);
229 * Initialize the locale and variables...
232 _cupsSetLocale(argv
);
234 memset(&vars
, 0, sizeof(vars
));
235 vars
.family
= AF_UNSPEC
;
236 vars
.vars
= cupsArrayNew((cups_array_func_t
)compare_vars
, NULL
);
241 * ipptool URI testfile
249 for (i
= 1; i
< argc
; i
++)
251 if (!strcmp(argv
[i
], "--help"))
255 else if (!strcmp(argv
[i
], "--ippserver"))
261 _cupsLangPuts(stderr
, _("ipptool: Missing filename for \"--ippserver\"."));
265 if (outfile
!= cupsFileStdout())
268 if ((outfile
= cupsFileOpen(argv
[i
], "w")) == NULL
)
270 _cupsLangPrintf(stderr
, _("%s: Unable to open \"%s\": %s"), "ipptool", argv
[i
], strerror(errno
));
274 Output
= _CUPS_OUTPUT_IPPSERVER
;
276 else if (!strcmp(argv
[i
], "--stop-after-include-error"))
278 StopAfterIncludeError
= 1;
280 else if (!strcmp(argv
[i
], "--version"))
285 else if (argv
[i
][0] == '-')
287 for (opt
= argv
[i
] + 1; *opt
; opt
++)
291 case '4' : /* Connect using IPv4 only */
292 vars
.family
= AF_INET
;
296 case '6' : /* Connect using IPv6 only */
297 vars
.family
= AF_INET6
;
299 #endif /* AF_INET6 */
301 case 'C' : /* Enable HTTP chunking */
302 Transfer
= _CUPS_TRANSFER_CHUNKED
;
305 case 'E' : /* Encrypt with TLS */
307 vars
.encryption
= HTTP_ENCRYPT_REQUIRED
;
309 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
311 #endif /* HAVE_SSL */
314 case 'I' : /* Ignore errors */
318 case 'L' : /* Disable HTTP chunking */
319 Transfer
= _CUPS_TRANSFER_LENGTH
;
322 case 'P' : /* Output to plist file */
327 _cupsLangPrintf(stderr
, _("%s: Missing filename for \"-P\"."), "ipptool");
331 if (outfile
!= cupsFileStdout())
334 if ((outfile
= cupsFileOpen(argv
[i
], "w")) == NULL
)
336 _cupsLangPrintf(stderr
, _("%s: Unable to open \"%s\": %s"), "ipptool", argv
[i
], strerror(errno
));
340 Output
= _CUPS_OUTPUT_PLIST
;
342 if (interval
|| repeat
)
344 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
349 case 'S' : /* Encrypt with SSL */
351 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
353 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
355 #endif /* HAVE_SSL */
358 case 'T' : /* Set timeout */
363 _cupsLangPrintf(stderr
,
364 _("%s: Missing timeout for \"-T\"."),
369 vars
.timeout
= _cupsStrScand(argv
[i
], NULL
, localeconv());
372 case 'V' : /* Set IPP version */
377 _cupsLangPrintf(stderr
,
378 _("%s: Missing version for \"-V\"."),
383 if (!strcmp(argv
[i
], "1.0"))
385 else if (!strcmp(argv
[i
], "1.1"))
387 else if (!strcmp(argv
[i
], "2.0"))
389 else if (!strcmp(argv
[i
], "2.1"))
391 else if (!strcmp(argv
[i
], "2.2"))
395 _cupsLangPrintf(stderr
,
396 _("%s: Bad version %s for \"-V\"."),
402 case 'X' : /* Produce XML output */
403 Output
= _CUPS_OUTPUT_PLIST
;
405 if (interval
|| repeat
)
407 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
412 case 'c' : /* CSV output */
413 Output
= _CUPS_OUTPUT_CSV
;
416 case 'd' : /* Define a variable */
421 _cupsLangPuts(stderr
,
422 _("ipptool: Missing name=value for \"-d\"."));
426 strlcpy(name
, argv
[i
], sizeof(name
));
427 if ((value
= strchr(name
, '=')) != NULL
)
430 value
= name
+ strlen(name
);
432 set_variable(outfile
, &vars
, name
, value
);
435 case 'f' : /* Set the default test filename */
440 _cupsLangPuts(stderr
,
441 _("ipptool: Missing filename for \"-f\"."));
448 vars
.filename
= NULL
;
451 if (access(argv
[i
], 0))
457 snprintf(filename
, sizeof(filename
), "%s.gz", argv
[i
]);
458 if (access(filename
, 0) && filename
[0] != '/'
460 && (!isalpha(filename
[0] & 255) || filename
[1] != ':')
464 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s",
465 cg
->cups_datadir
, argv
[i
]);
466 if (access(filename
, 0))
468 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s.gz",
469 cg
->cups_datadir
, argv
[i
]);
470 if (access(filename
, 0))
471 vars
.filename
= strdup(argv
[i
]);
473 vars
.filename
= strdup(filename
);
476 vars
.filename
= strdup(filename
);
479 vars
.filename
= strdup(filename
);
482 vars
.filename
= strdup(argv
[i
]);
484 if ((ext
= strrchr(vars
.filename
, '.')) != NULL
)
487 * Guess the MIME media type based on the extension...
490 if (!_cups_strcasecmp(ext
, ".gif"))
491 set_variable(outfile
, &vars
, "filetype", "image/gif");
492 else if (!_cups_strcasecmp(ext
, ".htm") ||
493 !_cups_strcasecmp(ext
, ".htm.gz") ||
494 !_cups_strcasecmp(ext
, ".html") ||
495 !_cups_strcasecmp(ext
, ".html.gz"))
496 set_variable(outfile
, &vars
, "filetype", "text/html");
497 else if (!_cups_strcasecmp(ext
, ".jpg") ||
498 !_cups_strcasecmp(ext
, ".jpeg"))
499 set_variable(outfile
, &vars
, "filetype", "image/jpeg");
500 else if (!_cups_strcasecmp(ext
, ".pcl") ||
501 !_cups_strcasecmp(ext
, ".pcl.gz"))
502 set_variable(outfile
, &vars
, "filetype", "application/vnd.hp-PCL");
503 else if (!_cups_strcasecmp(ext
, ".pdf"))
504 set_variable(outfile
, &vars
, "filetype", "application/pdf");
505 else if (!_cups_strcasecmp(ext
, ".png"))
506 set_variable(outfile
, &vars
, "filetype", "image/png");
507 else if (!_cups_strcasecmp(ext
, ".ps") ||
508 !_cups_strcasecmp(ext
, ".ps.gz"))
509 set_variable(outfile
, &vars
, "filetype", "application/postscript");
510 else if (!_cups_strcasecmp(ext
, ".pwg") ||
511 !_cups_strcasecmp(ext
, ".pwg.gz") ||
512 !_cups_strcasecmp(ext
, ".ras") ||
513 !_cups_strcasecmp(ext
, ".ras.gz"))
514 set_variable(outfile
, &vars
, "filetype", "image/pwg-raster");
515 else if (!_cups_strcasecmp(ext
, ".tif") ||
516 !_cups_strcasecmp(ext
, ".tiff"))
517 set_variable(outfile
, &vars
, "filetype", "image/tiff");
518 else if (!_cups_strcasecmp(ext
, ".txt") ||
519 !_cups_strcasecmp(ext
, ".txt.gz"))
520 set_variable(outfile
, &vars
, "filetype", "text/plain");
521 else if (!_cups_strcasecmp(ext
, ".urf") ||
522 !_cups_strcasecmp(ext
, ".urf.gz"))
523 set_variable(outfile
, &vars
, "filetype", "image/urf");
524 else if (!_cups_strcasecmp(ext
, ".xps"))
525 set_variable(outfile
, &vars
, "filetype", "application/openxps");
527 set_variable(outfile
, &vars
, "filetype", "application/octet-stream");
532 * Use the "auto-type" MIME media type...
535 set_variable(outfile
, &vars
, "filetype", "application/octet-stream");
539 case 'h' : /* Validate response headers */
543 case 'i' : /* Test every N seconds */
548 _cupsLangPuts(stderr
,
549 _("ipptool: Missing seconds for \"-i\"."));
554 interval
= (int)(_cupsStrScand(argv
[i
], NULL
, localeconv()) *
558 _cupsLangPuts(stderr
,
559 _("ipptool: Invalid seconds for \"-i\"."));
564 if ((Output
== _CUPS_OUTPUT_PLIST
|| Output
== _CUPS_OUTPUT_IPPSERVER
) && interval
)
566 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"--ippserver\", \"-P\", and \"-X\"."));
571 case 'l' : /* List as a table */
572 Output
= _CUPS_OUTPUT_LIST
;
575 case 'n' : /* Repeat count */
580 _cupsLangPuts(stderr
,
581 _("ipptool: Missing count for \"-n\"."));
585 repeat
= atoi(argv
[i
]);
587 if ((Output
== _CUPS_OUTPUT_PLIST
|| Output
== _CUPS_OUTPUT_IPPSERVER
) && repeat
)
589 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"--ippserver\", \"-P\", and \"-X\"."));
594 case 'q' : /* Be quiet */
595 Output
= _CUPS_OUTPUT_QUIET
;
598 case 't' : /* CUPS test output */
599 Output
= _CUPS_OUTPUT_TEST
;
602 case 'v' : /* Be verbose */
607 _cupsLangPrintf(stderr
, _("ipptool: Unknown option \"-%c\"."),
613 else if (!strncmp(argv
[i
], "ipp://", 6) || !strncmp(argv
[i
], "http://", 7)
615 || !strncmp(argv
[i
], "ipps://", 7)
616 || !strncmp(argv
[i
], "https://", 8)
617 #endif /* HAVE_SSL */
626 _cupsLangPuts(stderr
, _("ipptool: May only specify a single URI."));
631 if (!strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8))
632 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
633 #endif /* HAVE_SSL */
636 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, vars
.uri
,
637 vars
.scheme
, sizeof(vars
.scheme
),
638 vars
.userpass
, sizeof(vars
.userpass
),
639 vars
.hostname
, sizeof(vars
.hostname
),
641 vars
.resource
, sizeof(vars
.resource
));
643 if (uri_status
!= HTTP_URI_OK
)
645 _cupsLangPrintf(stderr
, _("ipptool: Bad URI - %s."), httpURIStatusString(uri_status
));
649 if (vars
.userpass
[0])
651 if ((Password
= strchr(vars
.userpass
, ':')) != NULL
)
654 Username
= vars
.userpass
;
655 cupsSetPasswordCB(password_cb
);
656 set_variable(outfile
, &vars
, "uriuser", vars
.userpass
);
659 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), vars
.scheme
, NULL
,
660 vars
.hostname
, vars
.port
, vars
.resource
);
671 _cupsLangPuts(stderr
, _("ipptool: URI required before test file."));
672 _cupsLangPuts(stderr
, argv
[i
]);
676 if (access(argv
[i
], 0) && argv
[i
][0] != '/'
678 && (!isalpha(argv
[i
][0] & 255) || argv
[i
][1] != ':')
682 snprintf(testname
, sizeof(testname
), "%s/ipptool/%s", cg
->cups_datadir
,
684 if (access(testname
, 0))
692 if (!do_tests(outfile
, &vars
, testfile
))
697 if (!vars
.uri
|| !testfile
)
701 * Loop if the interval is set...
704 if (Output
== _CUPS_OUTPUT_PLIST
)
705 print_xml_trailer(outfile
, !status
, NULL
);
706 else if (interval
> 0 && repeat
> 0)
710 usleep((useconds_t
)interval
);
711 do_tests(outfile
, &vars
, testfile
);
715 else if (interval
> 0)
719 usleep((useconds_t
)interval
);
720 do_tests(outfile
, &vars
, testfile
);
724 if ((Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
)) && TestCount
> 1)
727 * Show a summary report if there were multiple tests...
730 cupsFilePrintf(cupsFileStdout(), "\nSummary: %d tests, %d passed, %d failed, %d skipped\nScore: %d%%\n", TestCount
, PassCount
, FailCount
, SkipCount
, 100 * (PassCount
+ SkipCount
) / TestCount
);
733 cupsFileClose(outfile
);
744 * 'add_stringf()' - Add a formatted string to an array.
748 add_stringf(cups_array_t
*a
, /* I - Array */
749 const char *s
, /* I - Printf-style format string */
750 ...) /* I - Additional args as needed */
752 char buffer
[10240]; /* Format buffer */
753 va_list ap
; /* Argument pointer */
757 * Don't bother is the array is NULL...
764 * Format the message...
768 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
772 * Add it to the array...
775 cupsArrayAdd(a
, buffer
);
780 * 'compare_uris()' - Compare two URIs...
783 static int /* O - Result of comparison */
784 compare_uris(const char *a
, /* I - First URI */
785 const char *b
) /* I - Second URI */
787 char ascheme
[32], /* Components of first URI */
792 char bscheme
[32], /* Components of second URI */
797 char *ptr
; /* Pointer into string */
798 int result
; /* Result of comparison */
802 * Separate the URIs into their components...
805 if (httpSeparateURI(HTTP_URI_CODING_ALL
, a
, ascheme
, sizeof(ascheme
), auserpass
, sizeof(auserpass
), ahost
, sizeof(ahost
), &aport
, aresource
, sizeof(aresource
)) < HTTP_URI_STATUS_OK
)
808 if (httpSeparateURI(HTTP_URI_CODING_ALL
, b
, bscheme
, sizeof(bscheme
), buserpass
, sizeof(buserpass
), bhost
, sizeof(bhost
), &bport
, bresource
, sizeof(bresource
)) < HTTP_URI_STATUS_OK
)
812 * Strip trailing dots from the host components, if present...
815 if ((ptr
= ahost
+ strlen(ahost
) - 1) > ahost
&& *ptr
== '.')
818 if ((ptr
= bhost
+ strlen(bhost
) - 1) > bhost
&& *ptr
== '.')
822 * Compare each component...
825 if ((result
= _cups_strcasecmp(ascheme
, bscheme
)) != 0)
828 if ((result
= strcmp(auserpass
, buserpass
)) != 0)
831 if ((result
= _cups_strcasecmp(ahost
, bhost
)) != 0)
835 return (aport
- bport
);
837 if (!_cups_strcasecmp(ascheme
, "mailto") || !_cups_strcasecmp(ascheme
, "urn"))
838 return (_cups_strcasecmp(aresource
, bresource
));
840 return (strcmp(aresource
, bresource
));
845 * 'compare_vars()' - Compare two variables.
848 static int /* O - Result of comparison */
849 compare_vars(_cups_var_t
*a
, /* I - First variable */
850 _cups_var_t
*b
) /* I - Second variable */
852 return (_cups_strcasecmp(a
->name
, b
->name
));
857 * 'do_tests()' - Do tests as specified in the test file.
860 static int /* 1 = success, 0 = failure */
861 do_tests(cups_file_t
*outfile
, /* I - Output file */
862 _cups_vars_t
*vars
, /* I - Variables */
863 const char *testfile
) /* I - Test file to use */
865 int i
, /* Looping var */
866 linenum
, /* Current line number */
867 pass
, /* Did we pass the test? */
868 prev_pass
= 1, /* Did we pass the previous test? */
869 request_id
, /* Current request ID */
870 show_header
= 1, /* Show the test header? */
871 ignore_errors
, /* Ignore test failures? */
872 skip_previous
= 0, /* Skip on previous test failure? */
873 repeat_count
, /* Repeat count */
874 repeat_test
; /* Repeat a test? */
875 useconds_t delay
, /* Initial delay */
876 repeat_interval
; /* Repeat interval (delay) */
877 http_t
*http
= NULL
; /* HTTP connection to server */
878 cups_file_t
*fp
= NULL
; /* Test file */
879 char resource
[512], /* Resource for request */
880 token
[1024], /* Token from file */
881 *tokenptr
, /* Pointer into token */
882 temp
[1024], /* Temporary string */
883 buffer
[131072], /* Copy buffer */
884 compression
[16]; /* COMPRESSION value */
885 ipp_t
*request
= NULL
, /* IPP request */
886 *response
= NULL
; /* IPP response */
887 size_t length
; /* Length of IPP request */
888 http_status_t status
; /* HTTP status */
889 cups_file_t
*reqfile
; /* File to send */
890 ssize_t bytes
; /* Bytes read/written */
891 char attr
[128]; /* Attribute name */
892 ipp_op_t op
; /* Operation */
893 ipp_tag_t group
; /* Current group */
894 ipp_tag_t value
; /* Current value type */
895 ipp_attribute_t
*attrptr
, /* Attribute pointer */
896 *found
, /* Found attribute */
897 *lastcol
= NULL
; /* Last collection attribute */
898 char name
[1024], /* Name of test */
899 file_id
[1024], /* File identifier */
900 test_id
[1024]; /* Test identifier */
901 char filename
[1024]; /* Filename */
902 _cups_transfer_t transfer
; /* To chunk or not to chunk */
903 int version
, /* IPP version number to use */
904 skip_test
; /* Skip this test? */
905 int num_statuses
= 0; /* Number of valid status codes */
906 _cups_status_t statuses
[100], /* Valid status codes */
907 *last_status
; /* Last STATUS (for predicates) */
908 int status_ok
, /* Did we get a matching status? */
909 num_expects
= 0; /* Number of expected attributes */
910 _cups_expect_t expects
[200], /* Expected attributes */
911 *expect
, /* Current expected attribute */
912 *last_expect
; /* Last EXPECT (for predicates) */
913 int num_displayed
= 0; /* Number of displayed attributes */
914 char *displayed
[200]; /* Displayed attributes */
915 size_t widths
[200]; /* Width of columns */
916 cups_array_t
*a
, /* Duplicate attribute array */
917 *errors
= NULL
; /* Errors array */
918 const char *error
; /* Current error */
922 * Open the test file...
925 if ((fp
= cupsFileOpen(testfile
, "r")) == NULL
)
927 print_fatal_error(outfile
, "Unable to open test file %s - %s", testfile
,
934 * Connect to the server...
937 if ((http
= httpConnect2(vars
->hostname
, vars
->port
, NULL
, vars
->family
,
938 vars
->encryption
, 1, 30000, NULL
)) == NULL
)
940 print_fatal_error(outfile
, "Unable to connect to %s on port %d - %s", vars
->hostname
,
941 vars
->port
, cupsLastErrorString());
947 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
,
948 "deflate, gzip, identity");
950 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
, "identity");
951 #endif /* HAVE_LIBZ */
953 if (vars
->timeout
> 0.0)
954 httpSetTimeout(http
, vars
->timeout
, timeout_cb
, NULL
);
960 CUPS_SRAND((unsigned)time(NULL
));
962 errors
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
,
963 (cups_afree_func_t
)free
);
967 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
969 while (!Cancel
&& get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
972 * Expect an open brace...
975 if (!strcmp(token
, "DEFINE"))
981 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
982 get_token(fp
, temp
, sizeof(temp
), &linenum
))
984 expand_variables(vars
, token
, temp
, sizeof(token
));
985 set_variable(outfile
, vars
, attr
, token
);
989 print_fatal_error(outfile
, "Missing DEFINE name and/or value on line %d.",
997 else if (!strcmp(token
, "DEFINE-DEFAULT"))
1000 * DEFINE-DEFAULT name value
1003 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1004 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1006 expand_variables(vars
, token
, temp
, sizeof(token
));
1007 if (!get_variable(vars
, attr
))
1008 set_variable(outfile
, vars
, attr
, token
);
1012 print_fatal_error(outfile
, "Missing DEFINE-DEFAULT name and/or value on line "
1020 else if (!strcmp(token
, "FILE-ID"))
1026 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1028 expand_variables(vars
, file_id
, temp
, sizeof(file_id
));
1032 print_fatal_error(outfile
, "Missing FILE-ID value on line %d.", linenum
);
1039 else if (!strcmp(token
, "IGNORE-ERRORS"))
1046 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1047 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1049 IgnoreErrors
= !_cups_strcasecmp(temp
, "yes");
1053 print_fatal_error(outfile
, "Missing IGNORE-ERRORS value on line %d.", linenum
);
1060 else if (!strcmp(token
, "INCLUDE"))
1063 * INCLUDE "filename"
1064 * INCLUDE <filename>
1067 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1070 * Map the filename to and then run the tests...
1073 if (!do_tests(outfile
, vars
, get_filename(testfile
, filename
, temp
, sizeof(filename
))))
1077 if (StopAfterIncludeError
)
1083 print_fatal_error(outfile
, "Missing INCLUDE filename on line %d.", linenum
);
1091 else if (!strcmp(token
, "INCLUDE-IF-DEFINED"))
1094 * INCLUDE-IF-DEFINED name "filename"
1095 * INCLUDE-IF-DEFINED name <filename>
1098 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1099 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1102 * Map the filename to and then run the tests...
1105 if (get_variable(vars
, attr
) &&
1106 !do_tests(outfile
, vars
, get_filename(testfile
, filename
, temp
, sizeof(filename
))))
1110 if (StopAfterIncludeError
)
1116 print_fatal_error(outfile
, "Missing INCLUDE-IF-DEFINED name or filename on line "
1125 else if (!strcmp(token
, "INCLUDE-IF-NOT-DEFINED"))
1128 * INCLUDE-IF-NOT-DEFINED name "filename"
1129 * INCLUDE-IF-NOT-DEFINED name <filename>
1132 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1133 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1136 * Map the filename to and then run the tests...
1139 if (!get_variable(vars
, attr
) &&
1140 !do_tests(outfile
, vars
, get_filename(testfile
, filename
, temp
, sizeof(filename
))))
1144 if (StopAfterIncludeError
)
1150 print_fatal_error(outfile
, "Missing INCLUDE-IF-NOT-DEFINED name or filename on "
1151 "line %d.", linenum
);
1159 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1162 * SKIP-IF-DEFINED variable
1165 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1167 if (get_variable(vars
, temp
))
1172 print_fatal_error(outfile
, "Missing SKIP-IF-DEFINED variable on line %d.",
1178 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1181 * SKIP-IF-NOT-DEFINED variable
1184 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1186 if (!get_variable(vars
, temp
))
1191 print_fatal_error(outfile
, "Missing SKIP-IF-NOT-DEFINED variable on line %d.",
1197 else if (!strcmp(token
, "STOP-AFTER-INCLUDE-ERROR"))
1200 * STOP-AFTER-INCLUDE-ERROR yes
1201 * STOP-AFTER-INCLUDE-ERROR no
1204 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1205 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1207 StopAfterIncludeError
= !_cups_strcasecmp(temp
, "yes");
1211 print_fatal_error(outfile
, "Missing STOP-AFTER-INCLUDE-ERROR value on line %d.",
1219 else if (!strcmp(token
, "TRANSFER"))
1227 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1229 if (!strcmp(temp
, "auto"))
1230 Transfer
= _CUPS_TRANSFER_AUTO
;
1231 else if (!strcmp(temp
, "chunked"))
1232 Transfer
= _CUPS_TRANSFER_CHUNKED
;
1233 else if (!strcmp(temp
, "length"))
1234 Transfer
= _CUPS_TRANSFER_LENGTH
;
1237 print_fatal_error(outfile
, "Bad TRANSFER value \"%s\" on line %d.", temp
,
1245 print_fatal_error(outfile
, "Missing TRANSFER value on line %d.", linenum
);
1252 else if (!strcmp(token
, "VERSION"))
1254 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1256 if (!strcmp(temp
, "1.0"))
1258 else if (!strcmp(temp
, "1.1"))
1260 else if (!strcmp(temp
, "2.0"))
1262 else if (!strcmp(temp
, "2.1"))
1264 else if (!strcmp(temp
, "2.2"))
1268 print_fatal_error(outfile
, "Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1275 print_fatal_error(outfile
, "Missing VERSION number on line %d.", linenum
);
1282 else if (strcmp(token
, "{"))
1284 print_fatal_error(outfile
, "Unexpected token %s seen on line %d.", token
, linenum
);
1290 * Initialize things...
1295 if (Output
== _CUPS_OUTPUT_PLIST
)
1296 print_xml_header(outfile
);
1297 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= cupsFileStdout()))
1298 cupsFilePrintf(cupsFileStdout(), "\"%s\":\n", testfile
);
1303 strlcpy(resource
, vars
->resource
, sizeof(resource
));
1308 group
= IPP_TAG_ZERO
;
1309 ignore_errors
= IgnoreErrors
;
1317 transfer
= Transfer
;
1318 compression
[0] = '\0';
1321 repeat_interval
= 5000000;
1323 strlcpy(name
, testfile
, sizeof(name
));
1324 if (strrchr(name
, '.') != NULL
)
1325 *strrchr(name
, '.') = '\0';
1328 * Parse until we see a close brace...
1331 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
1333 if (_cups_strcasecmp(token
, "COUNT") &&
1334 _cups_strcasecmp(token
, "DEFINE-MATCH") &&
1335 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1336 _cups_strcasecmp(token
, "DEFINE-VALUE") &&
1337 _cups_strcasecmp(token
, "IF-DEFINED") &&
1338 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1339 _cups_strcasecmp(token
, "IN-GROUP") &&
1340 _cups_strcasecmp(token
, "OF-TYPE") &&
1341 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1342 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1343 _cups_strcasecmp(token
, "REPEAT-NO-MATCH") &&
1344 _cups_strcasecmp(token
, "SAME-COUNT-AS") &&
1345 _cups_strcasecmp(token
, "WITH-ALL-VALUES") &&
1346 _cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") &&
1347 _cups_strcasecmp(token
, "WITH-ALL-RESOURCES") &&
1348 _cups_strcasecmp(token
, "WITH-ALL-SCHEMES") &&
1349 _cups_strcasecmp(token
, "WITH-HOSTNAME") &&
1350 _cups_strcasecmp(token
, "WITH-RESOURCE") &&
1351 _cups_strcasecmp(token
, "WITH-SCHEME") &&
1352 _cups_strcasecmp(token
, "WITH-VALUE") &&
1353 _cups_strcasecmp(token
, "WITH-VALUE-FROM"))
1356 if (_cups_strcasecmp(token
, "DEFINE-MATCH") &&
1357 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1358 _cups_strcasecmp(token
, "IF-DEFINED") &&
1359 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1360 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1361 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1362 _cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
1365 if (!strcmp(token
, "}"))
1367 else if (!strcmp(token
, "{") && lastcol
)
1370 * Another collection value
1373 ipp_t
*col
= get_collection(outfile
, vars
, fp
, &linenum
);
1374 /* Collection value */
1378 ippSetCollection(request
, &lastcol
, ippGetCount(lastcol
), col
);
1386 else if (!strcmp(token
, "COMPRESSION"))
1390 * COMPRESSION deflate
1394 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1396 expand_variables(vars
, compression
, temp
, sizeof(compression
));
1398 if (strcmp(compression
, "none") && strcmp(compression
, "deflate") &&
1399 strcmp(compression
, "gzip"))
1401 if (strcmp(compression
, "none"))
1402 #endif /* HAVE_LIBZ */
1404 print_fatal_error(outfile
, "Unsupported COMPRESSION value '%s' on line %d.",
1405 compression
, linenum
);
1410 if (!strcmp(compression
, "none"))
1411 compression
[0] = '\0';
1415 print_fatal_error(outfile
, "Missing COMPRESSION value on line %d.", linenum
);
1420 else if (!strcmp(token
, "DEFINE"))
1426 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1427 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1429 expand_variables(vars
, token
, temp
, sizeof(token
));
1430 set_variable(outfile
, vars
, attr
, token
);
1434 print_fatal_error(outfile
, "Missing DEFINE name and/or value on line %d.",
1440 else if (!strcmp(token
, "IGNORE-ERRORS"))
1447 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1448 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1450 ignore_errors
= !_cups_strcasecmp(temp
, "yes");
1454 print_fatal_error(outfile
, "Missing IGNORE-ERRORS value on line %d.", linenum
);
1461 else if (!_cups_strcasecmp(token
, "NAME"))
1467 get_token(fp
, temp
, sizeof(temp
), &linenum
);
1468 expand_variables(vars
, name
, temp
, sizeof(name
));
1470 else if (!_cups_strcasecmp(token
, "PAUSE"))
1473 * Pause with a message...
1476 get_token(fp
, token
, sizeof(token
), &linenum
);
1477 pause_message(token
);
1479 else if (!strcmp(token
, "REQUEST-ID"))
1486 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1488 if (isdigit(temp
[0] & 255))
1489 request_id
= atoi(temp
);
1490 else if (!_cups_strcasecmp(temp
, "random"))
1491 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
1494 print_fatal_error(outfile
, "Bad REQUEST-ID value \"%s\" on line %d.", temp
,
1502 print_fatal_error(outfile
, "Missing REQUEST-ID value on line %d.", linenum
);
1507 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1510 * SKIP-IF-DEFINED variable
1513 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1515 if (get_variable(vars
, temp
))
1520 print_fatal_error(outfile
, "Missing SKIP-IF-DEFINED value on line %d.",
1526 else if (!strcmp(token
, "SKIP-IF-MISSING"))
1529 * SKIP-IF-MISSING filename
1532 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1534 expand_variables(vars
, token
, temp
, sizeof(token
));
1535 get_filename(testfile
, filename
, token
, sizeof(filename
));
1537 if (access(filename
, R_OK
))
1542 print_fatal_error(outfile
, "Missing SKIP-IF-MISSING filename on line %d.",
1548 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1551 * SKIP-IF-NOT-DEFINED variable
1554 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1556 if (!get_variable(vars
, temp
))
1561 print_fatal_error(outfile
, "Missing SKIP-IF-NOT-DEFINED value on line %d.",
1567 else if (!strcmp(token
, "SKIP-PREVIOUS-ERROR"))
1570 * SKIP-PREVIOUS-ERROR yes
1571 * SKIP-PREVIOUS-ERROR no
1574 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1575 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1577 skip_previous
= !_cups_strcasecmp(temp
, "yes");
1581 print_fatal_error(outfile
, "Missing SKIP-PREVIOUS-ERROR value on line %d.", linenum
);
1588 else if (!strcmp(token
, "TEST-ID"))
1594 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1596 expand_variables(vars
, test_id
, temp
, sizeof(test_id
));
1600 print_fatal_error(outfile
, "Missing TEST-ID value on line %d.", linenum
);
1607 else if (!strcmp(token
, "TRANSFER"))
1615 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1617 if (!strcmp(temp
, "auto"))
1618 transfer
= _CUPS_TRANSFER_AUTO
;
1619 else if (!strcmp(temp
, "chunked"))
1620 transfer
= _CUPS_TRANSFER_CHUNKED
;
1621 else if (!strcmp(temp
, "length"))
1622 transfer
= _CUPS_TRANSFER_LENGTH
;
1625 print_fatal_error(outfile
, "Bad TRANSFER value \"%s\" on line %d.", temp
,
1633 print_fatal_error(outfile
, "Missing TRANSFER value on line %d.", linenum
);
1638 else if (!_cups_strcasecmp(token
, "VERSION"))
1640 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1642 if (!strcmp(temp
, "0.0"))
1644 else if (!strcmp(temp
, "1.0"))
1646 else if (!strcmp(temp
, "1.1"))
1648 else if (!strcmp(temp
, "2.0"))
1650 else if (!strcmp(temp
, "2.1"))
1652 else if (!strcmp(temp
, "2.2"))
1656 print_fatal_error(outfile
, "Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1663 print_fatal_error(outfile
, "Missing VERSION number on line %d.", linenum
);
1668 else if (!_cups_strcasecmp(token
, "RESOURCE"))
1674 if (!get_token(fp
, resource
, sizeof(resource
), &linenum
))
1676 print_fatal_error(outfile
, "Missing RESOURCE path on line %d.", linenum
);
1681 else if (!_cups_strcasecmp(token
, "OPERATION"))
1687 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1689 print_fatal_error(outfile
, "Missing OPERATION code on line %d.", linenum
);
1694 expand_variables(vars
, token
, temp
, sizeof(token
));
1696 if ((op
= ippOpValue(token
)) == (ipp_op_t
)-1 &&
1697 (op
= (ipp_op_t
)strtol(token
, NULL
, 0)) == 0)
1699 print_fatal_error(outfile
, "Bad OPERATION code \"%s\" on line %d.", token
,
1705 else if (!_cups_strcasecmp(token
, "GROUP"))
1708 * Attribute group...
1711 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1713 print_fatal_error(outfile
, "Missing GROUP tag on line %d.", linenum
);
1718 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
|| value
>= IPP_TAG_UNSUPPORTED_VALUE
)
1720 print_fatal_error(outfile
, "Bad GROUP tag \"%s\" on line %d.", token
, linenum
);
1726 ippAddSeparator(request
);
1730 else if (!_cups_strcasecmp(token
, "DELAY"))
1733 * Delay before operation...
1736 double dval
; /* Delay value */
1738 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1740 print_fatal_error(outfile
, "Missing DELAY value on line %d.", linenum
);
1745 expand_variables(vars
, token
, temp
, sizeof(token
));
1747 if ((dval
= _cupsStrScand(token
, &tokenptr
, localeconv())) < 0.0 || (*tokenptr
&& *tokenptr
!= ','))
1749 print_fatal_error(outfile
, "Bad DELAY value \"%s\" on line %d.", token
,
1755 delay
= (useconds_t
)(1000000.0 * dval
);
1757 if (*tokenptr
== ',')
1759 if ((dval
= _cupsStrScand(tokenptr
+ 1, &tokenptr
, localeconv())) <= 0.0 || *tokenptr
)
1761 print_fatal_error(outfile
, "Bad DELAY value \"%s\" on line %d.", token
,
1767 repeat_interval
= (useconds_t
)(1000000.0 * dval
);
1770 repeat_interval
= delay
;
1772 else if (!_cups_strcasecmp(token
, "ATTR"))
1778 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1780 print_fatal_error(outfile
, "Missing ATTR value tag on line %d.", linenum
);
1785 if ((value
= ippTagValue(token
)) < IPP_TAG_UNSUPPORTED_VALUE
)
1787 print_fatal_error(outfile
, "Bad ATTR value tag \"%s\" on line %d.", token
,
1793 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
1795 print_fatal_error(outfile
, "Missing ATTR name on line %d.", linenum
);
1800 if (value
< IPP_TAG_INTEGER
)
1803 * Add out-of-band value - no value string needed...
1808 else if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1810 print_fatal_error(outfile
, "Missing ATTR value on line %d.", linenum
);
1816 expand_variables(vars
, token
, temp
, sizeof(token
));
1824 if (value
< IPP_TAG_INTEGER
)
1827 * Add out-of-band value...
1830 attrptr
= ippAddOutOfBand(request
, group
, value
, attr
);
1834 print_fatal_error(outfile
, "Unsupported ATTR value tag %s for \"%s\" on line %d.", ippTagString(value
), attr
, linenum
);
1840 case IPP_TAG_BOOLEAN
:
1841 if (!_cups_strcasecmp(token
, "true"))
1842 attrptr
= ippAddBoolean(request
, group
, attr
, 1);
1844 attrptr
= ippAddBoolean(request
, group
, attr
, (char)atoi(token
));
1847 case IPP_TAG_INTEGER
:
1849 if (!strchr(token
, ','))
1851 if (isdigit(token
[0] & 255) || token
[0] == '-')
1852 attrptr
= ippAddInteger(request
, group
, value
, attr
, (int)strtol(token
, &tokenptr
, 0));
1856 if ((i
= ippEnumValue(attr
, tokenptr
)) >= 0)
1858 attrptr
= ippAddInteger(request
, group
, value
, attr
, i
);
1859 tokenptr
+= strlen(tokenptr
);
1865 int values
[100], /* Values */
1866 num_values
= 1; /* Number of values */
1868 if (!isdigit(token
[0] & 255) && token
[0] != '-' && value
== IPP_TAG_ENUM
)
1870 char *ptr
; /* Pointer to next terminator */
1872 if ((ptr
= strchr(token
, ',')) != NULL
)
1875 ptr
= token
+ strlen(token
);
1877 if ((i
= ippEnumValue(attr
, token
)) < 0)
1883 i
= (int)strtol(token
, &tokenptr
, 0);
1887 while (tokenptr
&& *tokenptr
&&
1888 num_values
< (int)(sizeof(values
) / sizeof(values
[0])))
1890 if (*tokenptr
== ',')
1893 if (!isdigit(*tokenptr
& 255) && *tokenptr
!= '-')
1895 char *ptr
; /* Pointer to next terminator */
1897 if (value
!= IPP_TAG_ENUM
)
1900 if ((ptr
= strchr(tokenptr
, ',')) != NULL
)
1903 ptr
= tokenptr
+ strlen(tokenptr
);
1905 if ((i
= ippEnumValue(attr
, tokenptr
)) < 0)
1911 i
= (int)strtol(tokenptr
, &tokenptr
, 0);
1913 values
[num_values
++] = i
;
1916 attrptr
= ippAddIntegers(request
, group
, value
, attr
, num_values
, values
);
1919 if ((!token
[0] || !tokenptr
|| *tokenptr
) && !skip_test
)
1921 print_fatal_error(outfile
, "Bad %s value \'%s\' for \"%s\" on line %d.",
1922 ippTagString(value
), token
, attr
, linenum
);
1928 case IPP_TAG_RESOLUTION
:
1930 int xres
, /* X resolution */
1931 yres
; /* Y resolution */
1932 char *ptr
; /* Pointer into value */
1934 xres
= yres
= (int)strtol(token
, (char **)&ptr
, 10);
1935 if (ptr
> token
&& xres
> 0)
1938 yres
= (int)strtol(ptr
+ 1, (char **)&ptr
, 10);
1941 if (ptr
<= token
|| xres
<= 0 || yres
<= 0 || !ptr
||
1942 (_cups_strcasecmp(ptr
, "dpi") &&
1943 _cups_strcasecmp(ptr
, "dpc") &&
1944 _cups_strcasecmp(ptr
, "dpcm") &&
1945 _cups_strcasecmp(ptr
, "other")))
1950 print_fatal_error(outfile
, "Bad resolution value \'%s\' for \"%s\" on line %d.", token
, attr
, linenum
);
1955 if (!_cups_strcasecmp(ptr
, "dpi"))
1956 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
1957 else if (!_cups_strcasecmp(ptr
, "dpc") ||
1958 !_cups_strcasecmp(ptr
, "dpcm"))
1959 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_CM
, xres
, yres
);
1961 attrptr
= ippAddResolution(request
, group
, attr
, (ipp_res_t
)0, xres
, yres
);
1965 case IPP_TAG_RANGE
:
1967 int lowers
[4], /* Lower value */
1968 uppers
[4], /* Upper values */
1969 num_vals
; /* Number of values */
1972 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
1973 lowers
+ 0, uppers
+ 0,
1974 lowers
+ 1, uppers
+ 1,
1975 lowers
+ 2, uppers
+ 2,
1976 lowers
+ 3, uppers
+ 3);
1978 if ((num_vals
& 1) || num_vals
== 0)
1983 print_fatal_error(outfile
, "Bad rangeOfInteger value \'%s\' for \"%s\" on line %d.", token
, attr
, linenum
);
1988 attrptr
= ippAddRanges(request
, group
, attr
, num_vals
/ 2, lowers
,
1993 case IPP_TAG_BEGIN_COLLECTION
:
1994 if (!strcmp(token
, "{"))
1996 ipp_t
*col
= get_collection(outfile
, vars
, fp
, &linenum
);
1997 /* Collection value */
2001 attrptr
= lastcol
= ippAddCollection(request
, group
, attr
, col
);
2014 print_fatal_error(outfile
, "Bad ATTR collection value for \"%s\" on line %d.", attr
, linenum
);
2021 ipp_t
*col
; /* Collection value */
2022 off_t savepos
= cupsFileTell(fp
); /* Save position of file */
2023 int savelinenum
= linenum
; /* Save line number */
2025 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2028 if (strcmp(token
, ","))
2030 cupsFileSeek(fp
, savepos
);
2031 linenum
= savelinenum
;
2035 if (!get_token(fp
, token
, sizeof(token
), &linenum
) || strcmp(token
, "{"))
2037 print_fatal_error(outfile
, "Unexpected \"%s\" on line %d.", token
, linenum
);
2043 if ((col
= get_collection(outfile
, vars
, fp
, &linenum
)) == NULL
)
2046 ippSetCollection(request
, &attrptr
, ippGetCount(attrptr
), col
);
2049 while (!strcmp(token
, "{"));
2052 case IPP_TAG_STRING
:
2053 attrptr
= ippAddOctetString(request
, group
, attr
, token
, (int)strlen(token
));
2056 case IPP_TAG_TEXTLANG
:
2057 case IPP_TAG_NAMELANG
:
2060 case IPP_TAG_KEYWORD
:
2062 case IPP_TAG_URISCHEME
:
2063 case IPP_TAG_CHARSET
:
2064 case IPP_TAG_LANGUAGE
:
2065 case IPP_TAG_MIMETYPE
:
2066 if (!strchr(token
, ','))
2067 attrptr
= ippAddString(request
, group
, value
, attr
, NULL
, token
);
2071 * Multiple string values...
2074 int num_values
; /* Number of values */
2075 char *values
[100], /* Values */
2076 *ptr
; /* Pointer to next value */
2082 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
2084 if (ptr
> token
&& ptr
[-1] == '\\')
2085 _cups_strcpy(ptr
- 1, ptr
);
2089 values
[num_values
] = ptr
;
2094 attrptr
= ippAddStrings(request
, group
, value
, attr
, num_values
,
2095 NULL
, (const char **)values
);
2100 if (!attrptr
&& !skip_test
)
2102 print_fatal_error(outfile
, "Unable to add attribute \"%s\" on line %d.", attr
, linenum
);
2107 else if (!_cups_strcasecmp(token
, "FILE"))
2113 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
2115 print_fatal_error(outfile
, "Missing FILE filename on line %d.", linenum
);
2120 expand_variables(vars
, token
, temp
, sizeof(token
));
2121 get_filename(testfile
, filename
, token
, sizeof(filename
));
2123 if (access(filename
, R_OK
))
2125 print_fatal_error(outfile
, "Filename \"%s\" on line %d cannot be read.",
2127 print_fatal_error(outfile
, "Filename mapped to \"%s\".", filename
);
2132 else if (!_cups_strcasecmp(token
, "STATUS"))
2138 if (num_statuses
>= (int)(sizeof(statuses
) / sizeof(statuses
[0])))
2140 print_fatal_error(outfile
, "Too many STATUS's on line %d.", linenum
);
2145 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2147 print_fatal_error(outfile
, "Missing STATUS code on line %d.", linenum
);
2152 if ((statuses
[num_statuses
].status
= ippErrorValue(token
))
2153 == (ipp_status_t
)-1 &&
2154 (statuses
[num_statuses
].status
= (ipp_status_t
)strtol(token
, NULL
, 0)) == 0)
2156 print_fatal_error(outfile
, "Bad STATUS code \"%s\" on line %d.", token
,
2162 last_status
= statuses
+ num_statuses
;
2165 last_status
->define_match
= NULL
;
2166 last_status
->define_no_match
= NULL
;
2167 last_status
->if_defined
= NULL
;
2168 last_status
->if_not_defined
= NULL
;
2169 last_status
->repeat_limit
= 1000;
2170 last_status
->repeat_match
= 0;
2171 last_status
->repeat_no_match
= 0;
2173 else if (!_cups_strcasecmp(token
, "EXPECT") || !_cups_strcasecmp(token
, "EXPECT-ALL"))
2176 * Expected attributes...
2179 int expect_all
= !_cups_strcasecmp(token
, "EXPECT-ALL");
2181 if (num_expects
>= (int)(sizeof(expects
) / sizeof(expects
[0])))
2183 print_fatal_error(outfile
, "Too many EXPECT's on line %d.", linenum
);
2188 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2190 print_fatal_error(outfile
, "Missing EXPECT name on line %d.", linenum
);
2195 last_expect
= expects
+ num_expects
;
2198 memset(last_expect
, 0, sizeof(_cups_expect_t
));
2199 last_expect
->repeat_limit
= 1000;
2200 last_expect
->expect_all
= expect_all
;
2202 if (token
[0] == '!')
2204 last_expect
->not_expect
= 1;
2205 last_expect
->name
= strdup(token
+ 1);
2207 else if (token
[0] == '?')
2209 last_expect
->optional
= 1;
2210 last_expect
->name
= strdup(token
+ 1);
2213 last_expect
->name
= strdup(token
);
2215 else if (!_cups_strcasecmp(token
, "COUNT"))
2217 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2219 print_fatal_error(outfile
, "Missing COUNT number on line %d.", linenum
);
2224 if ((i
= atoi(token
)) <= 0)
2226 print_fatal_error(outfile
, "Bad COUNT \"%s\" on line %d.", token
, linenum
);
2232 last_expect
->count
= i
;
2235 print_fatal_error(outfile
, "COUNT without a preceding EXPECT on line %d.",
2241 else if (!_cups_strcasecmp(token
, "DEFINE-MATCH"))
2243 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2245 print_fatal_error(outfile
, "Missing DEFINE-MATCH variable on line %d.",
2252 last_expect
->define_match
= strdup(token
);
2253 else if (last_status
)
2254 last_status
->define_match
= strdup(token
);
2257 print_fatal_error(outfile
, "DEFINE-MATCH without a preceding EXPECT or STATUS "
2258 "on line %d.", linenum
);
2263 else if (!_cups_strcasecmp(token
, "DEFINE-NO-MATCH"))
2265 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2267 print_fatal_error(outfile
, "Missing DEFINE-NO-MATCH variable on line %d.",
2274 last_expect
->define_no_match
= strdup(token
);
2275 else if (last_status
)
2276 last_status
->define_no_match
= strdup(token
);
2279 print_fatal_error(outfile
, "DEFINE-NO-MATCH without a preceding EXPECT or "
2280 "STATUS on line %d.", linenum
);
2285 else if (!_cups_strcasecmp(token
, "DEFINE-VALUE"))
2287 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2289 print_fatal_error(outfile
, "Missing DEFINE-VALUE variable on line %d.",
2296 last_expect
->define_value
= strdup(token
);
2299 print_fatal_error(outfile
, "DEFINE-VALUE without a preceding EXPECT on "
2300 "line %d.", linenum
);
2305 else if (!_cups_strcasecmp(token
, "OF-TYPE"))
2307 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2309 print_fatal_error(outfile
, "Missing OF-TYPE value tag(s) on line %d.",
2316 last_expect
->of_type
= strdup(token
);
2319 print_fatal_error(outfile
, "OF-TYPE without a preceding EXPECT on line %d.",
2325 else if (!_cups_strcasecmp(token
, "IN-GROUP"))
2327 ipp_tag_t in_group
; /* IN-GROUP value */
2330 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2332 print_fatal_error(outfile
, "Missing IN-GROUP group tag on line %d.", linenum
);
2337 if ((in_group
= ippTagValue(token
)) == IPP_TAG_ZERO
|| in_group
>= IPP_TAG_UNSUPPORTED_VALUE
)
2339 print_fatal_error(outfile
, "Bad IN-GROUP group tag \"%s\" on line %d.", token
, linenum
);
2343 else if (last_expect
)
2344 last_expect
->in_group
= in_group
;
2347 print_fatal_error(outfile
, "IN-GROUP without a preceding EXPECT on line %d.", linenum
);
2352 else if (!_cups_strcasecmp(token
, "REPEAT-LIMIT"))
2354 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2356 print_fatal_error(outfile
, "Missing REPEAT-LIMIT value on line %d.", linenum
);
2360 else if (atoi(token
) <= 0)
2362 print_fatal_error(outfile
, "Bad REPEAT-LIMIT value on line %d.", linenum
);
2368 last_status
->repeat_limit
= atoi(token
);
2369 else if (last_expect
)
2370 last_expect
->repeat_limit
= atoi(token
);
2373 print_fatal_error(outfile
, "REPEAT-LIMIT without a preceding EXPECT or STATUS on line %d.", linenum
);
2378 else if (!_cups_strcasecmp(token
, "REPEAT-MATCH"))
2381 last_status
->repeat_match
= 1;
2382 else if (last_expect
)
2383 last_expect
->repeat_match
= 1;
2386 print_fatal_error(outfile
, "REPEAT-MATCH without a preceding EXPECT or STATUS on line %d.", linenum
);
2391 else if (!_cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
2394 last_status
->repeat_no_match
= 1;
2395 else if (last_expect
)
2396 last_expect
->repeat_no_match
= 1;
2399 print_fatal_error(outfile
, "REPEAT-NO-MATCH without a preceding EXPECT or STATUS on ine %d.", linenum
);
2404 else if (!_cups_strcasecmp(token
, "SAME-COUNT-AS"))
2406 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2408 print_fatal_error(outfile
, "Missing SAME-COUNT-AS name on line %d.", linenum
);
2414 last_expect
->same_count_as
= strdup(token
);
2417 print_fatal_error(outfile
, "SAME-COUNT-AS without a preceding EXPECT on line "
2423 else if (!_cups_strcasecmp(token
, "IF-DEFINED"))
2425 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2427 print_fatal_error(outfile
, "Missing IF-DEFINED name on line %d.", linenum
);
2433 last_expect
->if_defined
= strdup(token
);
2434 else if (last_status
)
2435 last_status
->if_defined
= strdup(token
);
2438 print_fatal_error(outfile
, "IF-DEFINED without a preceding EXPECT or STATUS on line %d.", linenum
);
2443 else if (!_cups_strcasecmp(token
, "IF-NOT-DEFINED"))
2445 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2447 print_fatal_error(outfile
, "Missing IF-NOT-DEFINED name on line %d.", linenum
);
2453 last_expect
->if_not_defined
= strdup(token
);
2454 else if (last_status
)
2455 last_status
->if_not_defined
= strdup(token
);
2458 print_fatal_error(outfile
, "IF-NOT-DEFINED without a preceding EXPECT or STATUS on line %d.", linenum
);
2463 else if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") ||
2464 !_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
2465 !_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
2466 !_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
2467 !_cups_strcasecmp(token
, "WITH-HOSTNAME") ||
2468 !_cups_strcasecmp(token
, "WITH-RESOURCE") ||
2469 !_cups_strcasecmp(token
, "WITH-SCHEME") ||
2470 !_cups_strcasecmp(token
, "WITH-VALUE"))
2474 if (!_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
2475 !_cups_strcasecmp(token
, "WITH-HOSTNAME"))
2476 last_expect
->with_flags
= _CUPS_WITH_HOSTNAME
;
2477 else if (!_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
2478 !_cups_strcasecmp(token
, "WITH-RESOURCE"))
2479 last_expect
->with_flags
= _CUPS_WITH_RESOURCE
;
2480 else if (!_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
2481 !_cups_strcasecmp(token
, "WITH-SCHEME"))
2482 last_expect
->with_flags
= _CUPS_WITH_SCHEME
;
2484 if (!_cups_strncasecmp(token
, "WITH-ALL-", 9))
2485 last_expect
->with_flags
|= _CUPS_WITH_ALL
;
2488 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
2490 print_fatal_error(outfile
, "Missing %s value on line %d.", token
, linenum
);
2498 * Expand any variables in the value and then save it.
2501 expand_variables(vars
, token
, temp
, sizeof(token
));
2503 tokenptr
= token
+ strlen(token
) - 1;
2505 if (token
[0] == '/' && tokenptr
> token
&& *tokenptr
== '/')
2508 * WITH-VALUE is a POSIX extended regular expression.
2511 last_expect
->with_value
= calloc(1, (size_t)(tokenptr
- token
));
2512 last_expect
->with_flags
|= _CUPS_WITH_REGEX
;
2514 if (last_expect
->with_value
)
2515 memcpy(last_expect
->with_value
, token
+ 1, (size_t)(tokenptr
- token
- 1));
2520 * WITH-VALUE is a literal value...
2523 char *ptr
; /* Pointer into value */
2525 for (ptr
= token
; *ptr
; ptr
++)
2527 if (*ptr
== '\\' && ptr
[1])
2530 * Remove \ from \foo...
2533 _cups_strcpy(ptr
, ptr
+ 1);
2537 last_expect
->with_value
= strdup(token
);
2538 last_expect
->with_flags
|= _CUPS_WITH_LITERAL
;
2543 print_fatal_error(outfile
, "%s without a preceding EXPECT on line %d.", token
, linenum
);
2548 else if (!_cups_strcasecmp(token
, "WITH-VALUE-FROM"))
2550 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
2552 print_fatal_error(outfile
, "Missing %s value on line %d.", token
, linenum
);
2560 * Expand any variables in the value and then save it.
2563 expand_variables(vars
, token
, temp
, sizeof(token
));
2565 last_expect
->with_value_from
= strdup(token
);
2566 last_expect
->with_flags
= _CUPS_WITH_LITERAL
;
2570 print_fatal_error(outfile
, "%s without a preceding EXPECT on line %d.", token
, linenum
);
2575 else if (!_cups_strcasecmp(token
, "DISPLAY"))
2578 * Display attributes...
2581 if (num_displayed
>= (int)(sizeof(displayed
) / sizeof(displayed
[0])))
2583 print_fatal_error(outfile
, "Too many DISPLAY's on line %d", linenum
);
2588 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2590 print_fatal_error(outfile
, "Missing DISPLAY name on line %d.", linenum
);
2595 displayed
[num_displayed
] = strdup(token
);
2600 print_fatal_error(outfile
, "Unexpected token %s seen on line %d.", token
, linenum
);
2607 * Submit the IPP request...
2612 ippSetVersion(request
, version
/ 10, version
% 10);
2613 ippSetOperation(request
, op
);
2614 ippSetRequestId(request
, request_id
);
2616 if (Output
== _CUPS_OUTPUT_PLIST
)
2618 cupsFilePuts(outfile
, "<dict>\n");
2619 cupsFilePuts(outfile
, "<key>Name</key>\n");
2620 print_xml_string(outfile
, "string", name
);
2623 cupsFilePuts(outfile
, "<key>FileId</key>\n");
2624 print_xml_string(outfile
, "string", file_id
);
2628 cupsFilePuts(outfile
, "<key>TestId</key>\n");
2629 print_xml_string(outfile
, "string", test_id
);
2631 cupsFilePuts(outfile
, "<key>Version</key>\n");
2632 cupsFilePrintf(outfile
, "<string>%d.%d</string>\n", version
/ 10, version
% 10);
2633 cupsFilePuts(outfile
, "<key>Operation</key>\n");
2634 print_xml_string(outfile
, "string", ippOpString(op
));
2635 cupsFilePuts(outfile
, "<key>RequestId</key>\n");
2636 cupsFilePrintf(outfile
, "<integer>%d</integer>\n", request_id
);
2637 cupsFilePuts(outfile
, "<key>RequestAttributes</key>\n");
2638 cupsFilePuts(outfile
, "<array>\n");
2641 cupsFilePuts(outfile
, "<dict>\n");
2642 for (attrptr
= request
->attrs
,
2643 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2645 attrptr
= attrptr
->next
)
2646 print_attr(outfile
, Output
, attrptr
, &group
);
2647 cupsFilePuts(outfile
, "</dict>\n");
2649 cupsFilePuts(outfile
, "</array>\n");
2652 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= cupsFileStdout()))
2656 cupsFilePrintf(cupsFileStdout(), " %s:\n", ippOpString(op
));
2658 for (attrptr
= request
->attrs
; attrptr
; attrptr
= attrptr
->next
)
2659 print_attr(cupsFileStdout(), _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
2662 cupsFilePrintf(cupsFileStdout(), " %-68.68s [", name
);
2665 if ((skip_previous
&& !prev_pass
) || skip_test
)
2672 if (Output
== _CUPS_OUTPUT_PLIST
)
2674 cupsFilePuts(outfile
, "<key>Successful</key>\n");
2675 cupsFilePuts(outfile
, "<true />\n");
2676 cupsFilePuts(outfile
, "<key>Skipped</key>\n");
2677 cupsFilePuts(outfile
, "<true />\n");
2678 cupsFilePuts(outfile
, "<key>StatusCode</key>\n");
2679 print_xml_string(outfile
, "string", "skip");
2680 cupsFilePuts(outfile
, "<key>ResponseAttributes</key>\n");
2681 cupsFilePuts(outfile
, "<dict />\n");
2684 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= cupsFileStdout()))
2685 cupsFilePuts(cupsFileStdout(), "SKIP]\n");
2697 delay
= repeat_interval
;
2700 status
= HTTP_STATUS_OK
;
2702 if (transfer
== _CUPS_TRANSFER_CHUNKED
||
2703 (transfer
== _CUPS_TRANSFER_AUTO
&& filename
[0]))
2706 * Send request using chunking - a 0 length means "chunk".
2714 * Send request using content length...
2717 length
= ippLength(request
);
2719 if (filename
[0] && (reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2722 * Read the file to get the uncompressed file size...
2725 while ((bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
2726 length
+= (size_t)bytes
;
2728 cupsFileClose(reqfile
);
2733 * Send the request...
2740 if (status
!= HTTP_STATUS_ERROR
)
2742 while (!response
&& !Cancel
&& prev_pass
)
2744 status
= cupsSendRequest(http
, request
, resource
, length
);
2748 httpSetField(http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
2749 #endif /* HAVE_LIBZ */
2751 if (!Cancel
&& status
== HTTP_STATUS_CONTINUE
&&
2752 request
->state
== IPP_DATA
&& filename
[0])
2754 if ((reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2757 (bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
2758 if ((status
= cupsWriteRequestData(http
, buffer
, (size_t)bytes
)) != HTTP_STATUS_CONTINUE
)
2761 cupsFileClose(reqfile
);
2765 snprintf(buffer
, sizeof(buffer
), "%s: %s", filename
,
2767 _cupsSetError(IPP_INTERNAL_ERROR
, buffer
, 0);
2769 status
= HTTP_STATUS_ERROR
;
2774 * Get the server's response...
2777 if (!Cancel
&& status
!= HTTP_STATUS_ERROR
)
2779 response
= cupsGetResponse(http
, resource
);
2780 status
= httpGetStatus(http
);
2783 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& http
->error
!= EINVAL
&&
2785 http
->error
!= WSAETIMEDOUT
)
2787 http
->error
!= ETIMEDOUT
)
2790 if (httpReconnect2(http
, 30000, NULL
))
2793 else if (status
== HTTP_STATUS_ERROR
|| status
== HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED
)
2798 else if (status
!= HTTP_STATUS_OK
)
2802 if (status
== HTTP_STATUS_UNAUTHORIZED
)
2810 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& http
->error
!= EINVAL
&&
2812 http
->error
!= WSAETIMEDOUT
)
2814 http
->error
!= ETIMEDOUT
)
2817 if (httpReconnect2(http
, 30000, NULL
))
2820 else if (status
== HTTP_STATUS_ERROR
)
2823 httpReconnect2(http
, 30000, NULL
);
2827 else if (status
!= HTTP_STATUS_OK
)
2834 * Check results of request...
2837 cupsArrayClear(errors
);
2839 if (http
->version
!= HTTP_1_1
)
2840 add_stringf(errors
, "Bad HTTP version (%d.%d)", http
->version
/ 100,
2841 http
->version
% 100);
2843 if (ValidateHeaders
)
2845 const char *header
; /* HTTP header value */
2847 if ((header
= httpGetField(http
, HTTP_FIELD_CONTENT_TYPE
)) == NULL
|| _cups_strcasecmp(header
, "application/ipp"))
2848 add_stringf(errors
, "Bad HTTP Content-Type in response (%s)", header
&& *header
? header
: "<missing>");
2850 if ((header
= httpGetField(http
, HTTP_FIELD_DATE
)) != NULL
&& *header
&& httpGetDateTime(header
) == 0)
2851 add_stringf(errors
, "Bad HTTP Date in response (%s)", header
);
2857 * No response, log error...
2860 add_stringf(errors
, "IPP request failed with status %s (%s)",
2861 ippErrorString(cupsLastError()),
2862 cupsLastErrorString());
2867 * Collect common attribute values...
2870 if ((attrptr
= ippFindAttribute(response
, "job-id",
2871 IPP_TAG_INTEGER
)) != NULL
)
2873 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2874 set_variable(outfile
, vars
, "job-id", temp
);
2877 if ((attrptr
= ippFindAttribute(response
, "job-uri",
2878 IPP_TAG_URI
)) != NULL
)
2879 set_variable(outfile
, vars
, "job-uri", attrptr
->values
[0].string
.text
);
2881 if ((attrptr
= ippFindAttribute(response
, "notify-subscription-id",
2882 IPP_TAG_INTEGER
)) != NULL
)
2884 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2885 set_variable(outfile
, vars
, "notify-subscription-id", temp
);
2889 * Check response, validating groups and attributes and logging errors
2893 if (response
->state
!= IPP_DATA
)
2895 "Missing end-of-attributes-tag in response "
2896 "(RFC 2910 section 3.5.1)");
2899 (response
->request
.status
.version
[0] != (version
/ 10) ||
2900 response
->request
.status
.version
[1] != (version
% 10)))
2902 "Bad version %d.%d in response - expected %d.%d "
2903 "(RFC 2911 section 3.1.8).",
2904 response
->request
.status
.version
[0],
2905 response
->request
.status
.version
[1],
2906 version
/ 10, version
% 10);
2908 if (response
->request
.status
.request_id
!= request_id
)
2910 "Bad request ID %d in response - expected %d "
2911 "(RFC 2911 section 3.1.1)",
2912 response
->request
.status
.request_id
, request_id
);
2914 attrptr
= response
->attrs
;
2917 "Missing first attribute \"attributes-charset "
2918 "(charset)\" in group operation-attributes-tag "
2919 "(RFC 2911 section 3.1.4).");
2922 if (!attrptr
->name
||
2923 attrptr
->value_tag
!= IPP_TAG_CHARSET
||
2924 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2925 attrptr
->num_values
!= 1 ||
2926 strcmp(attrptr
->name
, "attributes-charset"))
2928 "Bad first attribute \"%s (%s%s)\" in group %s, "
2929 "expected \"attributes-charset (charset)\" in "
2930 "group operation-attributes-tag (RFC 2911 section "
2932 attrptr
->name
? attrptr
->name
: "(null)",
2933 attrptr
->num_values
> 1 ? "1setOf " : "",
2934 ippTagString(attrptr
->value_tag
),
2935 ippTagString(attrptr
->group_tag
));
2937 attrptr
= attrptr
->next
;
2940 "Missing second attribute \"attributes-natural-"
2941 "language (naturalLanguage)\" in group "
2942 "operation-attributes-tag (RFC 2911 section "
2944 else if (!attrptr
->name
||
2945 attrptr
->value_tag
!= IPP_TAG_LANGUAGE
||
2946 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2947 attrptr
->num_values
!= 1 ||
2948 strcmp(attrptr
->name
, "attributes-natural-language"))
2950 "Bad first attribute \"%s (%s%s)\" in group %s, "
2951 "expected \"attributes-natural-language "
2952 "(naturalLanguage)\" in group "
2953 "operation-attributes-tag (RFC 2911 section "
2955 attrptr
->name
? attrptr
->name
: "(null)",
2956 attrptr
->num_values
> 1 ? "1setOf " : "",
2957 ippTagString(attrptr
->value_tag
),
2958 ippTagString(attrptr
->group_tag
));
2961 if ((attrptr
= ippFindAttribute(response
, "status-message",
2962 IPP_TAG_ZERO
)) != NULL
)
2964 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2966 "status-message (text(255)) has wrong value tag "
2967 "%s (RFC 2911 section 3.1.6.2).",
2968 ippTagString(attrptr
->value_tag
));
2969 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2971 "status-message (text(255)) has wrong group tag "
2972 "%s (RFC 2911 section 3.1.6.2).",
2973 ippTagString(attrptr
->group_tag
));
2974 if (attrptr
->num_values
!= 1)
2976 "status-message (text(255)) has %d values "
2977 "(RFC 2911 section 3.1.6.2).",
2978 attrptr
->num_values
);
2979 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2980 strlen(attrptr
->values
[0].string
.text
) > 255)
2982 "status-message (text(255)) has bad length %d"
2983 " (RFC 2911 section 3.1.6.2).",
2984 (int)strlen(attrptr
->values
[0].string
.text
));
2987 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
2988 IPP_TAG_ZERO
)) != NULL
)
2990 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2992 "detailed-status-message (text(MAX)) has wrong "
2993 "value tag %s (RFC 2911 section 3.1.6.3).",
2994 ippTagString(attrptr
->value_tag
));
2995 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2997 "detailed-status-message (text(MAX)) has wrong "
2998 "group tag %s (RFC 2911 section 3.1.6.3).",
2999 ippTagString(attrptr
->group_tag
));
3000 if (attrptr
->num_values
!= 1)
3002 "detailed-status-message (text(MAX)) has %d values"
3003 " (RFC 2911 section 3.1.6.3).",
3004 attrptr
->num_values
);
3005 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
3006 strlen(attrptr
->values
[0].string
.text
) > 1023)
3008 "detailed-status-message (text(MAX)) has bad "
3009 "length %d (RFC 2911 section 3.1.6.3).",
3010 (int)strlen(attrptr
->values
[0].string
.text
));
3013 a
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3015 for (attrptr
= response
->attrs
,
3016 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
3018 attrptr
= attrptr
->next
)
3020 if (attrptr
->group_tag
!= group
)
3022 int out_of_order
= 0; /* Are attribute groups out-of-order? */
3025 switch (attrptr
->group_tag
)
3030 case IPP_TAG_OPERATION
:
3034 case IPP_TAG_UNSUPPORTED_GROUP
:
3035 if (group
!= IPP_TAG_OPERATION
)
3040 case IPP_TAG_PRINTER
:
3041 if (group
!= IPP_TAG_OPERATION
&&
3042 group
!= IPP_TAG_UNSUPPORTED_GROUP
)
3046 case IPP_TAG_SUBSCRIPTION
:
3047 if (group
> attrptr
->group_tag
&&
3048 group
!= IPP_TAG_DOCUMENT
)
3053 if (group
> attrptr
->group_tag
)
3059 add_stringf(errors
, "Attribute groups out of order (%s < %s)",
3060 ippTagString(attrptr
->group_tag
),
3061 ippTagString(group
));
3063 if (attrptr
->group_tag
!= IPP_TAG_ZERO
)
3064 group
= attrptr
->group_tag
;
3067 validate_attr(outfile
, errors
, attrptr
);
3071 if (cupsArrayFind(a
, attrptr
->name
))
3072 add_stringf(errors
, "Duplicate \"%s\" attribute in %s group",
3073 attrptr
->name
, ippTagString(group
));
3075 cupsArrayAdd(a
, attrptr
->name
);
3082 * Now check the test-defined expected status-code and attribute
3086 for (i
= 0, status_ok
= 0; i
< num_statuses
; i
++)
3088 if (statuses
[i
].if_defined
&&
3089 !get_variable(vars
, statuses
[i
].if_defined
))
3092 if (statuses
[i
].if_not_defined
&&
3093 get_variable(vars
, statuses
[i
].if_not_defined
))
3096 if (ippGetStatusCode(response
) == statuses
[i
].status
)
3100 if (statuses
[i
].repeat_match
&& repeat_count
< statuses
[i
].repeat_limit
)
3103 if (statuses
[i
].define_match
)
3104 set_variable(outfile
, vars
, statuses
[i
].define_match
, "1");
3108 if (statuses
[i
].repeat_no_match
&& repeat_count
< statuses
[i
].repeat_limit
)
3111 if (statuses
[i
].define_no_match
)
3113 set_variable(outfile
, vars
, statuses
[i
].define_no_match
, "1");
3119 if (!status_ok
&& num_statuses
> 0)
3121 for (i
= 0; i
< num_statuses
; i
++)
3123 if (statuses
[i
].if_defined
&&
3124 !get_variable(vars
, statuses
[i
].if_defined
))
3127 if (statuses
[i
].if_not_defined
&&
3128 get_variable(vars
, statuses
[i
].if_not_defined
))
3131 if (!statuses
[i
].repeat_match
|| repeat_count
>= statuses
[i
].repeat_limit
)
3132 add_stringf(errors
, "EXPECTED: STATUS %s (got %s)",
3133 ippErrorString(statuses
[i
].status
),
3134 ippErrorString(cupsLastError()));
3137 if ((attrptr
= ippFindAttribute(response
, "status-message",
3138 IPP_TAG_TEXT
)) != NULL
)
3139 add_stringf(errors
, "status-message=\"%s\"",
3140 attrptr
->values
[0].string
.text
);
3143 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3145 if (expect
->if_defined
&& !get_variable(vars
, expect
->if_defined
))
3148 if (expect
->if_not_defined
&&
3149 get_variable(vars
, expect
->if_not_defined
))
3152 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
3156 if ((found
&& expect
->not_expect
) ||
3157 (!found
&& !(expect
->not_expect
|| expect
->optional
)) ||
3158 (found
&& !expect_matches(expect
, found
->value_tag
)) ||
3159 (found
&& expect
->in_group
&&
3160 found
->group_tag
!= expect
->in_group
))
3162 if (expect
->define_no_match
)
3163 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3164 else if (!expect
->define_match
&& !expect
->define_value
)
3166 if (found
&& expect
->not_expect
)
3167 add_stringf(errors
, "NOT EXPECTED: %s", expect
->name
);
3168 else if (!found
&& !(expect
->not_expect
|| expect
->optional
))
3169 add_stringf(errors
, "EXPECTED: %s", expect
->name
);
3172 if (!expect_matches(expect
, found
->value_tag
))
3173 add_stringf(errors
, "EXPECTED: %s OF-TYPE %s (got %s)",
3174 expect
->name
, expect
->of_type
,
3175 ippTagString(found
->value_tag
));
3177 if (expect
->in_group
&& found
->group_tag
!= expect
->in_group
)
3178 add_stringf(errors
, "EXPECTED: %s IN-GROUP %s (got %s).",
3179 expect
->name
, ippTagString(expect
->in_group
),
3180 ippTagString(found
->group_tag
));
3184 if (expect
->repeat_no_match
&& repeat_count
< expect
->repeat_limit
)
3191 ippAttributeString(found
, buffer
, sizeof(buffer
));
3193 if (found
&& expect
->with_value_from
&& !with_value_from(NULL
, ippFindAttribute(response
, expect
->with_value_from
, IPP_TAG_ZERO
), found
, buffer
, sizeof(buffer
)))
3195 if (expect
->define_no_match
)
3196 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3197 else if (!expect
->define_match
&& !expect
->define_value
&& ((!expect
->repeat_match
&& !expect
->repeat_no_match
) || repeat_count
>= expect
->repeat_limit
))
3199 add_stringf(errors
, "EXPECTED: %s WITH-VALUES-FROM %s", expect
->name
, expect
->with_value_from
);
3201 with_value_from(errors
, ippFindAttribute(response
, expect
->with_value_from
, IPP_TAG_ZERO
), found
, buffer
, sizeof(buffer
));
3204 if (expect
->repeat_no_match
&& repeat_count
< expect
->repeat_limit
)
3209 else if (found
&& !with_value(outfile
, NULL
, expect
->with_value
, expect
->with_flags
, found
, buffer
, sizeof(buffer
)))
3211 if (expect
->define_no_match
)
3212 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3213 else if (!expect
->define_match
&& !expect
->define_value
&&
3214 !expect
->repeat_match
&& (!expect
->repeat_no_match
|| repeat_count
>= expect
->repeat_limit
))
3216 if (expect
->with_flags
& _CUPS_WITH_REGEX
)
3217 add_stringf(errors
, "EXPECTED: %s %s /%s/", expect
->name
, with_flags_string(expect
->with_flags
), expect
->with_value
);
3219 add_stringf(errors
, "EXPECTED: %s %s \"%s\"", expect
->name
, with_flags_string(expect
->with_flags
), expect
->with_value
);
3221 with_value(outfile
, errors
, expect
->with_value
, expect
->with_flags
, found
, buffer
, sizeof(buffer
));
3224 if (expect
->repeat_no_match
&&
3225 repeat_count
< expect
->repeat_limit
)
3231 if (found
&& expect
->count
> 0 &&
3232 found
->num_values
!= expect
->count
)
3234 if (expect
->define_no_match
)
3235 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3236 else if (!expect
->define_match
&& !expect
->define_value
)
3238 add_stringf(errors
, "EXPECTED: %s COUNT %d (got %d)", expect
->name
,
3239 expect
->count
, found
->num_values
);
3242 if (expect
->repeat_no_match
&&
3243 repeat_count
< expect
->repeat_limit
)
3249 if (found
&& expect
->same_count_as
)
3251 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
3254 if (!attrptr
|| attrptr
->num_values
!= found
->num_values
)
3256 if (expect
->define_no_match
)
3257 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3258 else if (!expect
->define_match
&& !expect
->define_value
)
3262 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
3263 "(not returned)", expect
->name
,
3264 found
->num_values
, expect
->same_count_as
);
3265 else if (attrptr
->num_values
!= found
->num_values
)
3267 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
3268 "(%d values)", expect
->name
, found
->num_values
,
3269 expect
->same_count_as
, attrptr
->num_values
);
3272 if (expect
->repeat_no_match
&&
3273 repeat_count
< expect
->repeat_limit
)
3280 if (found
&& expect
->define_match
)
3281 set_variable(outfile
, vars
, expect
->define_match
, "1");
3283 if (found
&& expect
->define_value
)
3285 if (!expect
->with_value
)
3287 int last
= ippGetCount(found
) - 1;
3288 /* Last element in attribute */
3290 switch (ippGetValueTag(found
))
3293 case IPP_TAG_INTEGER
:
3294 snprintf(buffer
, sizeof(buffer
), "%d", ippGetInteger(found
, last
));
3297 case IPP_TAG_BOOLEAN
:
3298 if (ippGetBoolean(found
, last
))
3299 strlcpy(buffer
, "true", sizeof(buffer
));
3301 strlcpy(buffer
, "false", sizeof(buffer
));
3304 case IPP_TAG_RESOLUTION
:
3306 int xres
, /* Horizontal resolution */
3307 yres
; /* Vertical resolution */
3308 ipp_res_t units
; /* Resolution units */
3310 xres
= ippGetResolution(found
, last
, &yres
, &units
);
3313 snprintf(buffer
, sizeof(buffer
), "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
3315 snprintf(buffer
, sizeof(buffer
), "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
3319 case IPP_TAG_CHARSET
:
3320 case IPP_TAG_KEYWORD
:
3321 case IPP_TAG_LANGUAGE
:
3322 case IPP_TAG_MIMETYPE
:
3324 case IPP_TAG_NAMELANG
:
3326 case IPP_TAG_TEXTLANG
:
3328 case IPP_TAG_URISCHEME
:
3329 strlcpy(buffer
, ippGetString(found
, last
, NULL
), sizeof(buffer
));
3333 ippAttributeString(found
, buffer
, sizeof(buffer
));
3338 set_variable(outfile
, vars
, expect
->define_value
, buffer
);
3341 if (found
&& expect
->repeat_match
&&
3342 repeat_count
< expect
->repeat_limit
)
3345 while (expect
->expect_all
&& (found
= ippFindNextAttribute(response
, expect
->name
, IPP_TAG_ZERO
)) != NULL
);
3350 * If we are going to repeat this test, display intermediate results...
3355 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= cupsFileStdout()))
3357 cupsFilePrintf(cupsFileStdout(), "%04d]\n", repeat_count
);
3359 if (num_displayed
> 0)
3361 for (attrptr
= ippFirstAttribute(response
); attrptr
; attrptr
= ippNextAttribute(response
))
3363 const char *attrname
= ippGetName(attrptr
);
3366 for (i
= 0; i
< num_displayed
; i
++)
3368 if (!strcmp(displayed
[i
], attrname
))
3370 print_attr(cupsFileStdout(), _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
3379 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= cupsFileStdout()))
3381 cupsFilePrintf(cupsFileStdout(), " %-68.68s [", name
);
3384 ippDelete(response
);
3388 while (repeat_test
);
3394 if (cupsArrayCount(errors
) > 0)
3395 prev_pass
= pass
= 0;
3402 if (Output
== _CUPS_OUTPUT_PLIST
)
3404 cupsFilePuts(outfile
, "<key>Successful</key>\n");
3405 cupsFilePuts(outfile
, prev_pass
? "<true />\n" : "<false />\n");
3406 cupsFilePuts(outfile
, "<key>StatusCode</key>\n");
3407 print_xml_string(outfile
, "string", ippErrorString(cupsLastError()));
3408 cupsFilePuts(outfile
, "<key>ResponseAttributes</key>\n");
3409 cupsFilePuts(outfile
, "<array>\n");
3410 cupsFilePuts(outfile
, "<dict>\n");
3411 for (attrptr
= response
? response
->attrs
: NULL
,
3412 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
3414 attrptr
= attrptr
->next
)
3415 print_attr(outfile
, Output
, attrptr
, &group
);
3416 cupsFilePuts(outfile
, "</dict>\n");
3417 cupsFilePuts(outfile
, "</array>\n");
3419 else if (Output
== _CUPS_OUTPUT_IPPSERVER
&& response
)
3421 for (attrptr
= ippFirstAttribute(response
), group
= IPP_TAG_ZERO
; attrptr
; attrptr
= ippNextAttribute(response
))
3423 if (!ippGetName(attrptr
) || ippGetGroupTag(attrptr
) != IPP_TAG_PRINTER
)
3426 print_ippserver_attr(outfile
, attrptr
, 0);
3430 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= cupsFileStdout()))
3432 cupsFilePuts(cupsFileStdout(), prev_pass
? "PASS]\n" : "FAIL]\n");
3434 if (!prev_pass
|| (Verbosity
&& response
))
3436 cupsFilePrintf(cupsFileStdout(), " RECEIVED: %lu bytes in response\n", (unsigned long)ippLength(response
));
3437 cupsFilePrintf(cupsFileStdout(), " status-code = %s (%s)\n", ippErrorString(cupsLastError()), cupsLastErrorString());
3439 if (Verbosity
&& response
)
3441 for (attrptr
= response
->attrs
;
3443 attrptr
= attrptr
->next
)
3444 print_attr(cupsFileStdout(), _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
3448 else if (!prev_pass
&& Output
!= _CUPS_OUTPUT_QUIET
)
3449 fprintf(stderr
, "%s\n", cupsLastErrorString());
3451 if (prev_pass
&& Output
>= _CUPS_OUTPUT_LIST
&& !Verbosity
&&
3454 size_t width
; /* Length of value */
3456 for (i
= 0; i
< num_displayed
; i
++)
3458 widths
[i
] = strlen(displayed
[i
]);
3460 for (attrptr
= ippFindAttribute(response
, displayed
[i
], IPP_TAG_ZERO
);
3462 attrptr
= ippFindNextAttribute(response
, displayed
[i
],
3465 width
= ippAttributeString(attrptr
, NULL
, 0);
3466 if (width
> widths
[i
])
3471 if (Output
== _CUPS_OUTPUT_CSV
)
3472 print_csv(outfile
, NULL
, num_displayed
, displayed
, widths
);
3474 print_line(outfile
, NULL
, num_displayed
, displayed
, widths
);
3476 attrptr
= response
->attrs
;
3480 while (attrptr
&& attrptr
->group_tag
<= IPP_TAG_OPERATION
)
3481 attrptr
= attrptr
->next
;
3485 if (Output
== _CUPS_OUTPUT_CSV
)
3486 print_csv(outfile
, attrptr
, num_displayed
, displayed
, widths
);
3488 print_line(outfile
, attrptr
, num_displayed
, displayed
, widths
);
3490 while (attrptr
&& attrptr
->group_tag
> IPP_TAG_OPERATION
)
3491 attrptr
= attrptr
->next
;
3495 else if (!prev_pass
)
3497 if (Output
== _CUPS_OUTPUT_PLIST
)
3499 cupsFilePuts(outfile
, "<key>Errors</key>\n");
3500 cupsFilePuts(outfile
, "<array>\n");
3502 for (error
= (char *)cupsArrayFirst(errors
);
3504 error
= (char *)cupsArrayNext(errors
))
3505 print_xml_string(outfile
, "string", error
);
3507 cupsFilePuts(outfile
, "</array>\n");
3510 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= cupsFileStdout()))
3512 for (error
= (char *)cupsArrayFirst(errors
);
3514 error
= (char *)cupsArrayNext(errors
))
3515 cupsFilePrintf(cupsFileStdout(), " %s\n", error
);
3519 if (num_displayed
> 0 && !Verbosity
&& response
&& (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= cupsFileStdout())))
3521 for (attrptr
= response
->attrs
;
3523 attrptr
= attrptr
->next
)
3527 for (i
= 0; i
< num_displayed
; i
++)
3529 if (!strcmp(displayed
[i
], attrptr
->name
))
3531 print_attr(outfile
, Output
, attrptr
, NULL
);
3541 if (Output
== _CUPS_OUTPUT_PLIST
)
3542 cupsFilePuts(outfile
, "</dict>\n");
3544 ippDelete(response
);
3547 for (i
= 0; i
< num_statuses
; i
++)
3549 if (statuses
[i
].if_defined
)
3550 free(statuses
[i
].if_defined
);
3551 if (statuses
[i
].if_not_defined
)
3552 free(statuses
[i
].if_not_defined
);
3553 if (statuses
[i
].define_match
)
3554 free(statuses
[i
].define_match
);
3555 if (statuses
[i
].define_no_match
)
3556 free(statuses
[i
].define_no_match
);
3560 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3563 if (expect
->of_type
)
3564 free(expect
->of_type
);
3565 if (expect
->same_count_as
)
3566 free(expect
->same_count_as
);
3567 if (expect
->if_defined
)
3568 free(expect
->if_defined
);
3569 if (expect
->if_not_defined
)
3570 free(expect
->if_not_defined
);
3571 if (expect
->with_value
)
3572 free(expect
->with_value
);
3573 if (expect
->define_match
)
3574 free(expect
->define_match
);
3575 if (expect
->define_no_match
)
3576 free(expect
->define_no_match
);
3577 if (expect
->define_value
)
3578 free(expect
->define_value
);
3582 for (i
= 0; i
< num_displayed
; i
++)
3586 if (!ignore_errors
&& !prev_pass
)
3592 cupsArrayDelete(errors
);
3599 ippDelete(response
);
3601 for (i
= 0; i
< num_statuses
; i
++)
3603 if (statuses
[i
].if_defined
)
3604 free(statuses
[i
].if_defined
);
3605 if (statuses
[i
].if_not_defined
)
3606 free(statuses
[i
].if_not_defined
);
3607 if (statuses
[i
].define_match
)
3608 free(statuses
[i
].define_match
);
3609 if (statuses
[i
].define_no_match
)
3610 free(statuses
[i
].define_no_match
);
3613 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3616 if (expect
->of_type
)
3617 free(expect
->of_type
);
3618 if (expect
->same_count_as
)
3619 free(expect
->same_count_as
);
3620 if (expect
->if_defined
)
3621 free(expect
->if_defined
);
3622 if (expect
->if_not_defined
)
3623 free(expect
->if_not_defined
);
3624 if (expect
->with_value
)
3625 free(expect
->with_value
);
3626 if (expect
->define_match
)
3627 free(expect
->define_match
);
3628 if (expect
->define_no_match
)
3629 free(expect
->define_no_match
);
3630 if (expect
->define_value
)
3631 free(expect
->define_value
);
3634 for (i
= 0; i
< num_displayed
; i
++)
3642 * 'expand_variables()' - Expand variables in a string.
3646 expand_variables(_cups_vars_t
*vars
, /* I - Variables */
3647 char *dst
, /* I - Destination string buffer */
3648 const char *src
, /* I - Source string */
3649 size_t dstsize
) /* I - Size of destination buffer */
3651 char *dstptr
, /* Pointer into destination */
3652 *dstend
, /* End of destination */
3653 temp
[256], /* Temporary string */
3654 *tempptr
; /* Pointer into temporary string */
3655 const char *value
; /* Value to substitute */
3659 dstend
= dst
+ dstsize
- 1;
3661 while (*src
&& dstptr
< dstend
)
3666 * Substitute a string/number...
3669 if (!strncmp(src
, "$$", 2))
3674 else if (!strncmp(src
, "$ENV[", 5))
3676 strlcpy(temp
, src
+ 5, sizeof(temp
));
3678 for (tempptr
= temp
; *tempptr
; tempptr
++)
3679 if (*tempptr
== ']')
3685 value
= getenv(temp
);
3686 src
+= tempptr
- temp
+ 5;
3693 strlcpy(temp
, src
, sizeof(temp
));
3694 if ((tempptr
= strchr(temp
, '}')) != NULL
)
3697 tempptr
= temp
+ strlen(temp
);
3701 strlcpy(temp
, src
+ 1, sizeof(temp
));
3703 for (tempptr
= temp
; *tempptr
; tempptr
++)
3704 if (!isalnum(*tempptr
& 255) && *tempptr
!= '-' && *tempptr
!= '_')
3711 if (!strcmp(temp
, "uri"))
3713 else if (!strcmp(temp
, "filename"))
3714 value
= vars
->filename
;
3715 else if (!strcmp(temp
, "scheme") || !strcmp(temp
, "method"))
3716 value
= vars
->scheme
;
3717 else if (!strcmp(temp
, "username"))
3718 value
= vars
->userpass
;
3719 else if (!strcmp(temp
, "hostname"))
3720 value
= vars
->hostname
;
3721 else if (!strcmp(temp
, "port"))
3723 snprintf(temp
, sizeof(temp
), "%d", vars
->port
);
3726 else if (!strcmp(temp
, "resource"))
3727 value
= vars
->resource
;
3728 else if (!strcmp(temp
, "user"))
3731 value
= get_variable(vars
, temp
);
3733 src
+= tempptr
- temp
+ 1;
3738 strlcpy(dstptr
, value
, (size_t)(dstend
- dstptr
+ 1));
3739 dstptr
+= strlen(dstptr
);
3751 * 'expect_matches()' - Return true if the tag matches the specification.
3754 static int /* O - 1 if matches, 0 otherwise */
3756 _cups_expect_t
*expect
, /* I - Expected attribute */
3757 ipp_tag_t value_tag
) /* I - Value tag for attribute */
3759 int match
; /* Match? */
3760 char *of_type
, /* Type name to match */
3761 *next
, /* Next name to match */
3762 sep
; /* Separator character */
3766 * If we don't expect a particular type, return immediately...
3769 if (!expect
->of_type
)
3773 * Parse the "of_type" value since the string can contain multiple attribute
3774 * types separated by "," or "|"...
3777 for (of_type
= expect
->of_type
, match
= 0; !match
&& *of_type
; of_type
= next
)
3780 * Find the next separator, and set it (temporarily) to nul if present.
3783 for (next
= of_type
; *next
&& *next
!= '|' && *next
!= ','; next
++);
3785 if ((sep
= *next
) != '\0')
3789 * Support some meta-types to make it easier to write the test file.
3792 if (!strcmp(of_type
, "text"))
3793 match
= value_tag
== IPP_TAG_TEXTLANG
|| value_tag
== IPP_TAG_TEXT
;
3794 else if (!strcmp(of_type
, "name"))
3795 match
= value_tag
== IPP_TAG_NAMELANG
|| value_tag
== IPP_TAG_NAME
;
3796 else if (!strcmp(of_type
, "collection"))
3797 match
= value_tag
== IPP_TAG_BEGIN_COLLECTION
;
3799 match
= value_tag
== ippTagValue(of_type
);
3802 * Restore the separator if we have one...
3814 * 'get_collection()' - Get a collection value from the current test file.
3817 static ipp_t
* /* O - Collection value */
3818 get_collection(cups_file_t
*outfile
, /* I - Output file */
3819 _cups_vars_t
*vars
, /* I - Variables */
3820 cups_file_t
*fp
, /* I - File to read from */
3821 int *linenum
) /* IO - Line number */
3823 char token
[1024], /* Token from file */
3824 temp
[1024], /* Temporary string */
3825 attr
[128]; /* Attribute name */
3826 ipp_tag_t value
; /* Current value type */
3827 ipp_t
*col
= ippNew(); /* Collection value */
3828 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
3831 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
3833 if (!strcmp(token
, "}"))
3835 else if (!strcmp(token
, "{") && lastcol
)
3838 * Another collection value
3841 ipp_t
*subcol
= get_collection(outfile
, vars
, fp
, linenum
);
3842 /* Collection value */
3845 ippSetCollection(col
, &lastcol
, ippGetCount(lastcol
), subcol
);
3849 else if (!_cups_strcasecmp(token
, "MEMBER"))
3857 if (!get_token(fp
, token
, sizeof(token
), linenum
))
3859 print_fatal_error(outfile
, "Missing MEMBER value tag on line %d.", *linenum
);
3863 if ((value
= ippTagValue(token
)) < IPP_TAG_UNSUPPORTED_VALUE
)
3865 print_fatal_error(outfile
, "Bad MEMBER value tag \"%s\" on line %d.", token
,
3870 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
3872 print_fatal_error(outfile
, "Missing MEMBER name on line %d.", *linenum
);
3876 if (value
< IPP_TAG_INTEGER
)
3879 * Out-of-band member attributes have no value...
3884 else if (!get_token(fp
, temp
, sizeof(temp
), linenum
))
3886 print_fatal_error(outfile
, "Missing MEMBER value on line %d.", *linenum
);
3891 expand_variables(vars
, token
, temp
, sizeof(token
));
3897 if (value
< IPP_TAG_INTEGER
)
3900 * Add out-of-band value...
3903 ippAddOutOfBand(col
, IPP_TAG_ZERO
, value
, attr
);
3907 print_fatal_error(outfile
, "Unsupported MEMBER value tag %s for \"%s\" on line %d.", ippTagString(value
), attr
, *linenum
);
3912 case IPP_TAG_BOOLEAN
:
3913 if (!_cups_strcasecmp(token
, "true"))
3914 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
3916 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, (char)atoi(token
));
3919 case IPP_TAG_INTEGER
:
3921 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
3924 case IPP_TAG_RESOLUTION
:
3926 int xres
, /* X resolution */
3927 yres
; /* Y resolution */
3928 char units
[6]; /* Units */
3930 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
3931 (_cups_strcasecmp(units
, "dpi") &&
3932 _cups_strcasecmp(units
, "dpc") &&
3933 _cups_strcasecmp(units
, "dpcm") &&
3934 _cups_strcasecmp(units
, "other")))
3936 print_fatal_error(outfile
, "Bad resolution value \"%s\" on line %d.",
3941 if (!_cups_strcasecmp(units
, "dpi"))
3942 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
3943 else if (!_cups_strcasecmp(units
, "dpc") ||
3944 !_cups_strcasecmp(units
, "dpcm"))
3945 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_CM
, xres
, yres
);
3947 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, (ipp_res_t
)0, xres
, yres
);
3951 case IPP_TAG_RANGE
:
3953 int lowers
[4], /* Lower value */
3954 uppers
[4], /* Upper values */
3955 num_vals
; /* Number of values */
3958 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
3959 lowers
+ 0, uppers
+ 0,
3960 lowers
+ 1, uppers
+ 1,
3961 lowers
+ 2, uppers
+ 2,
3962 lowers
+ 3, uppers
+ 3);
3964 if ((num_vals
& 1) || num_vals
== 0)
3966 print_fatal_error(outfile
, "Bad rangeOfInteger value \"%s\" on line %d.",
3971 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
3976 case IPP_TAG_BEGIN_COLLECTION
:
3977 if (!strcmp(token
, "{"))
3979 ipp_t
*subcol
= get_collection(outfile
, vars
, fp
, linenum
);
3980 /* Collection value */
3984 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
3992 print_fatal_error(outfile
, "Bad collection value on line %d.", *linenum
);
3997 case IPP_TAG_STRING
:
3998 ippAddOctetString(col
, IPP_TAG_ZERO
, attr
, token
, (int)strlen(token
));
4001 case IPP_TAG_TEXTLANG
:
4002 case IPP_TAG_NAMELANG
:
4005 case IPP_TAG_KEYWORD
:
4007 case IPP_TAG_URISCHEME
:
4008 case IPP_TAG_CHARSET
:
4009 case IPP_TAG_LANGUAGE
:
4010 case IPP_TAG_MIMETYPE
:
4011 if (!strchr(token
, ','))
4012 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
4016 * Multiple string values...
4019 int num_values
; /* Number of values */
4020 char *values
[100], /* Values */
4021 *ptr
; /* Pointer to next value */
4027 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
4029 if (ptr
> token
&& ptr
[-1] == '\\')
4030 _cups_strcpy(ptr
- 1, ptr
);
4034 values
[num_values
] = ptr
;
4039 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
4040 NULL
, (const char **)values
);
4047 print_fatal_error(outfile
, "Unexpected token %s seen on line %d.", token
, *linenum
);
4055 * If we get here there was a parse error; free memory and return.
4067 * 'get_filename()' - Get a filename based on the current test file.
4070 static char * /* O - Filename */
4071 get_filename(const char *testfile
, /* I - Current test file */
4072 char *dst
, /* I - Destination filename */
4073 const char *src
, /* I - Source filename */
4074 size_t dstsize
) /* I - Size of destination buffer */
4076 char *dstptr
; /* Pointer into destination */
4077 _cups_globals_t
*cg
= _cupsGlobals();
4081 if (*src
== '<' && src
[strlen(src
) - 1] == '>')
4084 * Map <filename> to CUPS_DATADIR/ipptool/filename...
4087 snprintf(dst
, dstsize
, "%s/ipptool/%s", cg
->cups_datadir
, src
+ 1);
4088 dstptr
= dst
+ strlen(dst
) - 1;
4092 else if (*src
== '/' || !strchr(testfile
, '/')
4094 || (isalpha(*src
& 255) && src
[1] == ':')
4099 * Use the path as-is...
4102 strlcpy(dst
, src
, dstsize
);
4107 * Make path relative to testfile...
4110 strlcpy(dst
, testfile
, dstsize
);
4111 if ((dstptr
= strrchr(dst
, '/')) != NULL
)
4114 dstptr
= dst
; /* Should never happen */
4116 strlcpy(dstptr
, src
, dstsize
- (size_t)(dstptr
- dst
));
4124 * 'get_string()' - Get a pointer to a string value or the portion of interest.
4127 static const char * /* O - Pointer to string */
4128 get_string(ipp_attribute_t
*attr
, /* I - IPP attribute */
4129 int element
, /* I - Element to fetch */
4130 int flags
, /* I - Value ("with") flags */
4131 char *buffer
, /* I - Temporary buffer */
4132 size_t bufsize
) /* I - Size of temporary buffer */
4134 const char *value
; /* Value */
4135 char *ptr
, /* Pointer into value */
4136 scheme
[256], /* URI scheme */
4137 userpass
[256], /* Username/password */
4138 hostname
[256], /* Hostname */
4139 resource
[1024]; /* Resource */
4140 int port
; /* Port number */
4143 value
= ippGetString(attr
, element
, NULL
);
4145 if (flags
& _CUPS_WITH_HOSTNAME
)
4147 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), buffer
, (int)bufsize
, &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
4150 ptr
= buffer
+ strlen(buffer
) - 1;
4151 if (ptr
>= buffer
&& *ptr
== '.')
4152 *ptr
= '\0'; /* Drop trailing "." */
4156 else if (flags
& _CUPS_WITH_RESOURCE
)
4158 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, buffer
, (int)bufsize
) < HTTP_URI_STATUS_OK
)
4163 else if (flags
& _CUPS_WITH_SCHEME
)
4165 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, buffer
, (int)bufsize
, userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
4170 else if (ippGetValueTag(attr
) == IPP_TAG_URI
&& (!strncmp(value
, "ipp://", 6) || !strncmp(value
, "http://", 7) || !strncmp(value
, "ipps://", 7) || !strncmp(value
, "https://", 8)))
4172 http_uri_status_t status
= httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
));
4174 if (status
< HTTP_URI_STATUS_OK
)
4185 * Normalize URI with no trailing dot...
4188 if ((ptr
= hostname
+ strlen(hostname
) - 1) >= hostname
&& *ptr
== '.')
4191 httpAssembleURI(HTTP_URI_CODING_ALL
, buffer
, (int)bufsize
, scheme
, userpass
, hostname
, port
, resource
);
4202 * 'get_token()' - Get a token from a file.
4205 static char * /* O - Token from file or NULL on EOF */
4206 get_token(cups_file_t
*fp
, /* I - File to read from */
4207 char *buf
, /* I - Buffer to read into */
4208 int buflen
, /* I - Length of buffer */
4209 int *linenum
) /* IO - Current line number */
4211 int ch
, /* Character from file */
4212 quote
; /* Quoting character */
4213 char *bufptr
, /* Pointer into buffer */
4214 *bufend
; /* End of buffer */
4220 * Skip whitespace...
4223 while (isspace(ch
= cupsFileGetChar(fp
)))
4235 else if (ch
== '\'' || ch
== '\"')
4238 * Quoted text or regular expression...
4243 bufend
= buf
+ buflen
- 1;
4245 while ((ch
= cupsFileGetChar(fp
)) != EOF
)
4250 * Escape next character...
4253 if (bufptr
< bufend
)
4254 *bufptr
++ = (char)ch
;
4256 if ((ch
= cupsFileGetChar(fp
)) != EOF
&& bufptr
< bufend
)
4257 *bufptr
++ = (char)ch
;
4259 else if (ch
== quote
)
4261 else if (bufptr
< bufend
)
4262 *bufptr
++ = (char)ch
;
4275 while ((ch
= cupsFileGetChar(fp
)) != EOF
)
4281 else if (ch
== '{' || ch
== '}' || ch
== ',')
4291 * Whitespace delimited text...
4294 cupsFileSeek(fp
, cupsFileTell(fp
) - 1);
4297 bufend
= buf
+ buflen
- 1;
4299 while ((ch
= cupsFileGetChar(fp
)) != EOF
)
4300 if (isspace(ch
) || ch
== '#')
4302 else if (bufptr
< bufend
)
4303 *bufptr
++ = (char)ch
;
4306 cupsFileSeek(fp
, cupsFileTell(fp
) - 1);
4307 else if (ch
== '\n')
4319 * 'get_variable()' - Get the value of a variable.
4322 static char * /* O - Value or NULL */
4323 get_variable(_cups_vars_t
*vars
, /* I - Variables */
4324 const char *name
) /* I - Variable name */
4326 _cups_var_t key
, /* Search key */
4327 *match
; /* Matching variable, if any */
4330 key
.name
= (char *)name
;
4331 match
= cupsArrayFind(vars
->vars
, &key
);
4333 return (match
? match
->value
: NULL
);
4338 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
4342 static char * /* O - ISO 8601 date/time string */
4343 iso_date(const ipp_uchar_t
*date
) /* I - IPP (RFC 1903) date/time value */
4345 time_t utctime
; /* UTC time since 1970 */
4346 struct tm
*utcdate
; /* UTC date/time */
4347 static char buffer
[255]; /* String buffer */
4350 utctime
= ippDateToTime(date
);
4351 utcdate
= gmtime(&utctime
);
4353 snprintf(buffer
, sizeof(buffer
), "%04d-%02d-%02dT%02d:%02d:%02dZ",
4354 utcdate
->tm_year
+ 1900, utcdate
->tm_mon
+ 1, utcdate
->tm_mday
,
4355 utcdate
->tm_hour
, utcdate
->tm_min
, utcdate
->tm_sec
);
4362 * 'password_cb()' - Password callback for authenticated tests.
4365 static const char * /* O - Password */
4366 password_cb(const char *prompt
) /* I - Prompt (unused) */
4370 if (PasswordTries
< 3)
4374 cupsSetUser(Username
);
4384 * 'pause_message()' - Display the message and pause until the user presses a key.
4388 pause_message(const char *message
) /* I - Message */
4391 HANDLE tty
; /* Console handle */
4392 DWORD mode
; /* Console mode */
4393 char key
; /* Key press */
4394 DWORD bytes
; /* Bytes read for key press */
4398 * Disable input echo and set raw input...
4401 if ((tty
= GetStdHandle(STD_INPUT_HANDLE
)) == INVALID_HANDLE_VALUE
)
4404 if (!GetConsoleMode(tty
, &mode
))
4407 if (!SetConsoleMode(tty
, 0))
4411 int tty
; /* /dev/tty - never read from stdin */
4412 struct termios original
, /* Original input mode */
4413 noecho
; /* No echo input mode */
4414 char key
; /* Current key press */
4418 * Disable input echo and set raw input...
4421 if ((tty
= open("/dev/tty", O_RDONLY
)) < 0)
4424 if (tcgetattr(tty
, &original
))
4431 noecho
.c_lflag
&= (tcflag_t
)~(ICANON
| ECHO
| ECHOE
| ISIG
);
4433 if (tcsetattr(tty
, TCSAFLUSH
, &noecho
))
4441 * Display the prompt...
4444 cupsFilePrintf(cupsFileStdout(), "%s\n---- PRESS ANY KEY ----", message
);
4451 ReadFile(tty
, &key
, 1, &bytes
, NULL
);
4457 SetConsoleMode(tty
, mode
);
4470 tcsetattr(tty
, TCSAFLUSH
, &original
);
4475 * Erase the "press any key" prompt...
4478 cupsFilePuts(cupsFileStdout(), "\r \r");
4483 * 'print_attr()' - Print an attribute on the screen.
4487 print_attr(cups_file_t
*outfile
, /* I - Output file */
4488 int format
, /* I - Output format */
4489 ipp_attribute_t
*attr
, /* I - Attribute to print */
4490 ipp_tag_t
*group
) /* IO - Current group */
4492 int i
; /* Looping var */
4493 ipp_attribute_t
*colattr
; /* Collection attribute */
4496 if (format
== _CUPS_OUTPUT_PLIST
)
4498 if (!attr
->name
|| (group
&& *group
!= attr
->group_tag
))
4500 if (attr
->group_tag
!= IPP_TAG_ZERO
)
4502 cupsFilePuts(outfile
, "</dict>\n");
4503 cupsFilePuts(outfile
, "<dict>\n");
4507 *group
= attr
->group_tag
;
4513 print_xml_string(outfile
, "key", attr
->name
);
4514 if (attr
->num_values
> 1)
4515 cupsFilePuts(outfile
, "<array>\n");
4517 switch (attr
->value_tag
)
4519 case IPP_TAG_INTEGER
:
4521 for (i
= 0; i
< attr
->num_values
; i
++)
4522 cupsFilePrintf(outfile
, "<integer>%d</integer>\n", attr
->values
[i
].integer
);
4525 case IPP_TAG_BOOLEAN
:
4526 for (i
= 0; i
< attr
->num_values
; i
++)
4527 cupsFilePuts(outfile
, attr
->values
[i
].boolean
? "<true />\n" : "<false />\n");
4530 case IPP_TAG_RANGE
:
4531 for (i
= 0; i
< attr
->num_values
; i
++)
4532 cupsFilePrintf(outfile
, "<dict><key>lower</key><integer>%d</integer>"
4533 "<key>upper</key><integer>%d</integer></dict>\n",
4534 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
4537 case IPP_TAG_RESOLUTION
:
4538 for (i
= 0; i
< attr
->num_values
; i
++)
4539 cupsFilePrintf(outfile
, "<dict><key>xres</key><integer>%d</integer>"
4540 "<key>yres</key><integer>%d</integer>"
4541 "<key>units</key><string>%s</string></dict>\n",
4542 attr
->values
[i
].resolution
.xres
,
4543 attr
->values
[i
].resolution
.yres
,
4544 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
4549 for (i
= 0; i
< attr
->num_values
; i
++)
4550 cupsFilePrintf(outfile
, "<date>%s</date>\n", iso_date(attr
->values
[i
].date
));
4553 case IPP_TAG_STRING
:
4554 for (i
= 0; i
< attr
->num_values
; i
++)
4556 char buffer
[IPP_MAX_LENGTH
* 5 / 4 + 1];
4559 cupsFilePrintf(outfile
, "<data>%s</data>\n",
4560 httpEncode64_2(buffer
, sizeof(buffer
),
4561 attr
->values
[i
].unknown
.data
,
4562 attr
->values
[i
].unknown
.length
));
4568 case IPP_TAG_KEYWORD
:
4570 case IPP_TAG_URISCHEME
:
4571 case IPP_TAG_CHARSET
:
4572 case IPP_TAG_LANGUAGE
:
4573 case IPP_TAG_MIMETYPE
:
4574 for (i
= 0; i
< attr
->num_values
; i
++)
4575 print_xml_string(outfile
, "string", attr
->values
[i
].string
.text
);
4578 case IPP_TAG_TEXTLANG
:
4579 case IPP_TAG_NAMELANG
:
4580 for (i
= 0; i
< attr
->num_values
; i
++)
4582 cupsFilePuts(outfile
, "<dict><key>language</key><string>");
4583 print_xml_string(outfile
, NULL
, attr
->values
[i
].string
.language
);
4584 cupsFilePuts(outfile
, "</string><key>string</key><string>");
4585 print_xml_string(outfile
, NULL
, attr
->values
[i
].string
.text
);
4586 cupsFilePuts(outfile
, "</string></dict>\n");
4590 case IPP_TAG_BEGIN_COLLECTION
:
4591 for (i
= 0; i
< attr
->num_values
; i
++)
4593 cupsFilePuts(outfile
, "<dict>\n");
4594 for (colattr
= attr
->values
[i
].collection
->attrs
;
4596 colattr
= colattr
->next
)
4597 print_attr(outfile
, format
, colattr
, NULL
);
4598 cupsFilePuts(outfile
, "</dict>\n");
4603 cupsFilePrintf(outfile
, "<string><<%s>></string>\n", ippTagString(attr
->value_tag
));
4607 if (attr
->num_values
> 1)
4608 cupsFilePuts(outfile
, "</array>\n");
4612 char buffer
[131072]; /* Value buffer */
4614 if (format
== _CUPS_OUTPUT_TEST
)
4618 cupsFilePuts(outfile
, " -- separator --\n");
4622 cupsFilePrintf(outfile
, " %s (%s%s) = ", attr
->name
, attr
->num_values
> 1 ? "1setOf " : "", ippTagString(attr
->value_tag
));
4625 ippAttributeString(attr
, buffer
, sizeof(buffer
));
4626 cupsFilePrintf(outfile
, "%s\n", buffer
);
4632 * 'print_csv()' - Print a line of CSV text.
4637 cups_file_t
*outfile
, /* I - Output file */
4638 ipp_attribute_t
*attr
, /* I - First attribute for line */
4639 int num_displayed
, /* I - Number of attributes to display */
4640 char **displayed
, /* I - Attributes to display */
4641 size_t *widths
) /* I - Column widths */
4643 int i
; /* Looping var */
4644 size_t maxlength
; /* Max length of all columns */
4645 char *buffer
, /* String buffer */
4646 *bufptr
; /* Pointer into buffer */
4647 ipp_attribute_t
*current
; /* Current attribute */
4651 * Get the maximum string length we have to show and allocate...
4654 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4655 if (widths
[i
] > maxlength
)
4656 maxlength
= widths
[i
];
4660 if ((buffer
= malloc(maxlength
)) == NULL
)
4664 * Loop through the attributes to display...
4669 for (i
= 0; i
< num_displayed
; i
++)
4672 cupsFilePutChar(outfile
, ',');
4676 for (current
= attr
; current
; current
= current
->next
)
4680 else if (!strcmp(current
->name
, displayed
[i
]))
4682 ippAttributeString(current
, buffer
, maxlength
);
4687 if (strchr(buffer
, ',') != NULL
|| strchr(buffer
, '\"') != NULL
||
4688 strchr(buffer
, '\\') != NULL
)
4690 cupsFilePutChar(cupsFileStdout(), '\"');
4691 for (bufptr
= buffer
; *bufptr
; bufptr
++)
4693 if (*bufptr
== '\\' || *bufptr
== '\"')
4694 cupsFilePutChar(cupsFileStdout(), '\\');
4695 cupsFilePutChar(cupsFileStdout(), *bufptr
);
4697 cupsFilePutChar(cupsFileStdout(), '\"');
4700 cupsFilePuts(outfile
, buffer
);
4702 cupsFilePutChar(cupsFileStdout(), '\n');
4706 for (i
= 0; i
< num_displayed
; i
++)
4709 cupsFilePutChar(cupsFileStdout(), ',');
4711 cupsFilePuts(outfile
, displayed
[i
]);
4713 cupsFilePutChar(cupsFileStdout(), '\n');
4721 * 'print_fatal_error()' - Print a fatal error message.
4725 print_fatal_error(cups_file_t
*outfile
, /* I - Output file */
4726 const char *s
, /* I - Printf-style format string */
4727 ...) /* I - Additional arguments as needed */
4729 char buffer
[10240]; /* Format buffer */
4730 va_list ap
; /* Pointer to arguments */
4734 * Format the error message...
4738 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
4745 if (Output
== _CUPS_OUTPUT_PLIST
)
4747 print_xml_header(outfile
);
4748 print_xml_trailer(outfile
, 0, buffer
);
4751 _cupsLangPrintf(stderr
, "ipptool: %s", buffer
);
4756 * 'print_ippserver_attr()' - Print a attribute suitable for use by ippserver.
4760 print_ippserver_attr(
4761 cups_file_t
*outfile
, /* I - Output file */
4762 ipp_attribute_t
*attr
, /* I - Attribute to print */
4763 int indent
) /* I - Indentation level */
4765 int i
, /* Looping var */
4766 count
= ippGetCount(attr
);
4767 /* Number of values */
4768 ipp_attribute_t
*colattr
; /* Collection attribute */
4772 cupsFilePrintf(outfile
, "ATTR %s %s", ippTagString(ippGetValueTag(attr
)), ippGetName(attr
));
4774 cupsFilePrintf(outfile
, "%*sMEMBER %s %s", indent
, "", ippTagString(ippGetValueTag(attr
)), ippGetName(attr
));
4776 switch (ippGetValueTag(attr
))
4778 case IPP_TAG_INTEGER
:
4780 for (i
= 0; i
< count
; i
++)
4781 cupsFilePrintf(outfile
, "%s%d", i
? "," : " ", ippGetInteger(attr
, i
));
4784 case IPP_TAG_BOOLEAN
:
4785 cupsFilePuts(outfile
, ippGetBoolean(attr
, 0) ? " true" : " false");
4787 for (i
= 1; i
< count
; i
++)
4788 cupsFilePuts(outfile
, ippGetBoolean(attr
, 1) ? ",true" : ",false");
4791 case IPP_TAG_RANGE
:
4792 for (i
= 0; i
< count
; i
++)
4794 int upper
, lower
= ippGetRange(attr
, i
, &upper
);
4796 cupsFilePrintf(outfile
, "%s%d-%d", i
? "," : " ", lower
, upper
);
4800 case IPP_TAG_RESOLUTION
:
4801 for (i
= 0; i
< count
; i
++)
4804 int yres
, xres
= ippGetResolution(attr
, i
, &yres
, &units
);
4806 cupsFilePrintf(outfile
, "%s%dx%d%s", i
? "," : " ", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
4811 for (i
= 0; i
< count
; i
++)
4812 cupsFilePrintf(outfile
, "%s%s", i
? "," : " ", iso_date(ippGetDate(attr
, i
)));
4815 case IPP_TAG_STRING
:
4816 for (i
= 0; i
< count
; i
++)
4819 const char *s
= (const char *)ippGetOctetString(attr
, i
, &len
);
4821 cupsFilePuts(outfile
, i
? "," : " ");
4822 print_ippserver_string(outfile
, s
, (size_t)len
);
4827 case IPP_TAG_TEXTLANG
:
4829 case IPP_TAG_NAMELANG
:
4830 case IPP_TAG_KEYWORD
:
4832 case IPP_TAG_URISCHEME
:
4833 case IPP_TAG_CHARSET
:
4834 case IPP_TAG_LANGUAGE
:
4835 case IPP_TAG_MIMETYPE
:
4836 for (i
= 0; i
< count
; i
++)
4838 const char *s
= ippGetString(attr
, i
, NULL
);
4840 cupsFilePuts(outfile
, i
? "," : " ");
4841 print_ippserver_string(outfile
, s
, strlen(s
));
4845 case IPP_TAG_BEGIN_COLLECTION
:
4846 for (i
= 0; i
< attr
->num_values
; i
++)
4848 ipp_t
*col
= ippGetCollection(attr
, i
);
4850 cupsFilePuts(outfile
, i
? ",{\n" : " {\n");
4851 for (colattr
= ippFirstAttribute(col
); colattr
; colattr
= ippNextAttribute(col
))
4852 print_ippserver_attr(outfile
, colattr
, indent
+ 4);
4853 cupsFilePrintf(outfile
, "%*s}", indent
, "");
4858 cupsFilePuts(outfile
, " \"\"");
4862 cupsFilePuts(outfile
, "\n");
4867 * 'print_ippserver_string()' - Print a string suitable for use by ippserver.
4871 print_ippserver_string(
4872 cups_file_t
*outfile
, /* I - Output file */
4873 const char *s
, /* I - String to print */
4874 size_t len
) /* I - Length of string */
4876 cupsFilePutChar(outfile
, '\"');
4880 cupsFilePutChar(outfile
, '\\');
4881 cupsFilePutChar(outfile
, *s
);
4886 cupsFilePutChar(outfile
, '\"');
4891 * 'print_line()' - Print a line of formatted or CSV text.
4896 cups_file_t
*outfile
, /* I - Output file */
4897 ipp_attribute_t
*attr
, /* I - First attribute for line */
4898 int num_displayed
, /* I - Number of attributes to display */
4899 char **displayed
, /* I - Attributes to display */
4900 size_t *widths
) /* I - Column widths */
4902 int i
; /* Looping var */
4903 size_t maxlength
; /* Max length of all columns */
4904 char *buffer
; /* String buffer */
4905 ipp_attribute_t
*current
; /* Current attribute */
4909 * Get the maximum string length we have to show and allocate...
4912 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4913 if (widths
[i
] > maxlength
)
4914 maxlength
= widths
[i
];
4918 if ((buffer
= malloc(maxlength
)) == NULL
)
4922 * Loop through the attributes to display...
4927 for (i
= 0; i
< num_displayed
; i
++)
4930 cupsFilePutChar(cupsFileStdout(), ' ');
4934 for (current
= attr
; current
; current
= current
->next
)
4938 else if (!strcmp(current
->name
, displayed
[i
]))
4940 ippAttributeString(current
, buffer
, maxlength
);
4945 cupsFilePrintf(outfile
, "%*s", (int)-widths
[i
], buffer
);
4947 cupsFilePutChar(cupsFileStdout(), '\n');
4951 for (i
= 0; i
< num_displayed
; i
++)
4954 cupsFilePutChar(cupsFileStdout(), ' ');
4956 cupsFilePrintf(outfile
, "%*s", (int)-widths
[i
], displayed
[i
]);
4958 cupsFilePutChar(cupsFileStdout(), '\n');
4960 for (i
= 0; i
< num_displayed
; i
++)
4963 cupsFilePutChar(cupsFileStdout(), ' ');
4965 memset(buffer
, '-', widths
[i
]);
4966 buffer
[widths
[i
]] = '\0';
4967 cupsFilePuts(outfile
, buffer
);
4969 cupsFilePutChar(cupsFileStdout(), '\n');
4977 * 'print_xml_header()' - Print a standard XML plist header.
4981 print_xml_header(cups_file_t
*outfile
) /* I - Output file */
4985 cupsFilePuts(outfile
, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
4986 cupsFilePuts(outfile
, "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
4987 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n");
4988 cupsFilePuts(outfile
, "<plist version=\"1.0\">\n");
4989 cupsFilePuts(outfile
, "<dict>\n");
4990 cupsFilePuts(outfile
, "<key>ipptoolVersion</key>\n");
4991 cupsFilePuts(outfile
, "<string>" CUPS_SVERSION
"</string>\n");
4992 cupsFilePuts(outfile
, "<key>Transfer</key>\n");
4993 cupsFilePrintf(outfile
, "<string>%s</string>\n",
4994 Transfer
== _CUPS_TRANSFER_AUTO
? "auto" :
4995 Transfer
== _CUPS_TRANSFER_CHUNKED
? "chunked" : "length");
4996 cupsFilePuts(outfile
, "<key>Tests</key>\n");
4997 cupsFilePuts(outfile
, "<array>\n");
5005 * 'print_xml_string()' - Print an XML string with escaping.
5009 print_xml_string(cups_file_t
*outfile
, /* I - Output file */
5010 const char *element
, /* I - Element name or NULL */
5011 const char *s
) /* I - String to print */
5014 cupsFilePrintf(outfile
, "<%s>", element
);
5019 cupsFilePuts(outfile
, "&");
5021 cupsFilePuts(outfile
, "<");
5023 cupsFilePuts(outfile
, ">");
5024 else if ((*s
& 0xe0) == 0xc0)
5027 * Validate UTF-8 two-byte sequence...
5030 if ((s
[1] & 0xc0) != 0x80)
5032 cupsFilePutChar(outfile
, '?');
5037 cupsFilePutChar(outfile
, *s
++);
5038 cupsFilePutChar(outfile
, *s
);
5041 else if ((*s
& 0xf0) == 0xe0)
5044 * Validate UTF-8 three-byte sequence...
5047 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80)
5049 cupsFilePutChar(outfile
, '?');
5054 cupsFilePutChar(outfile
, *s
++);
5055 cupsFilePutChar(outfile
, *s
++);
5056 cupsFilePutChar(outfile
, *s
);
5059 else if ((*s
& 0xf8) == 0xf0)
5062 * Validate UTF-8 four-byte sequence...
5065 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80 ||
5066 (s
[3] & 0xc0) != 0x80)
5068 cupsFilePutChar(outfile
, '?');
5073 cupsFilePutChar(outfile
, *s
++);
5074 cupsFilePutChar(outfile
, *s
++);
5075 cupsFilePutChar(outfile
, *s
++);
5076 cupsFilePutChar(outfile
, *s
);
5079 else if ((*s
& 0x80) || (*s
< ' ' && !isspace(*s
& 255)))
5082 * Invalid control character...
5085 cupsFilePutChar(outfile
, '?');
5088 cupsFilePutChar(outfile
, *s
);
5094 cupsFilePrintf(outfile
, "</%s>\n", element
);
5099 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
5103 print_xml_trailer(cups_file_t
*outfile
, /* I - Output file */
5104 int success
, /* I - 1 on success, 0 on failure */
5105 const char *message
) /* I - Error message or NULL */
5109 cupsFilePuts(outfile
, "</array>\n");
5110 cupsFilePuts(outfile
, "<key>Successful</key>\n");
5111 cupsFilePuts(outfile
, success
? "<true />\n" : "<false />\n");
5114 cupsFilePuts(outfile
, "<key>ErrorMessage</key>\n");
5115 print_xml_string(outfile
, "string", message
);
5117 cupsFilePuts(outfile
, "</dict>\n");
5118 cupsFilePuts(outfile
, "</plist>\n");
5126 * 'set_variable()' - Set a variable value.
5130 set_variable(cups_file_t
*outfile
, /* I - Output file */
5131 _cups_vars_t
*vars
, /* I - Variables */
5132 const char *name
, /* I - Variable name */
5133 const char *value
) /* I - Value string */
5135 _cups_var_t key
, /* Search key */
5136 *var
; /* New variable */
5139 if (!_cups_strcasecmp(name
, "filename"))
5142 free(vars
->filename
);
5144 vars
->filename
= strdup(value
);
5148 key
.name
= (char *)name
;
5149 if ((var
= cupsArrayFind(vars
->vars
, &key
)) != NULL
)
5152 var
->value
= strdup(value
);
5154 else if ((var
= malloc(sizeof(_cups_var_t
))) == NULL
)
5156 print_fatal_error(outfile
, "Unable to allocate memory for variable \"%s\".", name
);
5161 var
->name
= strdup(name
);
5162 var
->value
= strdup(value
);
5164 cupsArrayAdd(vars
->vars
, var
);
5171 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
5175 sigterm_handler(int sig
) /* I - Signal number (unused) */
5181 signal(SIGINT
, SIG_DFL
);
5182 signal(SIGTERM
, SIG_DFL
);
5188 * 'timeout_cb()' - Handle HTTP timeouts.
5191 static int /* O - 1 to continue, 0 to cancel */
5192 timeout_cb(http_t
*http
, /* I - Connection to server */
5193 void *user_data
) /* I - User data (unused) */
5195 int buffered
= 0; /* Bytes buffered but not yet sent */
5201 * If the socket still have data waiting to be sent to the printer (as can
5202 * happen if the printer runs out of paper), continue to wait until the output
5203 * buffer is empty...
5206 #ifdef SO_NWRITE /* macOS and some versions of Linux */
5207 socklen_t len
= sizeof(buffered
); /* Size of return value */
5209 if (getsockopt(httpGetFd(http
), SOL_SOCKET
, SO_NWRITE
, &buffered
, &len
))
5212 #elif defined(SIOCOUTQ) /* Others except Windows */
5213 if (ioctl(httpGetFd(http
), SIOCOUTQ
, &buffered
))
5216 #else /* Windows (not possible) */
5218 #endif /* SO_NWRITE */
5220 return (buffered
> 0);
5225 * 'usage()' - Show program usage.
5231 _cupsLangPuts(stderr
, _("Usage: ipptool [options] URI filename [ ... filenameN ]"));
5232 _cupsLangPuts(stderr
, _("Options:"));
5233 _cupsLangPuts(stderr
, _(" --help Show help."));
5234 _cupsLangPuts(stderr
, _(" --ippserver filename Produce ippserver attribute file."));
5235 _cupsLangPuts(stderr
, _(" --stop-after-include-error\n"
5236 " Stop tests after a failed INCLUDE."));
5237 _cupsLangPuts(stderr
, _(" --version Show version."));
5238 _cupsLangPuts(stderr
, _(" -4 Connect using IPv4."));
5239 _cupsLangPuts(stderr
, _(" -6 Connect using IPv6."));
5240 _cupsLangPuts(stderr
, _(" -C Send requests using "
5241 "chunking (default)."));
5242 _cupsLangPuts(stderr
, _(" -E Test with encryption using HTTP Upgrade to TLS."));
5243 _cupsLangPuts(stderr
, _(" -I Ignore errors."));
5244 _cupsLangPuts(stderr
, _(" -L Send requests using content-length."));
5245 _cupsLangPuts(stderr
, _(" -P filename.plist Produce XML plist to a file and test report to standard output."));
5246 _cupsLangPuts(stderr
, _(" -S Test with encryption using HTTPS."));
5247 _cupsLangPuts(stderr
, _(" -T seconds Set the receive/send timeout in seconds."));
5248 _cupsLangPuts(stderr
, _(" -V version Set default IPP version."));
5249 _cupsLangPuts(stderr
, _(" -X Produce XML plist instead of plain text."));
5250 _cupsLangPuts(stderr
, _(" -c Produce CSV output."));
5251 _cupsLangPuts(stderr
, _(" -d name=value Set named variable to value."));
5252 _cupsLangPuts(stderr
, _(" -f filename Set default request filename."));
5253 _cupsLangPuts(stderr
, _(" -h Validate HTTP response headers."));
5254 _cupsLangPuts(stderr
, _(" -i seconds Repeat the last file with the given time interval."));
5255 _cupsLangPuts(stderr
, _(" -l Produce plain text output."));
5256 _cupsLangPuts(stderr
, _(" -n count Repeat the last file the given number of times."));
5257 _cupsLangPuts(stderr
, _(" -q Run silently."));
5258 _cupsLangPuts(stderr
, _(" -t Produce a test report."));
5259 _cupsLangPuts(stderr
, _(" -v Be verbose."));
5266 * 'validate_attr()' - Determine whether an attribute is valid.
5269 static int /* O - 1 if valid, 0 otherwise */
5270 validate_attr(cups_file_t
*outfile
, /* I - Output file */
5271 cups_array_t
*errors
, /* I - Errors array */
5272 ipp_attribute_t
*attr
) /* I - Attribute to validate */
5274 int i
; /* Looping var */
5275 char scheme
[64], /* Scheme from URI */
5276 userpass
[256], /* Username/password from URI */
5277 hostname
[256], /* Hostname from URI */
5278 resource
[1024]; /* Resource from URI */
5279 int port
, /* Port number from URI */
5280 uri_status
, /* URI separation status */
5281 valid
= 1; /* Is the attribute valid? */
5282 const char *ptr
; /* Pointer into string */
5283 ipp_attribute_t
*colattr
; /* Collection attribute */
5284 regex_t re
; /* Regular expression */
5285 ipp_uchar_t
*date
; /* Current date value */
5296 * Validate the attribute name.
5299 for (ptr
= attr
->name
; *ptr
; ptr
++)
5300 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' && *ptr
!= '_')
5303 if (*ptr
|| ptr
== attr
->name
)
5308 "\"%s\": Bad attribute name - invalid character "
5309 "(RFC 2911 section 4.1.3).", attr
->name
);
5312 if ((ptr
- attr
->name
) > 255)
5317 "\"%s\": Bad attribute name - bad length "
5318 "(RFC 2911 section 4.1.3).", attr
->name
);
5321 switch (attr
->value_tag
)
5323 case IPP_TAG_INTEGER
:
5326 case IPP_TAG_BOOLEAN
:
5327 for (i
= 0; i
< attr
->num_values
; i
++)
5329 if (attr
->values
[i
].boolean
!= 0 &&
5330 attr
->values
[i
].boolean
!= 1)
5335 "\"%s\": Bad boolen value %d "
5336 "(RFC 2911 section 4.1.11).", attr
->name
,
5337 attr
->values
[i
].boolean
);
5343 for (i
= 0; i
< attr
->num_values
; i
++)
5345 if (attr
->values
[i
].integer
< 1)
5350 "\"%s\": Bad enum value %d - out of range "
5351 "(RFC 2911 section 4.1.4).", attr
->name
,
5352 attr
->values
[i
].integer
);
5357 case IPP_TAG_STRING
:
5358 for (i
= 0; i
< attr
->num_values
; i
++)
5360 if (attr
->values
[i
].unknown
.length
> IPP_MAX_OCTETSTRING
)
5365 "\"%s\": Bad octetString value - bad length %d "
5366 "(RFC 2911 section 4.1.10).", attr
->name
,
5367 attr
->values
[i
].unknown
.length
);
5373 for (i
= 0; i
< attr
->num_values
; i
++)
5375 date
= attr
->values
[i
].date
;
5377 if (date
[2] < 1 || date
[2] > 12)
5382 "\"%s\": Bad dateTime month %u "
5383 "(RFC 2911 section 4.1.14).", attr
->name
, date
[2]);
5386 if (date
[3] < 1 || date
[3] > 31)
5391 "\"%s\": Bad dateTime day %u "
5392 "(RFC 2911 section 4.1.14).", attr
->name
, date
[3]);
5400 "\"%s\": Bad dateTime hours %u "
5401 "(RFC 2911 section 4.1.14).", attr
->name
, date
[4]);
5409 "\"%s\": Bad dateTime minutes %u "
5410 "(RFC 2911 section 4.1.14).", attr
->name
, date
[5]);
5418 "\"%s\": Bad dateTime seconds %u "
5419 "(RFC 2911 section 4.1.14).", attr
->name
, date
[6]);
5427 "\"%s\": Bad dateTime deciseconds %u "
5428 "(RFC 2911 section 4.1.14).", attr
->name
, date
[7]);
5431 if (date
[8] != '-' && date
[8] != '+')
5436 "\"%s\": Bad dateTime UTC sign '%c' "
5437 "(RFC 2911 section 4.1.14).", attr
->name
, date
[8]);
5445 "\"%s\": Bad dateTime UTC hours %u "
5446 "(RFC 2911 section 4.1.14).", attr
->name
, date
[9]);
5454 "\"%s\": Bad dateTime UTC minutes %u "
5455 "(RFC 2911 section 4.1.14).", attr
->name
, date
[10]);
5460 case IPP_TAG_RESOLUTION
:
5461 for (i
= 0; i
< attr
->num_values
; i
++)
5463 if (attr
->values
[i
].resolution
.xres
<= 0)
5468 "\"%s\": Bad resolution value %dx%d%s - cross "
5469 "feed resolution must be positive "
5470 "(RFC 2911 section 4.1.15).", attr
->name
,
5471 attr
->values
[i
].resolution
.xres
,
5472 attr
->values
[i
].resolution
.yres
,
5473 attr
->values
[i
].resolution
.units
==
5474 IPP_RES_PER_INCH
? "dpi" :
5475 attr
->values
[i
].resolution
.units
==
5476 IPP_RES_PER_CM
? "dpcm" : "unknown");
5479 if (attr
->values
[i
].resolution
.yres
<= 0)
5484 "\"%s\": Bad resolution value %dx%d%s - feed "
5485 "resolution must be positive "
5486 "(RFC 2911 section 4.1.15).", attr
->name
,
5487 attr
->values
[i
].resolution
.xres
,
5488 attr
->values
[i
].resolution
.yres
,
5489 attr
->values
[i
].resolution
.units
==
5490 IPP_RES_PER_INCH
? "dpi" :
5491 attr
->values
[i
].resolution
.units
==
5492 IPP_RES_PER_CM
? "dpcm" : "unknown");
5495 if (attr
->values
[i
].resolution
.units
!= IPP_RES_PER_INCH
&&
5496 attr
->values
[i
].resolution
.units
!= IPP_RES_PER_CM
)
5501 "\"%s\": Bad resolution value %dx%d%s - bad "
5502 "units value (RFC 2911 section 4.1.15).",
5503 attr
->name
, attr
->values
[i
].resolution
.xres
,
5504 attr
->values
[i
].resolution
.yres
,
5505 attr
->values
[i
].resolution
.units
==
5506 IPP_RES_PER_INCH
? "dpi" :
5507 attr
->values
[i
].resolution
.units
==
5508 IPP_RES_PER_CM
? "dpcm" : "unknown");
5513 case IPP_TAG_RANGE
:
5514 for (i
= 0; i
< attr
->num_values
; i
++)
5516 if (attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
5521 "\"%s\": Bad rangeOfInteger value %d-%d - lower "
5522 "greater than upper (RFC 2911 section 4.1.13).",
5523 attr
->name
, attr
->values
[i
].range
.lower
,
5524 attr
->values
[i
].range
.upper
);
5529 case IPP_TAG_BEGIN_COLLECTION
:
5530 for (i
= 0; i
< attr
->num_values
; i
++)
5532 for (colattr
= attr
->values
[i
].collection
->attrs
;
5534 colattr
= colattr
->next
)
5536 if (!validate_attr(outfile
, NULL
, colattr
))
5543 if (colattr
&& errors
)
5545 add_stringf(errors
, "\"%s\": Bad collection value.", attr
->name
);
5549 validate_attr(outfile
, errors
, colattr
);
5550 colattr
= colattr
->next
;
5557 case IPP_TAG_TEXTLANG
:
5558 for (i
= 0; i
< attr
->num_values
; i
++)
5560 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5562 if ((*ptr
& 0xe0) == 0xc0)
5565 if ((*ptr
& 0xc0) != 0x80)
5568 else if ((*ptr
& 0xf0) == 0xe0)
5571 if ((*ptr
& 0xc0) != 0x80)
5574 if ((*ptr
& 0xc0) != 0x80)
5577 else if ((*ptr
& 0xf8) == 0xf0)
5580 if ((*ptr
& 0xc0) != 0x80)
5583 if ((*ptr
& 0xc0) != 0x80)
5586 if ((*ptr
& 0xc0) != 0x80)
5589 else if (*ptr
& 0x80)
5598 "\"%s\": Bad text value \"%s\" - bad UTF-8 "
5599 "sequence (RFC 2911 section 4.1.1).", attr
->name
,
5600 attr
->values
[i
].string
.text
);
5603 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_TEXT
- 1))
5608 "\"%s\": Bad text value \"%s\" - bad length %d "
5609 "(RFC 2911 section 4.1.1).", attr
->name
,
5610 attr
->values
[i
].string
.text
,
5611 (int)strlen(attr
->values
[i
].string
.text
));
5617 case IPP_TAG_NAMELANG
:
5618 for (i
= 0; i
< attr
->num_values
; i
++)
5620 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5622 if ((*ptr
& 0xe0) == 0xc0)
5625 if ((*ptr
& 0xc0) != 0x80)
5628 else if ((*ptr
& 0xf0) == 0xe0)
5631 if ((*ptr
& 0xc0) != 0x80)
5634 if ((*ptr
& 0xc0) != 0x80)
5637 else if ((*ptr
& 0xf8) == 0xf0)
5640 if ((*ptr
& 0xc0) != 0x80)
5643 if ((*ptr
& 0xc0) != 0x80)
5646 if ((*ptr
& 0xc0) != 0x80)
5649 else if (*ptr
& 0x80)
5658 "\"%s\": Bad name value \"%s\" - bad UTF-8 "
5659 "sequence (RFC 2911 section 4.1.2).", attr
->name
,
5660 attr
->values
[i
].string
.text
);
5663 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_NAME
- 1))
5668 "\"%s\": Bad name value \"%s\" - bad length %d "
5669 "(RFC 2911 section 4.1.2).", attr
->name
,
5670 attr
->values
[i
].string
.text
,
5671 (int)strlen(attr
->values
[i
].string
.text
));
5676 case IPP_TAG_KEYWORD
:
5677 for (i
= 0; i
< attr
->num_values
; i
++)
5679 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5680 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' &&
5684 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5689 "\"%s\": Bad keyword value \"%s\" - invalid "
5690 "character (RFC 2911 section 4.1.3).",
5691 attr
->name
, attr
->values
[i
].string
.text
);
5694 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_KEYWORD
- 1))
5699 "\"%s\": Bad keyword value \"%s\" - bad "
5700 "length %d (RFC 2911 section 4.1.3).",
5701 attr
->name
, attr
->values
[i
].string
.text
,
5702 (int)strlen(attr
->values
[i
].string
.text
));
5708 for (i
= 0; i
< attr
->num_values
; i
++)
5710 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
,
5711 attr
->values
[i
].string
.text
,
5712 scheme
, sizeof(scheme
),
5713 userpass
, sizeof(userpass
),
5714 hostname
, sizeof(hostname
),
5715 &port
, resource
, sizeof(resource
));
5717 if (uri_status
< HTTP_URI_OK
)
5722 "\"%s\": Bad URI value \"%s\" - %s "
5723 "(RFC 2911 section 4.1.5).", attr
->name
,
5724 attr
->values
[i
].string
.text
,
5725 httpURIStatusString(uri_status
));
5728 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_URI
- 1))
5733 "\"%s\": Bad URI value \"%s\" - bad length %d "
5734 "(RFC 2911 section 4.1.5).", attr
->name
,
5735 attr
->values
[i
].string
.text
,
5736 (int)strlen(attr
->values
[i
].string
.text
));
5741 case IPP_TAG_URISCHEME
:
5742 for (i
= 0; i
< attr
->num_values
; i
++)
5744 ptr
= attr
->values
[i
].string
.text
;
5745 if (islower(*ptr
& 255))
5747 for (ptr
++; *ptr
; ptr
++)
5748 if (!islower(*ptr
& 255) && !isdigit(*ptr
& 255) &&
5749 *ptr
!= '+' && *ptr
!= '-' && *ptr
!= '.')
5753 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5758 "\"%s\": Bad uriScheme value \"%s\" - bad "
5759 "characters (RFC 2911 section 4.1.6).",
5760 attr
->name
, attr
->values
[i
].string
.text
);
5763 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_URISCHEME
- 1))
5768 "\"%s\": Bad uriScheme value \"%s\" - bad "
5769 "length %d (RFC 2911 section 4.1.6).",
5770 attr
->name
, attr
->values
[i
].string
.text
,
5771 (int)strlen(attr
->values
[i
].string
.text
));
5776 case IPP_TAG_CHARSET
:
5777 for (i
= 0; i
< attr
->num_values
; i
++)
5779 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5780 if (!isprint(*ptr
& 255) || isupper(*ptr
& 255) ||
5781 isspace(*ptr
& 255))
5784 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5789 "\"%s\": Bad charset value \"%s\" - bad "
5790 "characters (RFC 2911 section 4.1.7).",
5791 attr
->name
, attr
->values
[i
].string
.text
);
5794 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_CHARSET
- 1))
5799 "\"%s\": Bad charset value \"%s\" - bad "
5800 "length %d (RFC 2911 section 4.1.7).",
5801 attr
->name
, attr
->values
[i
].string
.text
,
5802 (int)strlen(attr
->values
[i
].string
.text
));
5807 case IPP_TAG_LANGUAGE
:
5809 * The following regular expression is derived from the ABNF for
5810 * language tags in RFC 4646. All I can say is that this is the
5811 * easiest way to check the values...
5814 if ((i
= regcomp(&re
,
5816 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
5818 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
5819 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
5820 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
5821 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
5822 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
5824 "x(-[a-z0-9]{1,8})+" /* privateuse */
5826 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
5828 REG_NOSUB
| REG_EXTENDED
)) != 0)
5830 char temp
[256]; /* Temporary error string */
5832 regerror(i
, &re
, temp
, sizeof(temp
));
5833 print_fatal_error(outfile
, "Unable to compile naturalLanguage regular "
5834 "expression: %s.", temp
);
5838 for (i
= 0; i
< attr
->num_values
; i
++)
5840 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5845 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5846 "characters (RFC 2911 section 4.1.8).",
5847 attr
->name
, attr
->values
[i
].string
.text
);
5850 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_LANGUAGE
- 1))
5855 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5856 "length %d (RFC 2911 section 4.1.8).",
5857 attr
->name
, attr
->values
[i
].string
.text
,
5858 (int)strlen(attr
->values
[i
].string
.text
));
5865 case IPP_TAG_MIMETYPE
:
5867 * The following regular expression is derived from the ABNF for
5868 * language tags in RFC 2045 and 4288. All I can say is that this is
5869 * the easiest way to check the values...
5872 if ((i
= regcomp(&re
,
5874 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
5876 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
5877 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
5878 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
5881 REG_NOSUB
| REG_EXTENDED
)) != 0)
5883 char temp
[256]; /* Temporary error string */
5885 regerror(i
, &re
, temp
, sizeof(temp
));
5886 print_fatal_error(outfile
, "Unable to compile mimeMediaType regular "
5887 "expression: %s.", temp
);
5891 for (i
= 0; i
< attr
->num_values
; i
++)
5893 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5898 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5899 "characters (RFC 2911 section 4.1.9).",
5900 attr
->name
, attr
->values
[i
].string
.text
);
5903 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_MIMETYPE
- 1))
5908 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5909 "length %d (RFC 2911 section 4.1.9).",
5910 attr
->name
, attr
->values
[i
].string
.text
,
5911 (int)strlen(attr
->values
[i
].string
.text
));
5927 * 'with_flags_string()' - Return the "WITH-xxx" predicate that corresponds to
5931 static const char * /* O - WITH-xxx string */
5932 with_flags_string(int flags
) /* I - WITH flags */
5934 if (flags
& _CUPS_WITH_ALL
)
5936 if (flags
& _CUPS_WITH_HOSTNAME
)
5937 return ("WITH-ALL-HOSTNAMES");
5938 else if (flags
& _CUPS_WITH_RESOURCE
)
5939 return ("WITH-ALL-RESOURCES");
5940 else if (flags
& _CUPS_WITH_SCHEME
)
5941 return ("WITH-ALL-SCHEMES");
5943 return ("WITH-ALL-VALUES");
5945 else if (flags
& _CUPS_WITH_HOSTNAME
)
5946 return ("WITH-HOSTNAME");
5947 else if (flags
& _CUPS_WITH_RESOURCE
)
5948 return ("WITH-RESOURCE");
5949 else if (flags
& _CUPS_WITH_SCHEME
)
5950 return ("WITH-SCHEME");
5952 return ("WITH-VALUE");
5957 * 'with_value()' - Test a WITH-VALUE predicate.
5960 static int /* O - 1 on match, 0 on non-match */
5961 with_value(cups_file_t
*outfile
, /* I - Output file */
5962 cups_array_t
*errors
, /* I - Errors array */
5963 char *value
, /* I - Value string */
5964 int flags
, /* I - Flags for match */
5965 ipp_attribute_t
*attr
, /* I - Attribute to compare */
5966 char *matchbuf
, /* I - Buffer to hold matching value */
5967 size_t matchlen
) /* I - Length of match buffer */
5969 int i
, /* Looping var */
5971 char temp
[1024], /* Temporary value string */
5972 *valptr
; /* Pointer into value */
5976 match
= (flags
& _CUPS_WITH_ALL
) ? 1 : 0;
5979 * NULL matches everything.
5982 if (!value
|| !*value
)
5986 * Compare the value string to the attribute value.
5989 switch (attr
->value_tag
)
5991 case IPP_TAG_INTEGER
:
5993 for (i
= 0; i
< attr
->num_values
; i
++)
5995 char op
, /* Comparison operator */
5996 *nextptr
; /* Next pointer */
5997 int intvalue
, /* Integer value */
5998 valmatch
= 0; /* Does the current value match? */
6002 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
6003 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
6004 *valptr
== '=' || *valptr
== '>')
6007 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
6009 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
6017 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
6018 if (nextptr
== valptr
)
6022 if ((op
== '=' && attr
->values
[i
].integer
== intvalue
) ||
6023 (op
== '<' && attr
->values
[i
].integer
< intvalue
) ||
6024 (op
== '>' && attr
->values
[i
].integer
> intvalue
))
6027 snprintf(matchbuf
, matchlen
, "%d", attr
->values
[i
].integer
);
6034 if (flags
& _CUPS_WITH_ALL
)
6049 if (!match
&& errors
)
6051 for (i
= 0; i
< attr
->num_values
; i
++)
6052 add_stringf(errors
, "GOT: %s=%d", attr
->name
,
6053 attr
->values
[i
].integer
);
6057 case IPP_TAG_RANGE
:
6058 for (i
= 0; i
< attr
->num_values
; i
++)
6060 char op
, /* Comparison operator */
6061 *nextptr
; /* Next pointer */
6062 int intvalue
, /* Integer value */
6063 valmatch
= 0; /* Does the current value match? */
6067 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
6068 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
6069 *valptr
== '=' || *valptr
== '>')
6072 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
6074 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
6082 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
6083 if (nextptr
== valptr
)
6087 if ((op
== '=' && (attr
->values
[i
].range
.lower
== intvalue
||
6088 attr
->values
[i
].range
.upper
== intvalue
)) ||
6089 (op
== '<' && attr
->values
[i
].range
.upper
< intvalue
) ||
6090 (op
== '>' && attr
->values
[i
].range
.upper
> intvalue
))
6093 snprintf(matchbuf
, matchlen
, "%d-%d",
6094 attr
->values
[0].range
.lower
,
6095 attr
->values
[0].range
.upper
);
6102 if (flags
& _CUPS_WITH_ALL
)
6117 if (!match
&& errors
)
6119 for (i
= 0; i
< attr
->num_values
; i
++)
6120 add_stringf(errors
, "GOT: %s=%d-%d", attr
->name
,
6121 attr
->values
[i
].range
.lower
,
6122 attr
->values
[i
].range
.upper
);
6126 case IPP_TAG_BOOLEAN
:
6127 for (i
= 0; i
< attr
->num_values
; i
++)
6129 if ((!strcmp(value
, "true")) == attr
->values
[i
].boolean
)
6132 strlcpy(matchbuf
, value
, matchlen
);
6134 if (!(flags
& _CUPS_WITH_ALL
))
6140 else if (flags
& _CUPS_WITH_ALL
)
6147 if (!match
&& errors
)
6149 for (i
= 0; i
< attr
->num_values
; i
++)
6150 add_stringf(errors
, "GOT: %s=%s", attr
->name
,
6151 attr
->values
[i
].boolean
? "true" : "false");
6155 case IPP_TAG_RESOLUTION
:
6156 for (i
= 0; i
< attr
->num_values
; i
++)
6158 if (attr
->values
[i
].resolution
.xres
==
6159 attr
->values
[i
].resolution
.yres
)
6160 snprintf(temp
, sizeof(temp
), "%d%s",
6161 attr
->values
[i
].resolution
.xres
,
6162 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
6165 snprintf(temp
, sizeof(temp
), "%dx%d%s",
6166 attr
->values
[i
].resolution
.xres
,
6167 attr
->values
[i
].resolution
.yres
,
6168 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
6171 if (!strcmp(value
, temp
))
6174 strlcpy(matchbuf
, value
, matchlen
);
6176 if (!(flags
& _CUPS_WITH_ALL
))
6182 else if (flags
& _CUPS_WITH_ALL
)
6189 if (!match
&& errors
)
6191 for (i
= 0; i
< attr
->num_values
; i
++)
6193 if (attr
->values
[i
].resolution
.xres
==
6194 attr
->values
[i
].resolution
.yres
)
6195 snprintf(temp
, sizeof(temp
), "%d%s",
6196 attr
->values
[i
].resolution
.xres
,
6197 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
6200 snprintf(temp
, sizeof(temp
), "%dx%d%s",
6201 attr
->values
[i
].resolution
.xres
,
6202 attr
->values
[i
].resolution
.yres
,
6203 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
6206 if (strcmp(value
, temp
))
6207 add_stringf(errors
, "GOT: %s=%s", attr
->name
, temp
);
6212 case IPP_TAG_NOVALUE
:
6213 case IPP_TAG_UNKNOWN
:
6216 case IPP_TAG_CHARSET
:
6217 case IPP_TAG_KEYWORD
:
6218 case IPP_TAG_LANGUAGE
:
6219 case IPP_TAG_MIMETYPE
:
6221 case IPP_TAG_NAMELANG
:
6223 case IPP_TAG_TEXTLANG
:
6225 case IPP_TAG_URISCHEME
:
6226 if (flags
& _CUPS_WITH_REGEX
)
6229 * Value is an extended, case-sensitive POSIX regular expression...
6232 regex_t re
; /* Regular expression */
6234 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
6236 regerror(i
, &re
, temp
, sizeof(temp
));
6238 print_fatal_error(outfile
, "Unable to compile WITH-VALUE regular expression "
6239 "\"%s\" - %s", value
, temp
);
6244 * See if ALL of the values match the given regular expression.
6247 for (i
= 0; i
< attr
->num_values
; i
++)
6249 if (!regexec(&re
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
6254 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
6257 if (!(flags
& _CUPS_WITH_ALL
))
6263 else if (flags
& _CUPS_WITH_ALL
)
6272 else if (ippGetValueTag(attr
) == IPP_TAG_URI
&& !(flags
& (_CUPS_WITH_SCHEME
| _CUPS_WITH_HOSTNAME
| _CUPS_WITH_RESOURCE
)))
6275 * Value is a literal URI string, see if the value(s) match...
6278 for (i
= 0; i
< attr
->num_values
; i
++)
6280 if (!compare_uris(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
))))
6284 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
6287 if (!(flags
& _CUPS_WITH_ALL
))
6293 else if (flags
& _CUPS_WITH_ALL
)
6303 * Value is a literal string, see if the value(s) match...
6306 for (i
= 0; i
< attr
->num_values
; i
++)
6310 switch (ippGetValueTag(attr
))
6314 * Some URI components are case-sensitive, some not...
6317 if (flags
& (_CUPS_WITH_SCHEME
| _CUPS_WITH_HOSTNAME
))
6318 result
= _cups_strcasecmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
6320 result
= strcmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
6323 case IPP_TAG_MIMETYPE
:
6325 case IPP_TAG_NAMELANG
:
6327 case IPP_TAG_TEXTLANG
:
6329 * mimeMediaType, nameWithoutLanguage, nameWithLanguage,
6330 * textWithoutLanguage, and textWithLanguage are defined to
6331 * be case-insensitive strings...
6334 result
= _cups_strcasecmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
6339 * Other string syntaxes are defined as lowercased so we use
6340 * case-sensitive comparisons to catch problems...
6343 result
= strcmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
6351 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
6354 if (!(flags
& _CUPS_WITH_ALL
))
6360 else if (flags
& _CUPS_WITH_ALL
)
6368 if (!match
&& errors
)
6370 for (i
= 0; i
< attr
->num_values
; i
++)
6371 add_stringf(errors
, "GOT: %s=\"%s\"", attr
->name
,
6372 attr
->values
[i
].string
.text
);
6385 * 'with_value_from()' - Test a WITH-VALUE-FROM predicate.
6388 static int /* O - 1 on match, 0 on non-match */
6390 cups_array_t
*errors
, /* I - Errors array */
6391 ipp_attribute_t
*fromattr
, /* I - "From" attribute */
6392 ipp_attribute_t
*attr
, /* I - Attribute to compare */
6393 char *matchbuf
, /* I - Buffer to hold matching value */
6394 size_t matchlen
) /* I - Length of match buffer */
6396 int i
, j
, /* Looping vars */
6397 count
= ippGetCount(attr
), /* Number of attribute values */
6398 match
= 1; /* Match? */
6404 * Compare the from value(s) to the attribute value(s)...
6407 switch (ippGetValueTag(attr
))
6409 case IPP_TAG_INTEGER
:
6410 if (ippGetValueTag(fromattr
) != IPP_TAG_INTEGER
&& ippGetValueTag(fromattr
) != IPP_TAG_RANGE
)
6411 goto wrong_value_tag
;
6413 for (i
= 0; i
< count
; i
++)
6415 int value
= ippGetInteger(attr
, i
);
6416 /* Current integer value */
6418 if (ippContainsInteger(fromattr
, value
))
6421 snprintf(matchbuf
, matchlen
, "%d", value
);
6425 add_stringf(errors
, "GOT: %s=%d", ippGetName(attr
), value
);
6432 if (ippGetValueTag(fromattr
) != IPP_TAG_ENUM
)
6433 goto wrong_value_tag
;
6435 for (i
= 0; i
< count
; i
++)
6437 int value
= ippGetInteger(attr
, i
);
6438 /* Current integer value */
6440 if (ippContainsInteger(fromattr
, value
))
6443 snprintf(matchbuf
, matchlen
, "%d", value
);
6447 add_stringf(errors
, "GOT: %s=%d", ippGetName(attr
), value
);
6453 case IPP_TAG_RESOLUTION
:
6454 if (ippGetValueTag(fromattr
) != IPP_TAG_RESOLUTION
)
6455 goto wrong_value_tag
;
6457 for (i
= 0; i
< count
; i
++)
6461 int fromcount
= ippGetCount(fromattr
);
6462 int fromxres
, fromyres
;
6463 ipp_res_t fromunits
;
6465 xres
= ippGetResolution(attr
, i
, &yres
, &units
);
6467 for (j
= 0; j
< fromcount
; j
++)
6469 fromxres
= ippGetResolution(fromattr
, j
, &fromyres
, &fromunits
);
6470 if (fromxres
== xres
&& fromyres
== yres
&& fromunits
== units
)
6479 snprintf(matchbuf
, matchlen
, "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6481 snprintf(matchbuf
, matchlen
, "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6487 add_stringf(errors
, "GOT: %s=%d%s", ippGetName(attr
), xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6489 add_stringf(errors
, "GOT: %s=%dx%d%s", ippGetName(attr
), xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
6496 case IPP_TAG_NOVALUE
:
6497 case IPP_TAG_UNKNOWN
:
6500 case IPP_TAG_CHARSET
:
6501 case IPP_TAG_KEYWORD
:
6502 case IPP_TAG_LANGUAGE
:
6503 case IPP_TAG_MIMETYPE
:
6505 case IPP_TAG_NAMELANG
:
6507 case IPP_TAG_TEXTLANG
:
6508 case IPP_TAG_URISCHEME
:
6509 for (i
= 0; i
< count
; i
++)
6511 const char *value
= ippGetString(attr
, i
, NULL
);
6512 /* Current string value */
6514 if (ippContainsString(fromattr
, value
))
6517 strlcpy(matchbuf
, value
, matchlen
);
6521 add_stringf(errors
, "GOT: %s='%s'", ippGetName(attr
), value
);
6528 for (i
= 0; i
< count
; i
++)
6530 const char *value
= ippGetString(attr
, i
, NULL
);
6531 /* Current string value */
6532 int fromcount
= ippGetCount(fromattr
);
6534 for (j
= 0; j
< fromcount
; j
++)
6536 if (!compare_uris(value
, ippGetString(fromattr
, j
, NULL
)))
6539 strlcpy(matchbuf
, value
, matchlen
);
6546 add_stringf(errors
, "GOT: %s='%s'", ippGetName(attr
), value
);
6559 /* value tag mismatch between fromattr and attr */
6562 add_stringf(errors
, "GOT: %s OF-TYPE %s", ippGetName(attr
), ippTagString(ippGetValueTag(attr
)));