4 * ipptool command for CUPS.
6 * Copyright 2007-2011 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
15 * This file is subject to the Apple OS-Developed Software exception.
19 * main() - Parse options and do tests.
20 * compare_vars() - Compare two variables.
21 * do_tests() - Do tests as specified in the test file.
22 * expand_variables() - Expand variables in a string.
23 * expect_matches() - Return true if the tag matches the specification.
24 * get_collection() - Get a collection value from the current test file.
25 * get_filename() - Get a filename based on the current test file.
26 * get_token() - Get a token from a file.
27 * get_variable() - Get the value of a variable.
28 * iso_date() - Return an ISO 8601 date/time string for the given IPP
30 * password_cb() - Password callback for authenticated tests.
31 * print_attr() - Print an attribute on the screen.
32 * print_col() - Print a collection attribute on the screen.
33 * print_csv() - Print a line of CSV text.
34 * print_fatal_error() - Print a fatal error message.
35 * print_line() - Print a line of formatted or CSV text.
36 * print_test_error() - Print a test error message.
37 * print_xml_header() - Print a standard XML plist header.
38 * print_xml_string() - Print an XML string with escaping.
39 * print_xml_trailer() - Print the XML trailer with success/fail value.
40 * set_variable() - Set a variable value.
41 * timeout_cb() - Handle HTTP timeouts.
42 * usage() - Show program usage.
43 * validate_attr() - Determine whether an attribute is valid.
44 * with_value() - Test a WITH-VALUE predicate.
48 * Include necessary headers...
51 #include <cups/cups-private.h>
52 #include <cups/file-private.h>
57 #endif /* !O_BINARY */
64 typedef enum _cups_transfer_e
/**** How to send request data ****/
66 _CUPS_TRANSFER_AUTO
, /* Chunk for files, length for static */
67 _CUPS_TRANSFER_CHUNKED
, /* Chunk always */
68 _CUPS_TRANSFER_LENGTH
/* Length always */
71 typedef enum _cups_output_e
/**** Output mode ****/
73 _CUPS_OUTPUT_QUIET
, /* No output */
74 _CUPS_OUTPUT_TEST
, /* Traditional CUPS test output */
75 _CUPS_OUTPUT_PLIST
, /* XML plist test output */
76 _CUPS_OUTPUT_LIST
, /* Tabular list output */
77 _CUPS_OUTPUT_CSV
/* Comma-separated values output */
80 typedef struct _cups_expect_s
/**** Expected attribute info ****/
82 int optional
, /* Optional attribute? */
83 not_expect
; /* Don't expect attribute? */
84 char *name
, /* Attribute name */
85 *of_type
, /* Type name */
86 *same_count_as
, /* Parallel attribute name */
87 *if_defined
, /* Only required if variable defined */
88 *if_not_defined
, /* Only required if variable is not defined */
89 *with_value
, /* Attribute must include this value */
90 *define_match
, /* Variable to define on match */
91 *define_no_match
, /* Variable to define on no-match */
92 *define_value
; /* Variable to define with value */
93 int with_regex
, /* WITH-VALUE is a regular expression */
94 count
; /* Expected count if > 0 */
95 ipp_tag_t in_group
; /* IN-GROUP value */
98 typedef struct _cups_status_s
/**** Status info ****/
100 ipp_status_t status
; /* Expected status code */
101 char *if_defined
, /* Only if variable is defined */
102 *if_not_defined
; /* Only if variable is not defined */
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 const char *uri
, /* URI for printer */
114 *filename
; /* Filename */
115 char 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 _cups_transfer_t Transfer
= _CUPS_TRANSFER_AUTO
;
132 /* How to transfer requests */
133 _cups_output_t Output
= _CUPS_OUTPUT_LIST
;
135 int IgnoreErrors
= 0, /* Ignore errors? */
136 Verbosity
= 0, /* Show all attributes? */
137 Version
= 11, /* Default IPP version */
138 XMLHeader
= 0; /* 1 if header is written */
139 char *Password
= NULL
; /* Password from URI */
140 const char * const URIStatusStrings
[] = /* URI status strings */
143 "Bad arguments to function",
144 "Bad resource in URI",
145 "Bad port number in URI",
146 "Bad hostname/address in URI",
147 "Bad username in URI",
151 "Missing scheme in URI",
152 "Unknown scheme in URI",
153 "Missing resource in URI"
161 static int compare_vars(_cups_var_t
*a
, _cups_var_t
*b
);
162 static int do_tests(_cups_vars_t
*vars
, const char *testfile
);
163 static void expand_variables(_cups_vars_t
*vars
, char *dst
, const char *src
,
166 __attribute((nonnull(1,2,3)))
167 #endif /* __GNUC__ */
169 static int expect_matches(_cups_expect_t
*expect
, ipp_tag_t value_tag
);
170 static ipp_t
*get_collection(_cups_vars_t
*vars
, FILE *fp
, int *linenum
);
171 static char *get_filename(const char *testfile
, char *dst
, const char *src
,
173 static char *get_token(FILE *fp
, char *buf
, int buflen
,
175 static char *get_variable(_cups_vars_t
*vars
, const char *name
);
176 static char *iso_date(ipp_uchar_t
*date
);
177 static const char *password_cb(const char *prompt
);
178 static void print_attr(ipp_attribute_t
*attr
, ipp_tag_t
*group
);
179 static void print_col(ipp_t
*col
);
180 static void print_csv(ipp_attribute_t
*attr
, int num_displayed
,
181 char **displayed
, size_t *widths
);
182 static void print_fatal_error(const char *s
, ...)
184 __attribute__ ((__format__ (__printf__
, 1, 2)))
185 #endif /* __GNUC__ */
187 static void print_line(ipp_attribute_t
*attr
, int num_displayed
,
188 char **displayed
, size_t *widths
);
189 static void print_test_error(const char *s
, ...)
191 __attribute__ ((__format__ (__printf__
, 1, 2)))
192 #endif /* __GNUC__ */
194 static void print_xml_header(void);
195 static void print_xml_string(const char *element
, const char *s
);
196 static void print_xml_trailer(int success
, const char *message
);
197 static void set_variable(_cups_vars_t
*vars
, const char *name
,
199 static int timeout_cb(http_t
*http
, void *user_data
);
200 static void usage(void);
201 static int validate_attr(ipp_attribute_t
*attr
, int print
);
202 static int with_value(char *value
, int regex
, ipp_attribute_t
*attr
,
207 * 'main()' - Parse options and do tests.
210 int /* O - Exit status */
211 main(int argc
, /* I - Number of command-line args */
212 char *argv
[]) /* I - Command-line arguments */
214 int i
; /* Looping var */
215 int status
; /* Status of tests... */
216 char *opt
, /* Current option */
217 name
[1024], /* Name/value buffer */
218 *value
, /* Pointer to value */
219 filename
[1024], /* Real filename */
220 testname
[1024]; /* Real test filename */
221 const char *testfile
; /* Test file to use */
222 int interval
, /* Test interval in microseconds */
223 repeat
; /* Repeat count */
224 _cups_vars_t vars
; /* Variables */
225 http_uri_status_t uri_status
; /* URI separation status */
226 _cups_globals_t
*cg
= _cupsGlobals();
232 * Initialize the locale and variables...
235 _cupsSetLocale(argv
);
237 memset(&vars
, 0, sizeof(vars
));
238 vars
.family
= AF_UNSPEC
;
239 vars
.vars
= cupsArrayNew((cups_array_func_t
)compare_vars
, NULL
);
244 * ipptool URI testfile
252 for (i
= 1; i
< argc
; i
++)
254 if (argv
[i
][0] == '-')
256 for (opt
= argv
[i
] + 1; *opt
; opt
++)
260 case '4' : /* Connect using IPv4 only */
261 vars
.family
= AF_INET
;
265 case '6' : /* Connect using IPv6 only */
266 vars
.family
= AF_INET6
;
268 #endif /* AF_INET6 */
270 case 'C' : /* Enable HTTP chunking */
271 Transfer
= _CUPS_TRANSFER_CHUNKED
;
274 case 'E' : /* Encrypt with TLS */
276 vars
.encryption
= HTTP_ENCRYPT_REQUIRED
;
278 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
280 #endif /* HAVE_SSL */
283 case 'I' : /* Ignore errors */
287 case 'L' : /* Disable HTTP chunking */
288 Transfer
= _CUPS_TRANSFER_LENGTH
;
291 case 'S' : /* Encrypt with SSL */
293 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
295 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
297 #endif /* HAVE_SSL */
300 case 'T' : /* Set timeout */
305 _cupsLangPuts(stderr
,
306 _("ipptool: Missing timeout for \"-T\"."));
310 vars
.timeout
= _cupsStrScand(argv
[i
], NULL
, localeconv());
313 case 'V' : /* Set IPP version */
318 _cupsLangPuts(stderr
,
319 _("ipptool: Missing version for \"-V\"."));
323 if (!strcmp(argv
[i
], "1.0"))
325 else if (!strcmp(argv
[i
], "1.1"))
327 else if (!strcmp(argv
[i
], "2.0"))
329 else if (!strcmp(argv
[i
], "2.1"))
331 else if (!strcmp(argv
[i
], "2.2"))
335 _cupsLangPrintf(stderr
,
336 _("ipptool: Bad version %s for \"-V\"."),
342 case 'X' : /* Produce XML output */
343 Output
= _CUPS_OUTPUT_PLIST
;
345 if (interval
|| repeat
)
347 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are "
348 "incompatible with -X\"."));
353 case 'c' : /* CSV output */
354 Output
= _CUPS_OUTPUT_CSV
;
357 case 'd' : /* Define a variable */
362 _cupsLangPuts(stderr
,
363 _("ipptool: Missing name=value for \"-d\"."));
367 strlcpy(name
, argv
[i
], sizeof(name
));
368 if ((value
= strchr(name
, '=')) != NULL
)
371 value
= name
+ strlen(name
);
373 set_variable(&vars
, name
, value
);
376 case 'f' : /* Set the default test filename */
381 _cupsLangPuts(stderr
,
382 _("ipptool: Missing filename for \"-f\"."));
386 if (access(argv
[i
], 0) && argv
[i
][0] != '/')
388 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s",
389 cg
->cups_datadir
, argv
[i
]);
390 if (access(argv
[i
], 0))
391 vars
.filename
= argv
[i
];
393 vars
.filename
= filename
;
396 vars
.filename
= argv
[i
];
399 case 'i' : /* Test every N seconds */
404 _cupsLangPuts(stderr
,
405 _("ipptool: Missing seconds for \"-i\"."));
410 interval
= (int)(_cupsStrScand(argv
[i
], NULL
, localeconv()) *
414 _cupsLangPuts(stderr
,
415 _("ipptool: Invalid seconds for \"-i\"."));
420 if (Output
== _CUPS_OUTPUT_PLIST
&& interval
)
422 _cupsLangPuts(stderr
, _("ipptool: \"-i\" is incompatible with "
428 case 'l' : /* List as a table */
429 Output
= _CUPS_OUTPUT_LIST
;
432 case 'n' : /* Repeat count */
437 _cupsLangPuts(stderr
,
438 _("ipptool: Missing count for \"-n\"."));
442 repeat
= atoi(argv
[i
]);
444 if (Output
== _CUPS_OUTPUT_PLIST
&& repeat
)
446 _cupsLangPuts(stderr
, _("ipptool: \"-n\" is incompatible with "
452 case 'q' : /* Be quiet */
453 Output
= _CUPS_OUTPUT_QUIET
;
456 case 't' : /* CUPS test output */
457 Output
= _CUPS_OUTPUT_TEST
;
460 case 'v' : /* Be verbose */
465 _cupsLangPrintf(stderr
, _("ipptool: Unknown option \"-%c\"."),
472 else if (!strncmp(argv
[i
], "ipp://", 6) || !strncmp(argv
[i
], "http://", 7)
474 || !strncmp(argv
[i
], "ipps://", 7)
475 || !strncmp(argv
[i
], "https://", 8)
476 #endif /* HAVE_SSL */
485 _cupsLangPuts(stderr
, _("ipptool: May only specify a single URI."));
490 if (!strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8))
491 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
492 #endif /* HAVE_SSL */
495 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, vars
.uri
,
496 vars
.scheme
, sizeof(vars
.scheme
),
497 vars
.userpass
, sizeof(vars
.userpass
),
498 vars
.hostname
, sizeof(vars
.hostname
),
500 vars
.resource
, sizeof(vars
.resource
));
502 if (uri_status
!= HTTP_URI_OK
)
504 _cupsLangPrintf(stderr
, _("ipptool: Bad URI - %s."),
505 URIStatusStrings
[uri_status
- HTTP_URI_OVERFLOW
]);
509 if (vars
.userpass
[0])
511 if ((Password
= strchr(vars
.userpass
, ':')) != NULL
)
514 cupsSetUser(vars
.userpass
);
515 cupsSetPasswordCB(password_cb
);
516 set_variable(&vars
, "uriuser", vars
.userpass
);
527 _cupsLangPuts(stderr
, _("ipptool: URI required before test file."));
531 if (access(argv
[i
], 0) && argv
[i
][0] != '/')
533 snprintf(testname
, sizeof(testname
), "%s/ipptool/%s", cg
->cups_datadir
,
535 if (access(testname
, 0))
543 if (!do_tests(&vars
, testfile
))
548 if (!vars
.uri
|| !testfile
)
552 * Loop if the interval is set...
555 if (Output
== _CUPS_OUTPUT_PLIST
)
556 print_xml_trailer(!status
, NULL
);
557 else if (interval
> 0 && repeat
> 0)
562 do_tests(&vars
, testfile
);
566 else if (interval
> 0)
571 do_tests(&vars
, testfile
);
584 * 'compare_vars()' - Compare two variables.
587 static int /* O - Result of comparison */
588 compare_vars(_cups_var_t
*a
, /* I - First variable */
589 _cups_var_t
*b
) /* I - Second variable */
591 return (strcasecmp(a
->name
, b
->name
));
596 * 'do_tests()' - Do tests as specified in the test file.
599 static int /* 1 = success, 0 = failure */
600 do_tests(_cups_vars_t
*vars
, /* I - Variables */
601 const char *testfile
) /* I - Test file to use */
603 int i
, /* Looping var */
604 linenum
, /* Current line number */
605 pass
, /* Did we pass the test? */
606 prev_pass
= 1, /* Did we pass the previous test? */
607 request_id
, /* Current request ID */
608 show_header
= 1, /* Show the test header? */
609 ignore_errors
, /* Ignore test failures? */
610 skip_previous
= 0; /* Skip on previous test failure? */
611 http_t
*http
= NULL
; /* HTTP connection to server */
612 FILE *fp
= NULL
; /* Test file */
613 char resource
[512], /* Resource for request */
614 token
[1024], /* Token from file */
615 *tokenptr
, /* Pointer into token */
616 temp
[1024]; /* Temporary string */
617 ipp_t
*request
= NULL
; /* IPP request */
618 ipp_t
*response
= NULL
; /* IPP response */
619 char attr
[128]; /* Attribute name */
620 ipp_op_t op
; /* Operation */
621 ipp_tag_t group
; /* Current group */
622 ipp_tag_t value
; /* Current value type */
623 ipp_attribute_t
*attrptr
, /* Attribute pointer */
624 *found
, /* Found attribute */
625 *lastcol
= NULL
; /* Last collection attribute */
626 char name
[1024]; /* Name of test */
627 char filename
[1024]; /* Filename */
628 _cups_transfer_t transfer
; /* To chunk or not to chunk */
629 int version
, /* IPP version number to use */
630 skip_test
; /* Skip this test? */
631 int num_statuses
= 0; /* Number of valid status codes */
632 _cups_status_t statuses
[100], /* Valid status codes */
633 *last_status
; /* Last STATUS (for predicates) */
634 int num_expects
= 0; /* Number of expected attributes */
635 _cups_expect_t expects
[200], /* Expected attributes */
636 *expect
, /* Current expected attribute */
637 *last_expect
; /* Last EXPECT (for predicates) */
638 int num_displayed
= 0; /* Number of displayed attributes */
639 char *displayed
[200]; /* Displayed attributes */
640 size_t widths
[200]; /* Width of columns */
644 * Open the test file...
647 if ((fp
= fopen(testfile
, "r")) == NULL
)
649 print_fatal_error("Unable to open test file %s - %s", testfile
,
656 * Connect to the server...
659 if ((http
= _httpCreate(vars
->hostname
, vars
->port
, NULL
, vars
->encryption
,
660 vars
->family
)) == NULL
)
662 print_fatal_error("Unable to connect to %s on port %d - %s", vars
->hostname
,
663 vars
->port
, strerror(errno
));
668 if (httpReconnect(http
))
670 print_fatal_error("Unable to connect to %s on port %d - %s", vars
->hostname
,
671 vars
->port
, strerror(errno
));
676 if (vars
->timeout
> 0.0)
677 _httpSetTimeout(http
, vars
->timeout
, timeout_cb
, NULL
);
683 CUPS_SRAND(time(NULL
));
687 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
689 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
692 * Expect an open brace...
695 if (!strcmp(token
, "DEFINE"))
701 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
702 get_token(fp
, temp
, sizeof(temp
), &linenum
))
704 expand_variables(vars
, token
, temp
, sizeof(token
));
705 set_variable(vars
, attr
, token
);
709 print_fatal_error("Missing DEFINE name and/or value on line %d.",
717 else if (!strcmp(token
, "DEFINE-DEFAULT"))
720 * DEFINE-DEFAULT name value
723 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
724 get_token(fp
, temp
, sizeof(temp
), &linenum
))
726 expand_variables(vars
, token
, temp
, sizeof(token
));
727 if (!get_variable(vars
, attr
))
728 set_variable(vars
, attr
, token
);
732 print_fatal_error("Missing DEFINE-DEFAULT name and/or value on line "
740 else if (!strcmp(token
, "IGNORE-ERRORS"))
747 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
748 (!strcasecmp(temp
, "yes") || !strcasecmp(temp
, "no")))
750 IgnoreErrors
= !strcasecmp(temp
, "yes");
754 print_fatal_error("Missing IGNORE-ERRORS value on line %d.", linenum
);
761 else if (!strcmp(token
, "INCLUDE"))
768 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
771 * Map the filename to and then run the tests...
774 if (!do_tests(vars
, get_filename(testfile
, filename
, temp
,
785 print_fatal_error("Missing INCLUDE filename on line %d.", linenum
);
793 else if (!strcmp(token
, "INCLUDE-IF-DEFINED"))
796 * INCLUDE-IF-DEFINED name "filename"
797 * INCLUDE-IF-DEFINED name <filename>
800 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
801 get_token(fp
, temp
, sizeof(temp
), &linenum
))
804 * Map the filename to and then run the tests...
807 if (get_variable(vars
, attr
) &&
808 !do_tests(vars
, get_filename(testfile
, filename
, temp
,
819 print_fatal_error("Missing INCLUDE-IF-DEFINED name or filename on line "
828 else if (!strcmp(token
, "INCLUDE-IF-NOT-DEFINED"))
831 * INCLUDE-IF-NOT-DEFINED name "filename"
832 * INCLUDE-IF-NOT-DEFINED name <filename>
835 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
836 get_token(fp
, temp
, sizeof(temp
), &linenum
))
839 * Map the filename to and then run the tests...
842 if (!get_variable(vars
, attr
) &&
843 !do_tests(vars
, get_filename(testfile
, filename
, temp
,
854 print_fatal_error("Missing INCLUDE-IF-NOT-DEFINED name or filename on "
855 "line %d.", linenum
);
863 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
866 * SKIP-IF-DEFINED variable
869 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
871 if (get_variable(vars
, temp
))
876 print_fatal_error("Missing SKIP-IF-DEFINED variable on line %d.",
882 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
885 * SKIP-IF-NOT-DEFINED variable
888 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
890 if (!get_variable(vars
, temp
))
895 print_fatal_error("Missing SKIP-IF-NOT-DEFINED variable on line %d.",
901 else if (!strcmp(token
, "TRANSFER"))
909 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
911 if (!strcmp(temp
, "auto"))
912 Transfer
= _CUPS_TRANSFER_AUTO
;
913 else if (!strcmp(temp
, "chunked"))
914 Transfer
= _CUPS_TRANSFER_CHUNKED
;
915 else if (!strcmp(temp
, "length"))
916 Transfer
= _CUPS_TRANSFER_LENGTH
;
919 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp
,
927 print_fatal_error("Missing TRANSFER value on line %d.", linenum
);
934 else if (!strcmp(token
, "VERSION"))
936 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
938 if (!strcmp(temp
, "1.0"))
940 else if (!strcmp(temp
, "1.1"))
942 else if (!strcmp(temp
, "2.0"))
944 else if (!strcmp(temp
, "2.1"))
946 else if (!strcmp(temp
, "2.2"))
950 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp
, linenum
);
957 print_fatal_error("Missing VERSION number on line %d.", linenum
);
964 else if (strcmp(token
, "{"))
966 print_fatal_error("Unexpected token %s seen on line %d.", token
, linenum
);
972 * Initialize things...
977 if (Output
== _CUPS_OUTPUT_PLIST
)
979 else if (Output
== _CUPS_OUTPUT_TEST
)
980 printf("\"%s\":\n", testfile
);
985 strlcpy(resource
, vars
->resource
, sizeof(resource
));
990 group
= IPP_TAG_ZERO
;
991 ignore_errors
= IgnoreErrors
;
999 strlcpy(name
, testfile
, sizeof(name
));
1000 if (strrchr(name
, '.') != NULL
)
1001 *strrchr(name
, '.') = '\0';
1004 * Parse until we see a close brace...
1007 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
1009 if (strcasecmp(token
, "COUNT") &&
1010 strcasecmp(token
, "DEFINE-MATCH") &&
1011 strcasecmp(token
, "DEFINE-NO-MATCH") &&
1012 strcasecmp(token
, "DEFINE-VALUE") &&
1013 strcasecmp(token
, "IF-DEFINED") &&
1014 strcasecmp(token
, "IF-NOT-DEFINED") &&
1015 strcasecmp(token
, "IN-GROUP") &&
1016 strcasecmp(token
, "OF-TYPE") &&
1017 strcasecmp(token
, "SAME-COUNT-AS") &&
1018 strcasecmp(token
, "WITH-VALUE"))
1021 if (strcasecmp(token
, "IF-DEFINED") &&
1022 strcasecmp(token
, "IF-NOT-DEFINED"))
1025 if (!strcmp(token
, "}"))
1027 else if (!strcmp(token
, "{") && lastcol
)
1030 * Another collection value
1033 ipp_t
*col
= get_collection(vars
, fp
, &linenum
);
1034 /* Collection value */
1038 ipp_attribute_t
*tempcol
; /* Pointer to new buffer */
1042 * Reallocate memory...
1045 if ((tempcol
= realloc(lastcol
, sizeof(ipp_attribute_t
) +
1046 (lastcol
->num_values
+ 1) *
1047 sizeof(ipp_value_t
))) == NULL
)
1049 print_fatal_error("Unable to allocate memory on line %d.", linenum
);
1054 if (tempcol
!= lastcol
)
1057 * Reset pointers in the list...
1061 request
->prev
->next
= tempcol
;
1063 request
->attrs
= tempcol
;
1065 lastcol
= request
->current
= request
->last
= tempcol
;
1068 lastcol
->values
[lastcol
->num_values
].collection
= col
;
1069 lastcol
->num_values
++;
1077 else if (!strcmp(token
, "DEFINE"))
1083 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1084 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1086 expand_variables(vars
, token
, temp
, sizeof(token
));
1087 set_variable(vars
, attr
, token
);
1091 print_fatal_error("Missing DEFINE name and/or value on line %d.",
1097 else if (!strcmp(token
, "IGNORE-ERRORS"))
1104 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1105 (!strcasecmp(temp
, "yes") || !strcasecmp(temp
, "no")))
1107 ignore_errors
= !strcasecmp(temp
, "yes");
1111 print_fatal_error("Missing IGNORE-ERRORS value on line %d.", linenum
);
1118 else if (!strcasecmp(token
, "NAME"))
1124 get_token(fp
, name
, sizeof(name
), &linenum
);
1126 else if (!strcmp(token
, "REQUEST-ID"))
1133 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1135 if (isdigit(temp
[0] & 255))
1136 request_id
= atoi(temp
);
1137 else if (!strcasecmp(temp
, "random"))
1138 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
1141 print_fatal_error("Bad REQUEST-ID value \"%s\" on line %d.", temp
,
1149 print_fatal_error("Missing REQUEST-ID value on line %d.", linenum
);
1154 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1157 * SKIP-IF-DEFINED variable
1160 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1162 if (get_variable(vars
, temp
))
1167 print_fatal_error("Missing SKIP-IF-DEFINED value on line %d.",
1173 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1176 * SKIP-IF-NOT-DEFINED variable
1179 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1181 if (!get_variable(vars
, temp
))
1186 print_fatal_error("Missing SKIP-IF-NOT-DEFINED value on line %d.",
1192 else if (!strcmp(token
, "SKIP-PREVIOUS-ERROR"))
1195 * SKIP-PREVIOUS-ERROR yes
1196 * SKIP-PREVIOUS-ERROR no
1199 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1200 (!strcasecmp(temp
, "yes") || !strcasecmp(temp
, "no")))
1202 skip_previous
= !strcasecmp(temp
, "yes");
1206 print_fatal_error("Missing SKIP-PREVIOUS-ERROR value on line %d.", linenum
);
1213 else if (!strcmp(token
, "TRANSFER"))
1221 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1223 if (!strcmp(temp
, "auto"))
1224 transfer
= _CUPS_TRANSFER_AUTO
;
1225 else if (!strcmp(temp
, "chunked"))
1226 transfer
= _CUPS_TRANSFER_CHUNKED
;
1227 else if (!strcmp(temp
, "length"))
1228 transfer
= _CUPS_TRANSFER_LENGTH
;
1231 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp
,
1239 print_fatal_error("Missing TRANSFER value on line %d.", linenum
);
1244 else if (!strcasecmp(token
, "VERSION"))
1246 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1248 if (!strcmp(temp
, "0.0"))
1250 else if (!strcmp(temp
, "1.0"))
1252 else if (!strcmp(temp
, "1.1"))
1254 else if (!strcmp(temp
, "2.0"))
1256 else if (!strcmp(temp
, "2.1"))
1258 else if (!strcmp(temp
, "2.2"))
1262 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1269 print_fatal_error("Missing VERSION number on line %d.", linenum
);
1274 else if (!strcasecmp(token
, "RESOURCE"))
1280 if (!get_token(fp
, resource
, sizeof(resource
), &linenum
))
1282 print_fatal_error("Missing RESOURCE path on line %d.", linenum
);
1287 else if (!strcasecmp(token
, "OPERATION"))
1293 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1295 print_fatal_error("Missing OPERATION code on line %d.", linenum
);
1300 if ((op
= ippOpValue(token
)) == (ipp_op_t
)-1 &&
1301 (op
= strtol(token
, NULL
, 0)) == 0)
1303 print_fatal_error("Bad OPERATION code \"%s\" on line %d.", token
,
1309 else if (!strcasecmp(token
, "GROUP"))
1312 * Attribute group...
1315 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1317 print_fatal_error("Missing GROUP tag on line %d.", linenum
);
1322 if ((value
= ippTagValue(token
)) < 0)
1324 print_fatal_error("Bad GROUP tag \"%s\" on line %d.", token
, linenum
);
1330 ippAddSeparator(request
);
1334 else if (!strcasecmp(token
, "DELAY"))
1337 * Delay before operation...
1342 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1344 print_fatal_error("Missing DELAY value on line %d.", linenum
);
1349 if ((delay
= _cupsStrScand(token
, NULL
, localeconv())) <= 0.0)
1351 print_fatal_error("Bad DELAY value \"%s\" on line %d.", token
,
1358 if (Output
== _CUPS_OUTPUT_TEST
)
1359 printf(" [%g second delay]\n", delay
);
1361 usleep((int)(1000000.0 * delay
));
1364 else if (!strcasecmp(token
, "ATTR"))
1370 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1372 print_fatal_error("Missing ATTR value tag on line %d.", linenum
);
1377 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
1379 print_fatal_error("Bad ATTR value tag \"%s\" on line %d.", token
,
1385 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
1387 print_fatal_error("Missing ATTR name on line %d.", linenum
);
1392 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1394 print_fatal_error("Missing ATTR value on line %d.", linenum
);
1399 expand_variables(vars
, token
, temp
, sizeof(token
));
1403 case IPP_TAG_BOOLEAN
:
1404 if (!strcasecmp(token
, "true"))
1405 ippAddBoolean(request
, group
, attr
, 1);
1407 ippAddBoolean(request
, group
, attr
, atoi(token
));
1410 case IPP_TAG_INTEGER
:
1412 if (!strchr(token
, ','))
1413 ippAddInteger(request
, group
, value
, attr
,
1414 strtol(token
, &tokenptr
, 0));
1417 int values
[100], /* Values */
1418 num_values
= 1; /* Number of values */
1420 values
[0] = strtol(token
, &tokenptr
, 10);
1421 while (tokenptr
&& *tokenptr
&&
1422 num_values
< (int)(sizeof(values
) / sizeof(values
[0])))
1424 if (*tokenptr
== ',')
1426 else if (!isdigit(*tokenptr
& 255) && *tokenptr
!= '-')
1429 values
[num_values
] = strtol(tokenptr
, &tokenptr
, 0);
1433 ippAddIntegers(request
, group
, value
, attr
, num_values
, values
);
1436 if (!tokenptr
|| *tokenptr
)
1438 print_fatal_error("Bad %s value \"%s\" on line %d.",
1439 ippTagString(value
), token
, linenum
);
1445 case IPP_TAG_RESOLUTION
:
1447 int xres
, /* X resolution */
1448 yres
; /* Y resolution */
1449 char *ptr
; /* Pointer into value */
1451 xres
= yres
= strtol(token
, (char **)&ptr
, 10);
1452 if (ptr
> token
&& xres
> 0)
1455 yres
= strtol(ptr
+ 1, (char **)&ptr
, 10);
1458 if (ptr
<= token
|| xres
<= 0 || yres
<= 0 || !ptr
||
1459 (strcasecmp(ptr
, "dpi") && strcasecmp(ptr
, "dpc") &&
1460 strcasecmp(ptr
, "other")))
1462 print_fatal_error("Bad resolution value \"%s\" on line %d.",
1468 if (!strcasecmp(ptr
, "dpi"))
1469 ippAddResolution(request
, group
, attr
, IPP_RES_PER_INCH
,
1471 else if (!strcasecmp(ptr
, "dpc"))
1472 ippAddResolution(request
, group
, attr
, IPP_RES_PER_CM
,
1475 ippAddResolution(request
, group
, attr
, (ipp_res_t
)0,
1480 case IPP_TAG_RANGE
:
1482 int lowers
[4], /* Lower value */
1483 uppers
[4], /* Upper values */
1484 num_vals
; /* Number of values */
1487 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
1488 lowers
+ 0, uppers
+ 0,
1489 lowers
+ 1, uppers
+ 1,
1490 lowers
+ 2, uppers
+ 2,
1491 lowers
+ 3, uppers
+ 3);
1493 if ((num_vals
& 1) || num_vals
== 0)
1495 print_fatal_error("Bad rangeOfInteger value \"%s\" on line "
1496 "%d.", token
, linenum
);
1501 ippAddRanges(request
, group
, attr
, num_vals
/ 2, lowers
,
1506 case IPP_TAG_BEGIN_COLLECTION
:
1507 if (!strcmp(token
, "{"))
1509 ipp_t
*col
= get_collection(vars
, fp
, &linenum
);
1510 /* Collection value */
1514 lastcol
= ippAddCollection(request
, group
, attr
, col
);
1525 print_fatal_error("Bad ATTR collection value on line %d.",
1533 print_fatal_error("Unsupported ATTR value tag %s on line %d.",
1534 ippTagString(value
), linenum
);
1538 case IPP_TAG_TEXTLANG
:
1539 case IPP_TAG_NAMELANG
:
1542 case IPP_TAG_KEYWORD
:
1544 case IPP_TAG_URISCHEME
:
1545 case IPP_TAG_CHARSET
:
1546 case IPP_TAG_LANGUAGE
:
1547 case IPP_TAG_MIMETYPE
:
1548 if (!strchr(token
, ','))
1549 ippAddString(request
, group
, value
, attr
, NULL
, token
);
1553 * Multiple string values...
1556 int num_values
; /* Number of values */
1557 char *values
[100], /* Values */
1558 *ptr
; /* Pointer to next value */
1564 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
1567 values
[num_values
] = ptr
;
1571 ippAddStrings(request
, group
, value
, attr
, num_values
,
1572 NULL
, (const char **)values
);
1577 else if (!strcasecmp(token
, "FILE"))
1583 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1585 print_fatal_error("Missing FILE filename on line %d.", linenum
);
1590 expand_variables(vars
, token
, temp
, sizeof(token
));
1591 get_filename(testfile
, filename
, token
, sizeof(filename
));
1593 else if (!strcasecmp(token
, "STATUS"))
1599 if (num_statuses
>= (int)(sizeof(statuses
) / sizeof(statuses
[0])))
1601 print_fatal_error("Too many STATUS's on line %d.", linenum
);
1606 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1608 print_fatal_error("Missing STATUS code on line %d.", linenum
);
1613 if ((statuses
[num_statuses
].status
= ippErrorValue(token
))
1614 == (ipp_status_t
)-1 &&
1615 (statuses
[num_statuses
].status
= strtol(token
, NULL
, 0)) == 0)
1617 print_fatal_error("Bad STATUS code \"%s\" on line %d.", token
,
1623 last_status
= statuses
+ num_statuses
;
1626 last_status
->if_defined
= NULL
;
1627 last_status
->if_not_defined
= NULL
;
1629 else if (!strcasecmp(token
, "EXPECT"))
1632 * Expected attributes...
1635 if (num_expects
>= (int)(sizeof(expects
) / sizeof(expects
[0])))
1637 print_fatal_error("Too many EXPECT's on line %d.", linenum
);
1642 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1644 print_fatal_error("Missing EXPECT name on line %d.", linenum
);
1649 last_expect
= expects
+ num_expects
;
1652 memset(last_expect
, 0, sizeof(_cups_expect_t
));
1654 if (token
[0] == '!')
1656 last_expect
->not_expect
= 1;
1657 last_expect
->name
= strdup(token
+ 1);
1659 else if (token
[0] == '?')
1661 last_expect
->optional
= 1;
1662 last_expect
->name
= strdup(token
+ 1);
1665 last_expect
->name
= strdup(token
);
1667 else if (!strcasecmp(token
, "COUNT"))
1669 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1671 print_fatal_error("Missing COUNT number on line %d.", linenum
);
1676 if ((i
= atoi(token
)) <= 0)
1678 print_fatal_error("Bad COUNT \"%s\" on line %d.", token
, linenum
);
1684 last_expect
->count
= i
;
1687 print_fatal_error("COUNT without a preceding EXPECT on line %d.",
1693 else if (!strcasecmp(token
, "DEFINE-MATCH"))
1695 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1697 print_fatal_error("Missing DEFINE-MATCH variable on line %d.",
1704 last_expect
->define_match
= strdup(token
);
1707 print_fatal_error("DEFINE-MATCH without a preceding EXPECT on line "
1713 else if (!strcasecmp(token
, "DEFINE-NO-MATCH"))
1715 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1717 print_fatal_error("Missing DEFINE-NO-MATCH variable on line %d.",
1724 last_expect
->define_no_match
= strdup(token
);
1727 print_fatal_error("DEFINE-NO-MATCH without a preceding EXPECT on "
1728 "line %d.", linenum
);
1733 else if (!strcasecmp(token
, "DEFINE-VALUE"))
1735 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1737 print_fatal_error("Missing DEFINE-VALUE variable on line %d.",
1744 last_expect
->define_value
= strdup(token
);
1747 print_fatal_error("DEFINE-VALUE without a preceding EXPECT on line "
1753 else if (!strcasecmp(token
, "OF-TYPE"))
1755 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1757 print_fatal_error("Missing OF-TYPE value tag(s) on line %d.",
1764 last_expect
->of_type
= strdup(token
);
1767 print_fatal_error("OF-TYPE without a preceding EXPECT on line %d.",
1773 else if (!strcasecmp(token
, "IN-GROUP"))
1775 ipp_tag_t in_group
; /* IN-GROUP value */
1778 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1780 print_fatal_error("Missing IN-GROUP group tag on line %d.", linenum
);
1785 if ((in_group
= ippTagValue(token
)) == (ipp_tag_t
)-1)
1788 else if (last_expect
)
1789 last_expect
->in_group
= in_group
;
1792 print_fatal_error("IN-GROUP without a preceding EXPECT on line %d.",
1798 else if (!strcasecmp(token
, "SAME-COUNT-AS"))
1800 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1802 print_fatal_error("Missing SAME-COUNT-AS name on line %d.", linenum
);
1808 last_expect
->same_count_as
= strdup(token
);
1811 print_fatal_error("SAME-COUNT-AS without a preceding EXPECT on line "
1817 else if (!strcasecmp(token
, "IF-DEFINED"))
1819 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1821 print_fatal_error("Missing IF-DEFINED name on line %d.", linenum
);
1827 last_expect
->if_defined
= strdup(token
);
1828 else if (last_status
)
1829 last_status
->if_defined
= strdup(token
);
1832 print_fatal_error("IF-DEFINED without a preceding EXPECT or STATUS "
1833 "on line %d.", linenum
);
1838 else if (!strcasecmp(token
, "IF-NOT-DEFINED"))
1840 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1842 print_fatal_error("Missing IF-NOT-DEFINED name on line %d.", linenum
);
1848 last_expect
->if_not_defined
= strdup(token
);
1849 else if (last_status
)
1850 last_status
->if_not_defined
= strdup(token
);
1853 print_fatal_error("IF-NOT-DEFINED without a preceding EXPECT or STATUS "
1854 "on line %d.", linenum
);
1859 else if (!strcasecmp(token
, "WITH-VALUE"))
1861 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1863 print_fatal_error("Missing WITH-VALUE value on line %d.", linenum
);
1871 * Expand any variables in the value and then save it.
1874 expand_variables(vars
, token
, temp
, sizeof(token
));
1876 tokenptr
= token
+ strlen(token
) - 1;
1878 if (token
[0] == '/' && tokenptr
> token
&& *tokenptr
== '/')
1881 * WITH-VALUE is a POSIX extended regular expression.
1884 last_expect
->with_value
= calloc(1, tokenptr
- token
);
1885 last_expect
->with_regex
= 1;
1887 if (last_expect
->with_value
)
1888 memcpy(last_expect
->with_value
, token
+ 1, tokenptr
- token
- 1);
1893 * WITH-VALUE is a literal value...
1896 last_expect
->with_value
= strdup(token
);
1901 print_fatal_error("WITH-VALUE without a preceding EXPECT on line %d.",
1907 else if (!strcasecmp(token
, "DISPLAY"))
1910 * Display attributes...
1913 if (num_displayed
>= (int)(sizeof(displayed
) / sizeof(displayed
[0])))
1915 print_fatal_error("Too many DISPLAY's on line %d", linenum
);
1920 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1922 print_fatal_error("Missing DISPLAY name on line %d.", linenum
);
1927 displayed
[num_displayed
] = strdup(token
);
1932 print_fatal_error("Unexpected token %s seen on line %d.", token
,
1940 * Submit the IPP request...
1943 request
->request
.op
.version
[0] = version
/ 10;
1944 request
->request
.op
.version
[1] = version
% 10;
1945 request
->request
.op
.operation_id
= op
;
1946 request
->request
.op
.request_id
= request_id
;
1948 if (Output
== _CUPS_OUTPUT_PLIST
)
1951 puts("<key>Name</key>");
1952 print_xml_string("string", name
);
1953 puts("<key>Operation</key>");
1954 print_xml_string("string", ippOpString(op
));
1955 puts("<key>RequestAttributes</key>");
1960 for (attrptr
= request
->attrs
, group
= attrptr
->group_tag
;
1962 attrptr
= attrptr
->next
)
1963 print_attr(attrptr
, &group
);
1968 else if (Output
== _CUPS_OUTPUT_TEST
)
1972 printf(" %s:\n", ippOpString(op
));
1974 for (attrptr
= request
->attrs
; attrptr
; attrptr
= attrptr
->next
)
1975 print_attr(attrptr
, NULL
);
1978 printf(" %-69.69s [", name
);
1982 if ((skip_previous
&& !prev_pass
) || skip_test
)
1987 if (Output
== _CUPS_OUTPUT_PLIST
)
1989 puts("<key>Successful</key>");
1991 puts("<key>StatusCode</key>");
1992 print_xml_string("string", "skip");
1993 puts("<key>ResponseAttributes</key>");
1997 else if (Output
== _CUPS_OUTPUT_TEST
)
2003 if (transfer
== _CUPS_TRANSFER_CHUNKED
||
2004 (transfer
== _CUPS_TRANSFER_AUTO
&& filename
[0]))
2007 * Send request using chunking...
2010 http_status_t status
= cupsSendRequest(http
, request
, resource
, 0);
2012 if (status
== HTTP_CONTINUE
&& filename
[0])
2014 int fd
; /* File to send */
2015 char buffer
[8192]; /* Copy buffer */
2016 ssize_t bytes
; /* Bytes read/written */
2018 if ((fd
= open(filename
, O_RDONLY
| O_BINARY
)) >= 0)
2020 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
2021 if ((status
= cupsWriteRequestData(http
, buffer
,
2022 bytes
)) != HTTP_CONTINUE
)
2027 snprintf(buffer
, sizeof(buffer
), "%s: %s", filename
, strerror(errno
));
2028 _cupsSetError(IPP_INTERNAL_ERROR
, buffer
, 0);
2030 status
= HTTP_ERROR
;
2036 if (status
== HTTP_CONTINUE
)
2037 response
= cupsGetResponse(http
, resource
);
2041 else if (filename
[0])
2042 response
= cupsDoFileRequest(http
, request
, resource
, filename
);
2044 response
= cupsDoRequest(http
, request
, resource
);
2050 prev_pass
= pass
= 0;
2053 if (http
->version
!= HTTP_1_1
)
2054 prev_pass
= pass
= 0;
2056 if (response
->request
.status
.request_id
!= request_id
)
2057 prev_pass
= pass
= 0;
2060 (response
->request
.status
.version
[0] != (version
/ 10) ||
2061 response
->request
.status
.version
[1] != (version
% 10)))
2062 prev_pass
= pass
= 0;
2064 if ((attrptr
= ippFindAttribute(response
, "job-id",
2065 IPP_TAG_INTEGER
)) != NULL
)
2067 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2068 set_variable(vars
, "job-id", temp
);
2071 if ((attrptr
= ippFindAttribute(response
, "job-uri",
2072 IPP_TAG_URI
)) != NULL
)
2073 set_variable(vars
, "job-uri", attrptr
->values
[0].string
.text
);
2075 if ((attrptr
= ippFindAttribute(response
, "notify-subscription-id",
2076 IPP_TAG_INTEGER
)) != NULL
)
2078 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2079 set_variable(vars
, "notify-subscription-id", temp
);
2082 attrptr
= response
->attrs
;
2083 if (!attrptr
|| !attrptr
->name
||
2084 attrptr
->value_tag
!= IPP_TAG_CHARSET
||
2085 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2086 attrptr
->num_values
!= 1 ||
2087 strcmp(attrptr
->name
, "attributes-charset"))
2088 prev_pass
= pass
= 0;
2092 attrptr
= attrptr
->next
;
2093 if (!attrptr
|| !attrptr
->name
||
2094 attrptr
->value_tag
!= IPP_TAG_LANGUAGE
||
2095 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2096 attrptr
->num_values
!= 1 ||
2097 strcmp(attrptr
->name
, "attributes-natural-language"))
2098 prev_pass
= pass
= 0;
2101 if ((attrptr
= ippFindAttribute(response
, "status-message",
2102 IPP_TAG_ZERO
)) != NULL
&&
2103 (attrptr
->value_tag
!= IPP_TAG_TEXT
||
2104 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2105 attrptr
->num_values
!= 1 ||
2106 (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2107 strlen(attrptr
->values
[0].string
.text
) > 255)))
2108 prev_pass
= pass
= 0;
2110 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
2111 IPP_TAG_ZERO
)) != NULL
&&
2112 (attrptr
->value_tag
!= IPP_TAG_TEXT
||
2113 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2114 attrptr
->num_values
!= 1 ||
2115 (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2116 strlen(attrptr
->values
[0].string
.text
) > 1023)))
2117 prev_pass
= pass
= 0;
2119 for (attrptr
= response
->attrs
, group
= attrptr
->group_tag
;
2121 attrptr
= attrptr
->next
)
2123 if (attrptr
->group_tag
< group
&& attrptr
->group_tag
!= IPP_TAG_ZERO
)
2125 prev_pass
= pass
= 0;
2129 if (!validate_attr(attrptr
, 0))
2131 prev_pass
= pass
= 0;
2136 for (i
= 0; i
< num_statuses
; i
++)
2138 if (statuses
[i
].if_defined
&&
2139 !get_variable(vars
, statuses
[i
].if_defined
))
2142 if (statuses
[i
].if_not_defined
&&
2143 get_variable(vars
, statuses
[i
].if_not_defined
))
2146 if (response
->request
.status
.status_code
== statuses
[i
].status
)
2150 if (i
== num_statuses
&& num_statuses
> 0)
2151 prev_pass
= pass
= 0;
2154 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
2156 if (expect
->if_defined
&& !get_variable(vars
, expect
->if_defined
))
2159 if (expect
->if_not_defined
&&
2160 get_variable(vars
, expect
->if_not_defined
))
2163 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
2165 if ((found
&& expect
->not_expect
) ||
2166 (!found
&& !(expect
->not_expect
|| expect
->optional
)) ||
2167 (found
&& !expect_matches(expect
, found
->value_tag
)) ||
2168 (found
&& expect
->in_group
&&
2169 found
->group_tag
!= expect
->in_group
))
2171 if (expect
->define_no_match
)
2172 set_variable(vars
, expect
->define_no_match
, "1");
2173 else if (!expect
->define_match
)
2174 prev_pass
= pass
= 0;
2180 !with_value(expect
->with_value
, expect
->with_regex
, found
, 0))
2182 if (expect
->define_no_match
)
2183 set_variable(vars
, expect
->define_no_match
, "1");
2184 else if (!expect
->define_match
)
2185 prev_pass
= pass
= 0;
2190 if (found
&& expect
->count
> 0 && found
->num_values
!= expect
->count
)
2192 if (expect
->define_no_match
)
2193 set_variable(vars
, expect
->define_no_match
, "1");
2194 else if (!expect
->define_match
)
2195 prev_pass
= pass
= 0;
2200 if (found
&& expect
->same_count_as
)
2202 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
2205 if (!attrptr
|| attrptr
->num_values
!= found
->num_values
)
2207 if (expect
->define_no_match
)
2208 set_variable(vars
, expect
->define_no_match
, "1");
2209 else if (!expect
->define_match
)
2210 prev_pass
= pass
= 0;
2216 if (found
&& expect
->define_match
)
2217 set_variable(vars
, expect
->define_match
, "1");
2219 if (found
&& expect
->define_value
)
2221 _ippAttrString(found
, token
, sizeof(token
));
2222 set_variable(vars
, expect
->define_value
, token
);
2228 if (Output
== _CUPS_OUTPUT_PLIST
)
2230 puts("<key>Successful</key>");
2231 puts(prev_pass
? "<true />" : "<false />");
2232 puts("<key>StatusCode</key>");
2233 print_xml_string("string", ippErrorString(cupsLastError()));
2234 puts("<key>ResponseAttributes</key>");
2237 for (attrptr
= response
? response
->attrs
: NULL
,
2238 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2240 attrptr
= attrptr
->next
)
2241 print_attr(attrptr
, &group
);
2245 else if (Output
== _CUPS_OUTPUT_TEST
)
2247 puts(prev_pass
? "PASS]" : "FAIL]");
2249 if (Verbosity
&& response
)
2251 printf(" RECEIVED: %lu bytes in response\n",
2252 (unsigned long)ippLength(response
));
2253 printf(" status-code = %x (%s)\n", cupsLastError(),
2254 ippErrorString(cupsLastError()));
2256 for (attrptr
= response
->attrs
;
2258 attrptr
= attrptr
->next
)
2260 print_attr(attrptr
, NULL
);
2264 else if (!prev_pass
)
2265 fprintf(stderr
, "%s\n", cupsLastErrorString());
2267 if (prev_pass
&& Output
!= _CUPS_OUTPUT_PLIST
&&
2268 Output
!= _CUPS_OUTPUT_QUIET
&& !Verbosity
&& num_displayed
> 0)
2270 if (Output
>= _CUPS_OUTPUT_LIST
)
2272 size_t width
; /* Length of value */
2275 for (i
= 0; i
< num_displayed
; i
++)
2277 widths
[i
] = strlen(displayed
[i
]);
2279 for (attrptr
= ippFindAttribute(response
, displayed
[i
], IPP_TAG_ZERO
);
2281 attrptr
= ippFindNextAttribute(response
, displayed
[i
],
2284 width
= _ippAttrString(attrptr
, NULL
, 0);
2285 if (width
> widths
[i
])
2290 if (Output
== _CUPS_OUTPUT_CSV
)
2291 print_csv(NULL
, num_displayed
, displayed
, widths
);
2293 print_line(NULL
, num_displayed
, displayed
, widths
);
2295 attrptr
= response
->attrs
;
2299 while (attrptr
&& attrptr
->group_tag
<= IPP_TAG_OPERATION
)
2300 attrptr
= attrptr
->next
;
2304 if (Output
== _CUPS_OUTPUT_CSV
)
2305 print_csv(attrptr
, num_displayed
, displayed
, widths
);
2307 print_line(attrptr
, num_displayed
, displayed
, widths
);
2309 while (attrptr
&& attrptr
->group_tag
> IPP_TAG_OPERATION
)
2310 attrptr
= attrptr
->next
;
2316 for (attrptr
= response
->attrs
;
2318 attrptr
= attrptr
->next
)
2322 for (i
= 0; i
< num_displayed
; i
++)
2324 if (!strcmp(displayed
[i
], attrptr
->name
))
2326 print_attr(attrptr
, NULL
);
2334 else if (!prev_pass
)
2336 if (Output
== _CUPS_OUTPUT_PLIST
)
2338 puts("<key>Errors</key>");
2342 if (http
->version
!= HTTP_1_1
)
2343 print_test_error("Bad HTTP version (%d.%d)", http
->version
/ 100,
2344 http
->version
% 100);
2347 print_test_error("IPP request failed with status %s (%s)",
2348 ippErrorString(cupsLastError()),
2349 cupsLastErrorString());
2353 (response
->request
.status
.version
[0] != (version
/ 10) ||
2354 response
->request
.status
.version
[1] != (version
% 10)))
2355 print_test_error("Bad version %d.%d in response - expected %d.%d "
2356 "(RFC 2911 section 3.1.8).",
2357 response
->request
.status
.version
[0],
2358 response
->request
.status
.version
[1],
2359 version
/ 10, version
% 10);
2361 if (response
->request
.status
.request_id
!= request_id
)
2362 print_test_error("Bad request ID %d in response - expected %d "
2363 "(RFC 2911 section 3.1.1)",
2364 response
->request
.status
.request_id
, request_id
);
2366 attrptr
= response
->attrs
;
2368 print_test_error("Missing first attribute \"attributes-charset "
2369 "(charset)\" in group operation-attributes-tag "
2370 "(RFC 2911 section 3.1.4).");
2373 if (!attrptr
->name
||
2374 attrptr
->value_tag
!= IPP_TAG_CHARSET
||
2375 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2376 attrptr
->num_values
!= 1 ||
2377 strcmp(attrptr
->name
, "attributes-charset"))
2378 print_test_error("Bad first attribute \"%s (%s%s)\" in group %s, "
2379 "expected \"attributes-charset (charset)\" in "
2380 "group operation-attributes-tag (RFC 2911 section "
2382 attrptr
->name
? attrptr
->name
: "(null)",
2383 attrptr
->num_values
> 1 ? "1setOf " : "",
2384 ippTagString(attrptr
->value_tag
),
2385 ippTagString(attrptr
->group_tag
));
2387 attrptr
= attrptr
->next
;
2389 print_test_error("Missing second attribute \"attributes-natural-"
2390 "language (naturalLanguage)\" in group "
2391 "operation-attributes-tag (RFC 2911 section "
2393 else if (!attrptr
->name
||
2394 attrptr
->value_tag
!= IPP_TAG_LANGUAGE
||
2395 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2396 attrptr
->num_values
!= 1 ||
2397 strcmp(attrptr
->name
, "attributes-natural-language"))
2398 print_test_error("Bad first attribute \"%s (%s%s)\" in group %s, "
2399 "expected \"attributes-natural-language "
2400 "(naturalLanguage)\" in group "
2401 "operation-attributes-tag (RFC 2911 section "
2403 attrptr
->name
? attrptr
->name
: "(null)",
2404 attrptr
->num_values
> 1 ? "1setOf " : "",
2405 ippTagString(attrptr
->value_tag
),
2406 ippTagString(attrptr
->group_tag
));
2409 if ((attrptr
= ippFindAttribute(response
, "status-message",
2410 IPP_TAG_ZERO
)) != NULL
)
2412 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2413 print_test_error("status-message (text(255)) has wrong value tag "
2414 "%s (RFC 2911 section 3.1.6.2).",
2415 ippTagString(attrptr
->value_tag
));
2416 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2417 print_test_error("status-message (text(255)) has wrong group tag "
2418 "%s (RFC 2911 section 3.1.6.2).",
2419 ippTagString(attrptr
->group_tag
));
2420 if (attrptr
->num_values
!= 1)
2421 print_test_error("status-message (text(255)) has %d values "
2422 "(RFC 2911 section 3.1.6.2).",
2423 attrptr
->num_values
);
2424 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2425 strlen(attrptr
->values
[0].string
.text
) > 255)
2426 print_test_error("status-message (text(255)) has bad length %d"
2427 " (RFC 2911 section 3.1.6.2).",
2428 (int)strlen(attrptr
->values
[0].string
.text
));
2431 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
2432 IPP_TAG_ZERO
)) != NULL
)
2434 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2435 print_test_error("detailed-status-message (text(MAX)) has wrong "
2436 "value tag %s (RFC 2911 section 3.1.6.3).",
2437 ippTagString(attrptr
->value_tag
));
2438 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2439 print_test_error("detailed-status-message (text(MAX)) has wrong "
2440 "group tag %s (RFC 2911 section 3.1.6.3).",
2441 ippTagString(attrptr
->group_tag
));
2442 if (attrptr
->num_values
!= 1)
2443 print_test_error("detailed-status-message (text(MAX)) has %d values"
2444 " (RFC 2911 section 3.1.6.3).",
2445 attrptr
->num_values
);
2446 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2447 strlen(attrptr
->values
[0].string
.text
) > 1023)
2448 print_test_error("detailed-status-message (text(MAX)) has bad "
2449 "length %d (RFC 2911 section 3.1.6.3).",
2450 (int)strlen(attrptr
->values
[0].string
.text
));
2453 for (attrptr
= response
->attrs
, group
= attrptr
->group_tag
;
2455 attrptr
= attrptr
->next
)
2457 if (attrptr
->group_tag
< group
&& attrptr
->group_tag
!= IPP_TAG_ZERO
)
2458 print_test_error("Attribute groups out of order (%s < %s)",
2459 ippTagString(attrptr
->group_tag
),
2460 ippTagString(group
));
2462 validate_attr(attrptr
, 1);
2465 for (i
= 0; i
< num_statuses
; i
++)
2467 if (statuses
[i
].if_defined
&&
2468 !get_variable(vars
, statuses
[i
].if_defined
))
2471 if (statuses
[i
].if_not_defined
&&
2472 get_variable(vars
, statuses
[i
].if_not_defined
))
2475 if (response
->request
.status
.status_code
== statuses
[i
].status
)
2479 if (i
== num_statuses
&& num_statuses
> 0)
2481 for (i
= 0; i
< num_statuses
; i
++)
2483 if (statuses
[i
].if_defined
&&
2484 !get_variable(vars
, statuses
[i
].if_defined
))
2487 if (statuses
[i
].if_not_defined
&&
2488 get_variable(vars
, statuses
[i
].if_not_defined
))
2491 print_test_error("EXPECTED: STATUS %s (got %s)",
2492 ippErrorString(statuses
[i
].status
),
2493 ippErrorString(cupsLastError()));
2496 if ((attrptr
= ippFindAttribute(response
, "status-message",
2497 IPP_TAG_TEXT
)) != NULL
)
2498 print_test_error("status-message=\"%s\"",
2499 attrptr
->values
[0].string
.text
);
2502 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
2504 if (expect
->define_match
|| expect
->define_no_match
)
2507 if (expect
->if_defined
&& !get_variable(vars
, expect
->if_defined
))
2510 if (expect
->if_not_defined
&&
2511 get_variable(vars
, expect
->if_not_defined
))
2514 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
2516 if (found
&& expect
->not_expect
)
2517 print_test_error("NOT EXPECTED: %s", expect
->name
);
2518 else if (!found
&& !(expect
->not_expect
|| expect
->optional
))
2519 print_test_error("EXPECTED: %s", expect
->name
);
2522 if (!expect_matches(expect
, found
->value_tag
))
2523 print_test_error("EXPECTED: %s OF-TYPE %s (got %s)",
2524 expect
->name
, expect
->of_type
,
2525 ippTagString(found
->value_tag
));
2527 if (expect
->in_group
&& found
->group_tag
!= expect
->in_group
)
2528 print_test_error("EXPECTED: %s IN-GROUP %s (got %s).",
2529 expect
->name
, ippTagString(expect
->in_group
),
2530 ippTagString(found
->group_tag
));
2532 if (!with_value(expect
->with_value
, expect
->with_regex
, found
, 0))
2534 if (expect
->with_regex
)
2535 print_test_error("EXPECTED: %s WITH-VALUE /%s/",
2536 expect
->name
, expect
->with_value
);
2538 print_test_error("EXPECTED: %s WITH-VALUE \"%s\"",
2539 expect
->name
, expect
->with_value
);
2541 with_value(expect
->with_value
, expect
->with_regex
, found
, 1);
2544 if (expect
->count
> 0 && found
->num_values
!= expect
->count
)
2546 print_test_error("EXPECTED: %s COUNT %d (got %d)", expect
->name
,
2547 expect
->count
, found
->num_values
);
2550 if (expect
->same_count_as
)
2552 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
2556 print_test_error("EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2557 "(not returned)", expect
->name
,
2558 found
->num_values
, expect
->same_count_as
);
2559 else if (attrptr
->num_values
!= found
->num_values
)
2560 print_test_error("EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2561 "(%d values)", expect
->name
, found
->num_values
,
2562 expect
->same_count_as
, attrptr
->num_values
);
2568 if (Output
== _CUPS_OUTPUT_PLIST
)
2572 if (Output
== _CUPS_OUTPUT_PLIST
)
2577 ippDelete(response
);
2580 for (i
= 0; i
< num_statuses
; i
++)
2582 if (statuses
[i
].if_defined
)
2583 free(statuses
[i
].if_defined
);
2584 if (statuses
[i
].if_not_defined
)
2585 free(statuses
[i
].if_not_defined
);
2589 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
2592 if (expect
->of_type
)
2593 free(expect
->of_type
);
2594 if (expect
->same_count_as
)
2595 free(expect
->same_count_as
);
2596 if (expect
->if_defined
)
2597 free(expect
->if_defined
);
2598 if (expect
->if_not_defined
)
2599 free(expect
->if_not_defined
);
2600 if (expect
->with_value
)
2601 free(expect
->with_value
);
2602 if (expect
->define_match
)
2603 free(expect
->define_match
);
2604 if (expect
->define_no_match
)
2605 free(expect
->define_no_match
);
2606 if (expect
->define_value
)
2607 free(expect
->define_value
);
2611 for (i
= 0; i
< num_displayed
; i
++)
2615 if (!ignore_errors
&& !prev_pass
)
2626 ippDelete(response
);
2628 for (i
= 0; i
< num_statuses
; i
++)
2630 if (statuses
[i
].if_defined
)
2631 free(statuses
[i
].if_defined
);
2632 if (statuses
[i
].if_not_defined
)
2633 free(statuses
[i
].if_not_defined
);
2636 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
2639 if (expect
->of_type
)
2640 free(expect
->of_type
);
2641 if (expect
->same_count_as
)
2642 free(expect
->same_count_as
);
2643 if (expect
->if_defined
)
2644 free(expect
->if_defined
);
2645 if (expect
->if_not_defined
)
2646 free(expect
->if_not_defined
);
2647 if (expect
->with_value
)
2648 free(expect
->with_value
);
2649 if (expect
->define_match
)
2650 free(expect
->define_match
);
2651 if (expect
->define_no_match
)
2652 free(expect
->define_no_match
);
2653 if (expect
->define_value
)
2654 free(expect
->define_value
);
2657 for (i
= 0; i
< num_displayed
; i
++)
2665 * 'expand_variables()' - Expand variables in a string.
2669 expand_variables(_cups_vars_t
*vars
, /* I - Variables */
2670 char *dst
, /* I - Destination string buffer */
2671 const char *src
, /* I - Source string */
2672 size_t dstsize
) /* I - Size of destination buffer */
2674 char *dstptr
, /* Pointer into destination */
2675 *dstend
, /* End of destination */
2676 temp
[256], /* Temporary string */
2677 *tempptr
; /* Pointer into temporary string */
2678 const char *value
; /* Value to substitute */
2682 dstend
= dst
+ dstsize
- 1;
2684 while (*src
&& dstptr
< dstend
)
2689 * Substitute a string/number...
2692 if (!strncmp(src
, "$$", 2))
2697 else if (!strncmp(src
, "$ENV[", 5))
2699 strlcpy(temp
, src
+ 5, sizeof(temp
));
2701 for (tempptr
= temp
; *tempptr
; tempptr
++)
2702 if (*tempptr
== ']')
2708 value
= getenv(temp
);
2709 src
+= tempptr
- temp
+ 5;
2713 strlcpy(temp
, src
+ 1, sizeof(temp
));
2715 for (tempptr
= temp
; *tempptr
; tempptr
++)
2716 if (!isalnum(*tempptr
& 255) && *tempptr
!= '-' && *tempptr
!= '_')
2722 if (!strcmp(temp
, "uri"))
2724 else if (!strcmp(temp
, "filename"))
2725 value
= vars
->filename
;
2726 else if (!strcmp(temp
, "scheme") || !strcmp(temp
, "method"))
2727 value
= vars
->scheme
;
2728 else if (!strcmp(temp
, "username"))
2729 value
= vars
->userpass
;
2730 else if (!strcmp(temp
, "hostname"))
2731 value
= vars
->hostname
;
2732 else if (!strcmp(temp
, "port"))
2734 snprintf(temp
, sizeof(temp
), "%d", vars
->port
);
2737 else if (!strcmp(temp
, "resource"))
2738 value
= vars
->resource
;
2739 else if (!strcmp(temp
, "user"))
2742 value
= get_variable(vars
, temp
);
2744 src
+= tempptr
- temp
+ 1;
2754 strlcpy(dstptr
, value
, dstend
- dstptr
+ 1);
2755 dstptr
+= strlen(dstptr
);
2767 * 'expect_matches()' - Return true if the tag matches the specification.
2770 static int /* O - 1 if matches, 0 otherwise */
2772 _cups_expect_t
*expect
, /* I - Expected attribute */
2773 ipp_tag_t value_tag
) /* I - Value tag for attribute */
2775 int match
; /* Match? */
2776 char *of_type
, /* Type name to match */
2777 *next
, /* Next name to match */
2778 sep
; /* Separator character */
2782 * If we don't expect a particular type, return immediately...
2785 if (!expect
->of_type
)
2789 * Parse the "of_type" value since the string can contain multiple attribute
2790 * types separated by "," or "|"...
2793 for (of_type
= expect
->of_type
, match
= 0; !match
&& *of_type
; of_type
= next
)
2796 * Find the next separator, and set it (temporarily) to nul if present.
2799 for (next
= of_type
; *next
&& *next
!= '|' && *next
!= ','; next
++);
2801 if ((sep
= *next
) != '\0')
2805 * Support some meta-types to make it easier to write the test file.
2808 if (!strcmp(of_type
, "text"))
2809 match
= value_tag
== IPP_TAG_TEXTLANG
|| value_tag
== IPP_TAG_TEXT
;
2810 else if (!strcmp(of_type
, "name"))
2811 match
= value_tag
== IPP_TAG_NAMELANG
|| value_tag
== IPP_TAG_NAME
;
2812 else if (!strcmp(of_type
, "collection"))
2813 match
= value_tag
== IPP_TAG_BEGIN_COLLECTION
;
2815 match
= value_tag
== ippTagValue(of_type
);
2818 * Restore the separator if we have one...
2830 * 'get_collection()' - Get a collection value from the current test file.
2833 static ipp_t
* /* O - Collection value */
2834 get_collection(_cups_vars_t
*vars
, /* I - Variables */
2835 FILE *fp
, /* I - File to read from */
2836 int *linenum
) /* IO - Line number */
2838 char token
[1024], /* Token from file */
2839 temp
[1024], /* Temporary string */
2840 attr
[128]; /* Attribute name */
2841 ipp_tag_t value
; /* Current value type */
2842 ipp_t
*col
= ippNew(); /* Collection value */
2843 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
2846 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
2848 if (!strcmp(token
, "}"))
2850 else if (!strcmp(token
, "{") && lastcol
)
2853 * Another collection value
2856 ipp_t
*subcol
= get_collection(vars
, fp
, linenum
);
2857 /* Collection value */
2861 ipp_attribute_t
*tempcol
; /* Pointer to new buffer */
2865 * Reallocate memory...
2868 if ((tempcol
= realloc(lastcol
, sizeof(ipp_attribute_t
) +
2869 (lastcol
->num_values
+ 1) *
2870 sizeof(ipp_value_t
))) == NULL
)
2872 print_fatal_error("Unable to allocate memory on line %d.", *linenum
);
2876 if (tempcol
!= lastcol
)
2879 * Reset pointers in the list...
2883 col
->prev
->next
= tempcol
;
2885 col
->attrs
= tempcol
;
2887 lastcol
= col
->current
= col
->last
= tempcol
;
2890 lastcol
->values
[lastcol
->num_values
].collection
= subcol
;
2891 lastcol
->num_values
++;
2896 else if (!strcasecmp(token
, "MEMBER"))
2904 if (!get_token(fp
, token
, sizeof(token
), linenum
))
2906 print_fatal_error("Missing MEMBER value tag on line %d.", *linenum
);
2910 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
2912 print_fatal_error("Bad MEMBER value tag \"%s\" on line %d.", token
,
2917 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
2919 print_fatal_error("Missing MEMBER name on line %d.", *linenum
);
2923 if (!get_token(fp
, temp
, sizeof(temp
), linenum
))
2925 print_fatal_error("Missing MEMBER value on line %d.", *linenum
);
2929 expand_variables(vars
, token
, temp
, sizeof(token
));
2933 case IPP_TAG_BOOLEAN
:
2934 if (!strcasecmp(token
, "true"))
2935 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
2937 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, atoi(token
));
2940 case IPP_TAG_INTEGER
:
2942 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
2945 case IPP_TAG_RESOLUTION
:
2947 int xres
, /* X resolution */
2948 yres
; /* Y resolution */
2949 char units
[6]; /* Units */
2951 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
2952 (strcasecmp(units
, "dpi") && strcasecmp(units
, "dpc") &&
2953 strcasecmp(units
, "other")))
2955 print_fatal_error("Bad resolution value \"%s\" on line %d.",
2960 if (!strcasecmp(units
, "dpi"))
2961 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
2963 else if (!strcasecmp(units
, "dpc"))
2964 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
2967 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
2972 case IPP_TAG_RANGE
:
2974 int lowers
[4], /* Lower value */
2975 uppers
[4], /* Upper values */
2976 num_vals
; /* Number of values */
2979 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
2980 lowers
+ 0, uppers
+ 0,
2981 lowers
+ 1, uppers
+ 1,
2982 lowers
+ 2, uppers
+ 2,
2983 lowers
+ 3, uppers
+ 3);
2985 if ((num_vals
& 1) || num_vals
== 0)
2987 print_fatal_error("Bad rangeOfInteger value \"%s\" on line %d.",
2992 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
2997 case IPP_TAG_BEGIN_COLLECTION
:
2998 if (!strcmp(token
, "{"))
3000 ipp_t
*subcol
= get_collection(vars
, fp
, linenum
);
3001 /* Collection value */
3005 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
3013 print_fatal_error("Bad collection value on line %d.", *linenum
);
3019 if (!strchr(token
, ','))
3020 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
3024 * Multiple string values...
3027 int num_values
; /* Number of values */
3028 char *values
[100], /* Values */
3029 *ptr
; /* Pointer to next value */
3035 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
3038 values
[num_values
] = ptr
;
3042 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
3043 NULL
, (const char **)values
);
3053 * If we get here there was a parse error; free memory and return.
3065 * 'get_filename()' - Get a filename based on the current test file.
3068 static char * /* O - Filename */
3069 get_filename(const char *testfile
, /* I - Current test file */
3070 char *dst
, /* I - Destination filename */
3071 const char *src
, /* I - Source filename */
3072 size_t dstsize
) /* I - Size of destination buffer */
3074 char *dstptr
; /* Pointer into destination */
3075 _cups_globals_t
*cg
= _cupsGlobals();
3079 if (*src
== '<' && src
[strlen(src
) - 1] == '>')
3082 * Map <filename> to CUPS_DATADIR/ipptool/filename...
3085 snprintf(dst
, dstsize
, "%s/ipptool/%s", cg
->cups_datadir
, src
+ 1);
3086 dstptr
= dst
+ strlen(dst
) - 1;
3090 else if (*src
== '/' || !strchr(testfile
, '/'))
3093 * Use the path as-is...
3096 strlcpy(dst
, src
, dstsize
);
3101 * Make path relative to testfile...
3104 strlcpy(dst
, testfile
, dstsize
);
3105 if ((dstptr
= strrchr(dst
, '/')) != NULL
)
3108 dstptr
= dst
; /* Should never happen */
3110 strlcpy(dstptr
, src
, dstsize
- (dstptr
- dst
));
3118 * 'get_token()' - Get a token from a file.
3121 static char * /* O - Token from file or NULL on EOF */
3122 get_token(FILE *fp
, /* I - File to read from */
3123 char *buf
, /* I - Buffer to read into */
3124 int buflen
, /* I - Length of buffer */
3125 int *linenum
) /* IO - Current line number */
3127 int ch
, /* Character from file */
3128 quote
; /* Quoting character */
3129 char *bufptr
, /* Pointer into buffer */
3130 *bufend
; /* End of buffer */
3136 * Skip whitespace...
3139 while (isspace(ch
= getc(fp
)))
3151 else if (ch
== '\'' || ch
== '\"')
3154 * Quoted text or regular expression...
3159 bufend
= buf
+ buflen
- 1;
3161 while ((ch
= getc(fp
)) != EOF
)
3166 * Escape next character...
3169 if (bufptr
< bufend
)
3172 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
3175 else if (ch
== quote
)
3177 else if (bufptr
< bufend
)
3191 while ((ch
= getc(fp
)) != EOF
)
3200 * Whitespace delimited text...
3206 bufend
= buf
+ buflen
- 1;
3208 while ((ch
= getc(fp
)) != EOF
)
3209 if (isspace(ch
) || ch
== '#')
3211 else if (bufptr
< bufend
)
3216 else if (ch
== '\n')
3228 * 'get_variable()' - Get the value of a variable.
3231 static char * /* O - Value or NULL */
3232 get_variable(_cups_vars_t
*vars
, /* I - Variables */
3233 const char *name
) /* I - Variable name */
3235 _cups_var_t key
, /* Search key */
3236 *match
; /* Matching variable, if any */
3239 key
.name
= (char *)name
;
3240 match
= cupsArrayFind(vars
->vars
, &key
);
3242 return (match
? match
->value
: NULL
);
3247 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
3251 static char * /* O - ISO 8601 date/time string */
3252 iso_date(ipp_uchar_t
*date
) /* I - IPP (RFC 1903) date/time value */
3254 unsigned year
= (date
[0] << 8) + date
[1];
3256 static char buffer
[255]; /* String buffer */
3259 if (date
[9] == 0 && date
[10] == 0)
3260 snprintf(buffer
, sizeof(buffer
), "%04u-%02u-%02uT%02u:%02u:%02uZ",
3261 year
, date
[2], date
[3], date
[4], date
[5], date
[6]);
3263 snprintf(buffer
, sizeof(buffer
), "%04u-%02u-%02uT%02u:%02u:%02u%c%02u%02u",
3264 year
, date
[2], date
[3], date
[4], date
[5], date
[6],
3265 date
[8], date
[9], date
[10]);
3272 * 'password_cb()' - Password callback for authenticated tests.
3275 static const char * /* O - Password */
3276 password_cb(const char *prompt
) /* I - Prompt (unused) */
3285 * 'print_attr()' - Print an attribute on the screen.
3289 print_attr(ipp_attribute_t
*attr
, /* I - Attribute to print */
3290 ipp_tag_t
*group
) /* IO - Current group */
3292 int i
; /* Looping var */
3293 ipp_attribute_t
*colattr
; /* Collection attribute */
3296 if (Output
== _CUPS_OUTPUT_PLIST
)
3298 if (!attr
->name
|| (group
&& *group
!= attr
->group_tag
))
3304 *group
= attr
->group_tag
;
3310 print_xml_string("key", attr
->name
);
3311 if (attr
->num_values
> 1)
3314 else if (Output
== _CUPS_OUTPUT_TEST
)
3318 puts(" -- separator --");
3322 printf(" %s (%s%s) = ", attr
->name
,
3323 attr
->num_values
> 1 ? "1setOf " : "",
3324 ippTagString(attr
->value_tag
));
3327 switch (attr
->value_tag
)
3329 case IPP_TAG_INTEGER
:
3331 for (i
= 0; i
< attr
->num_values
; i
++)
3332 if (Output
== _CUPS_OUTPUT_PLIST
)
3333 printf("<integer>%d</integer>\n", attr
->values
[i
].integer
);
3335 printf("%d ", attr
->values
[i
].integer
);
3338 case IPP_TAG_BOOLEAN
:
3339 for (i
= 0; i
< attr
->num_values
; i
++)
3340 if (Output
== _CUPS_OUTPUT_PLIST
)
3341 puts(attr
->values
[i
].boolean
? "<true />" : "<false />");
3342 else if (attr
->values
[i
].boolean
)
3343 fputs("true ", stdout
);
3345 fputs("false ", stdout
);
3348 case IPP_TAG_RANGE
:
3349 for (i
= 0; i
< attr
->num_values
; i
++)
3350 if (Output
== _CUPS_OUTPUT_PLIST
)
3351 printf("<dict><key>lower</key><integer>%d</integer>"
3352 "<key>upper</key><integer>%d</integer></dict>\n",
3353 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
3355 printf("%d-%d ", attr
->values
[i
].range
.lower
,
3356 attr
->values
[i
].range
.upper
);
3359 case IPP_TAG_RESOLUTION
:
3360 for (i
= 0; i
< attr
->num_values
; i
++)
3361 if (Output
== _CUPS_OUTPUT_PLIST
)
3362 printf("<dict><key>xres</key><integer>%d</integer>"
3363 "<key>yres</key><integer>%d</integer>"
3364 "<key>units</key><string>%s</string></dict>\n",
3365 attr
->values
[i
].resolution
.xres
,
3366 attr
->values
[i
].resolution
.yres
,
3367 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
3370 printf("%dx%d%s ", attr
->values
[i
].resolution
.xres
,
3371 attr
->values
[i
].resolution
.yres
,
3372 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
3377 for (i
= 0; i
< attr
->num_values
; i
++)
3378 if (Output
== _CUPS_OUTPUT_PLIST
)
3379 printf("<date>%s</date>\n", iso_date(attr
->values
[i
].date
));
3381 printf("%s ", iso_date(attr
->values
[i
].date
));
3384 case IPP_TAG_STRING
:
3387 case IPP_TAG_KEYWORD
:
3388 case IPP_TAG_CHARSET
:
3390 case IPP_TAG_MIMETYPE
:
3391 case IPP_TAG_LANGUAGE
:
3392 for (i
= 0; i
< attr
->num_values
; i
++)
3393 if (Output
== _CUPS_OUTPUT_PLIST
)
3394 print_xml_string("string", attr
->values
[i
].string
.text
);
3396 printf("\"%s\" ", attr
->values
[i
].string
.text
);
3399 case IPP_TAG_TEXTLANG
:
3400 case IPP_TAG_NAMELANG
:
3401 for (i
= 0; i
< attr
->num_values
; i
++)
3402 if (Output
== _CUPS_OUTPUT_PLIST
)
3404 fputs("<dict><key>language</key><string>", stdout
);
3405 print_xml_string(NULL
, attr
->values
[i
].string
.charset
);
3406 fputs("</string><key>string</key><string>", stdout
);
3407 print_xml_string(NULL
, attr
->values
[i
].string
.text
);
3408 puts("</string></dict>");
3411 printf("\"%s\"(%s) ", attr
->values
[i
].string
.text
,
3412 attr
->values
[i
].string
.charset
);
3415 case IPP_TAG_BEGIN_COLLECTION
:
3416 for (i
= 0; i
< attr
->num_values
; i
++)
3418 if (Output
== _CUPS_OUTPUT_PLIST
)
3421 for (colattr
= attr
->values
[i
].collection
->attrs
;
3423 colattr
= colattr
->next
)
3424 print_attr(colattr
, NULL
);
3432 print_col(attr
->values
[i
].collection
);
3438 if (Output
== _CUPS_OUTPUT_PLIST
)
3439 printf("<string><<%s>></string>\n",
3440 ippTagString(attr
->value_tag
));
3442 fputs(ippTagString(attr
->value_tag
), stdout
);
3446 if (Output
== _CUPS_OUTPUT_PLIST
)
3448 if (attr
->num_values
> 1)
3457 * 'print_col()' - Print a collection attribute on the screen.
3461 print_col(ipp_t
*col
) /* I - Collection attribute to print */
3463 int i
; /* Looping var */
3464 ipp_attribute_t
*attr
; /* Current attribute in collection */
3467 fputs("{ ", stdout
);
3468 for (attr
= col
->attrs
; attr
; attr
= attr
->next
)
3470 printf("%s (%s%s) = ", attr
->name
, attr
->num_values
> 1 ? "1setOf " : "",
3471 ippTagString(attr
->value_tag
));
3473 switch (attr
->value_tag
)
3475 case IPP_TAG_INTEGER
:
3477 for (i
= 0; i
< attr
->num_values
; i
++)
3478 printf("%d ", attr
->values
[i
].integer
);
3481 case IPP_TAG_BOOLEAN
:
3482 for (i
= 0; i
< attr
->num_values
; i
++)
3483 if (attr
->values
[i
].boolean
)
3489 case IPP_TAG_NOVALUE
:
3493 case IPP_TAG_RANGE
:
3494 for (i
= 0; i
< attr
->num_values
; i
++)
3495 printf("%d-%d ", attr
->values
[i
].range
.lower
,
3496 attr
->values
[i
].range
.upper
);
3499 case IPP_TAG_RESOLUTION
:
3500 for (i
= 0; i
< attr
->num_values
; i
++)
3501 printf("%dx%d%s ", attr
->values
[i
].resolution
.xres
,
3502 attr
->values
[i
].resolution
.yres
,
3503 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
3507 case IPP_TAG_STRING
:
3510 case IPP_TAG_KEYWORD
:
3511 case IPP_TAG_CHARSET
:
3513 case IPP_TAG_MIMETYPE
:
3514 case IPP_TAG_LANGUAGE
:
3515 for (i
= 0; i
< attr
->num_values
; i
++)
3516 printf("\"%s\" ", attr
->values
[i
].string
.text
);
3519 case IPP_TAG_TEXTLANG
:
3520 case IPP_TAG_NAMELANG
:
3521 for (i
= 0; i
< attr
->num_values
; i
++)
3522 printf("\"%s\",%s ", attr
->values
[i
].string
.text
,
3523 attr
->values
[i
].string
.charset
);
3526 case IPP_TAG_BEGIN_COLLECTION
:
3527 for (i
= 0; i
< attr
->num_values
; i
++)
3529 print_col(attr
->values
[i
].collection
);
3535 break; /* anti-compiler-warning-code */
3544 * 'print_csv()' - Print a line of CSV text.
3549 ipp_attribute_t
*attr
, /* I - First attribute for line */
3550 int num_displayed
, /* I - Number of attributes to display */
3551 char **displayed
, /* I - Attributes to display */
3552 size_t *widths
) /* I - Column widths */
3554 int i
; /* Looping var */
3555 size_t maxlength
; /* Max length of all columns */
3556 char *buffer
, /* String buffer */
3557 *bufptr
; /* Pointer into buffer */
3558 ipp_attribute_t
*current
; /* Current attribute */
3562 * Get the maximum string length we have to show and allocate...
3565 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
3566 if (widths
[i
] > maxlength
)
3567 maxlength
= widths
[i
];
3571 if ((buffer
= malloc(maxlength
)) == NULL
)
3575 * Loop through the attributes to display...
3580 for (i
= 0; i
< num_displayed
; i
++)
3587 for (current
= attr
; current
; current
= current
->next
)
3591 else if (!strcmp(current
->name
, displayed
[i
]))
3593 _ippAttrString(current
, buffer
, maxlength
);
3598 if (strchr(buffer
, ',') != NULL
|| strchr(buffer
, '\"') != NULL
||
3599 strchr(buffer
, '\\') != NULL
)
3602 for (bufptr
= buffer
; *bufptr
; bufptr
++)
3604 if (*bufptr
== '\\' || *bufptr
== '\"')
3611 fputs(buffer
, stdout
);
3617 for (i
= 0; i
< num_displayed
; i
++)
3622 fputs(displayed
[i
], stdout
);
3632 * 'print_fatal_error()' - Print a fatal error message.
3636 print_fatal_error(const char *s
, /* I - Printf-style format string */
3637 ...) /* I - Additional arguments as needed */
3639 char buffer
[10240]; /* Format buffer */
3640 va_list ap
; /* Pointer to arguments */
3644 * Format the error message...
3648 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
3655 if (Output
== _CUPS_OUTPUT_PLIST
)
3658 print_xml_trailer(0, buffer
);
3661 _cupsLangPrintf(stderr
, "ipptool: %s", buffer
);
3666 * 'print_line()' - Print a line of formatted or CSV text.
3671 ipp_attribute_t
*attr
, /* I - First attribute for line */
3672 int num_displayed
, /* I - Number of attributes to display */
3673 char **displayed
, /* I - Attributes to display */
3674 size_t *widths
) /* I - Column widths */
3676 int i
; /* Looping var */
3677 size_t maxlength
; /* Max length of all columns */
3678 char *buffer
; /* String buffer */
3679 ipp_attribute_t
*current
; /* Current attribute */
3683 * Get the maximum string length we have to show and allocate...
3686 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
3687 if (widths
[i
] > maxlength
)
3688 maxlength
= widths
[i
];
3692 if ((buffer
= malloc(maxlength
)) == NULL
)
3696 * Loop through the attributes to display...
3701 for (i
= 0; i
< num_displayed
; i
++)
3708 for (current
= attr
; current
; current
= current
->next
)
3712 else if (!strcmp(current
->name
, displayed
[i
]))
3714 _ippAttrString(current
, buffer
, maxlength
);
3719 printf("%*s", (int)-widths
[i
], buffer
);
3725 for (i
= 0; i
< num_displayed
; i
++)
3730 printf("%*s", (int)-widths
[i
], displayed
[i
]);
3734 for (i
= 0; i
< num_displayed
; i
++)
3739 memset(buffer
, '-', widths
[i
]);
3740 buffer
[widths
[i
]] = '\0';
3741 fputs(buffer
, stdout
);
3751 * 'print_test_error()' - Print a test error message.
3755 print_test_error(const char *s
, /* I - Printf-style format string */
3756 ...) /* I - Additional arguments as needed */
3758 char buffer
[10240]; /* Format buffer */
3759 va_list ap
; /* Pointer to arguments */
3763 * Format the error message...
3767 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
3774 if (Output
== _CUPS_OUTPUT_PLIST
)
3775 print_xml_string("string", buffer
);
3777 printf(" %s\n", buffer
);
3782 * 'print_xml_header()' - Print a standard XML plist header.
3786 print_xml_header(void)
3790 puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
3791 puts("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
3792 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
3793 puts("<plist version=\"1.0\">");
3795 puts("<key>Transfer</key>");
3796 printf("<string>%s</string>\n",
3797 Transfer
== _CUPS_TRANSFER_AUTO
? "auto" :
3798 Transfer
== _CUPS_TRANSFER_CHUNKED
? "chunked" : "length");
3799 puts("<key>Tests</key>");
3808 * 'print_xml_string()' - Print an XML string with escaping.
3812 print_xml_string(const char *element
, /* I - Element name or NULL */
3813 const char *s
) /* I - String to print */
3816 printf("<%s>", element
);
3821 fputs("&", stdout
);
3823 fputs("<", stdout
);
3825 fputs(">", stdout
);
3833 printf("</%s>\n", element
);
3838 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
3842 print_xml_trailer(int success
, /* I - 1 on success, 0 on failure */
3843 const char *message
) /* I - Error message or NULL */
3848 puts("<key>Successful</key>");
3849 puts(success
? "<true />" : "<false />");
3852 puts("<key>ErrorMessage</key>");
3853 print_xml_string("string", message
);
3864 * 'set_variable()' - Set a variable value.
3868 set_variable(_cups_vars_t
*vars
, /* I - Variables */
3869 const char *name
, /* I - Variable name */
3870 const char *value
) /* I - Value string */
3872 _cups_var_t key
, /* Search key */
3873 *var
; /* New variable */
3876 key
.name
= (char *)name
;
3877 if ((var
= cupsArrayFind(vars
->vars
, &key
)) != NULL
)
3880 var
->value
= strdup(value
);
3882 else if ((var
= malloc(sizeof(_cups_var_t
))) == NULL
)
3884 print_fatal_error("Unable to allocate memory for variable \"%s\".", name
);
3889 var
->name
= strdup(name
);
3890 var
->value
= strdup(value
);
3892 cupsArrayAdd(vars
->vars
, var
);
3898 * 'timeout_cb()' - Handle HTTP timeouts.
3901 static int /* O - 1 to continue, 0 to cancel */
3902 timeout_cb(http_t
*http
, /* I - Connection to server (unused) */
3903 void *user_data
) /* I - User data (unused) */
3913 * 'usage()' - Show program usage.
3919 _cupsLangPuts(stderr
, _("Usage: ipptool [options] URI filename [ ... "
3921 _cupsLangPuts(stderr
, _("Options:"));
3922 _cupsLangPuts(stderr
, _(" -4 Connect using IPv4."));
3923 _cupsLangPuts(stderr
, _(" -6 Connect using IPv6."));
3924 _cupsLangPuts(stderr
, _(" -C Send requests using "
3925 "chunking (default)."));
3926 _cupsLangPuts(stderr
, _(" -E Test with TLS "
3928 _cupsLangPuts(stderr
, _(" -I Ignore errors."));
3929 _cupsLangPuts(stderr
, _(" -L Send requests using "
3930 "content-length."));
3931 _cupsLangPuts(stderr
, _(" -S Test with SSL "
3933 _cupsLangPuts(stderr
, _(" -T Set the receive/send "
3934 "timeout in seconds."));
3935 _cupsLangPuts(stderr
, _(" -V version Set default IPP "
3937 _cupsLangPuts(stderr
, _(" -X Produce XML plist instead "
3939 _cupsLangPuts(stderr
, _(" -d name=value Set named variable to "
3941 _cupsLangPuts(stderr
, _(" -f filename Set default request "
3943 _cupsLangPuts(stderr
, _(" -i seconds Repeat the last file with "
3944 "the given time interval."));
3945 _cupsLangPuts(stderr
, _(" -n count Repeat the last file the "
3946 "given number of times."));
3947 _cupsLangPuts(stderr
, _(" -q Be quiet - no output "
3949 _cupsLangPuts(stderr
, _(" -t Produce a test report."));
3950 _cupsLangPuts(stderr
, _(" -v Show all attributes sent "
3958 * 'validate_attr()' - Determine whether an attribute is valid.
3961 static int /* O - 1 if valid, 0 otherwise */
3962 validate_attr(ipp_attribute_t
*attr
, /* I - Attribute to validate */
3963 int print
) /* I - 1 = report issues to stdout */
3965 int i
; /* Looping var */
3966 char scheme
[64], /* Scheme from URI */
3967 userpass
[256], /* Username/password from URI */
3968 hostname
[256], /* Hostname from URI */
3969 resource
[1024]; /* Resource from URI */
3970 int port
, /* Port number from URI */
3971 uri_status
, /* URI separation status */
3972 valid
= 1; /* Is the attribute valid? */
3973 const char *ptr
; /* Pointer into string */
3974 ipp_attribute_t
*colattr
; /* Collection attribute */
3975 regex_t re
; /* Regular expression */
3976 ipp_uchar_t
*date
; /* Current date value */
3987 * Validate the attribute name.
3990 for (ptr
= attr
->name
; *ptr
; ptr
++)
3991 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' && *ptr
!= '_')
3994 if (*ptr
|| ptr
== attr
->name
)
3999 print_test_error("\"%s\": Bad attribute name - invalid character (RFC "
4000 "2911 section 4.1.3).", attr
->name
);
4003 if ((ptr
- attr
->name
) > 255)
4008 print_test_error("\"%s\": Bad attribute name - bad length (RFC 2911 "
4009 "section 4.1.3).", attr
->name
);
4012 switch (attr
->value_tag
)
4014 case IPP_TAG_INTEGER
:
4017 case IPP_TAG_BOOLEAN
:
4018 for (i
= 0; i
< attr
->num_values
; i
++)
4020 if (attr
->values
[i
].boolean
!= 0 &&
4021 attr
->values
[i
].boolean
!= 1)
4026 print_test_error("\"%s\": Bad boolen value %d (RFC 2911 section "
4027 "4.1.10).", attr
->name
, attr
->values
[i
].boolean
);
4035 for (i
= 0; i
< attr
->num_values
; i
++)
4037 if (attr
->values
[i
].integer
< 1)
4042 print_test_error("\"%s\": Bad enum value %d - out of range "
4043 "(RFC 2911 section 4.1.4).", attr
->name
,
4044 attr
->values
[i
].integer
);
4051 case IPP_TAG_STRING
:
4052 for (i
= 0; i
< attr
->num_values
; i
++)
4054 if (attr
->values
[i
].unknown
.length
> 1023)
4059 print_test_error("\"%s\": Bad octetString value - bad length %d "
4060 "(RFC 2911 section 4.1.10).", attr
->name
,
4061 attr
->values
[i
].unknown
.length
);
4069 for (i
= 0; i
< attr
->num_values
; i
++)
4071 date
= attr
->values
[i
].date
;
4073 if (date
[2] < 1 || date
[2] > 12)
4078 print_test_error("\"%s\": Bad dateTime month %u (RFC 2911 "
4079 "section 4.1.13).", attr
->name
, date
[2]);
4084 if (date
[3] < 1 || date
[3] > 31)
4089 print_test_error("\"%s\": Bad dateTime day %u (RFC 2911 "
4090 "section 4.1.13).", attr
->name
, date
[3]);
4100 print_test_error("\"%s\": Bad dateTime hours %u (RFC 2911 "
4101 "section 4.1.13).", attr
->name
, date
[4]);
4111 print_test_error("\"%s\": Bad dateTime minutes %u (RFC 2911 "
4112 "section 4.1.13).", attr
->name
, date
[5]);
4122 print_test_error("\"%s\": Bad dateTime seconds %u (RFC 2911 "
4123 "section 4.1.13).", attr
->name
, date
[6]);
4133 print_test_error("\"%s\": Bad dateTime deciseconds %u (RFC 2911 "
4134 "section 4.1.13).", attr
->name
, date
[7]);
4139 if (date
[8] != '-' && date
[8] != '+')
4144 print_test_error("\"%s\": Bad dateTime UTC sign '%c' (RFC 2911 "
4145 "section 4.1.13).", attr
->name
, date
[8]);
4155 print_test_error("\"%s\": Bad dateTime UTC hours %u (RFC 2911 "
4156 "section 4.1.13).", attr
->name
, date
[9]);
4166 print_test_error("\"%s\": Bad dateTime UTC minutes %u (RFC 2911 "
4167 "section 4.1.13).", attr
->name
, date
[10]);
4174 case IPP_TAG_RESOLUTION
:
4175 for (i
= 0; i
< attr
->num_values
; i
++)
4177 if (attr
->values
[i
].resolution
.xres
<= 0)
4182 print_test_error("\"%s\": Bad resolution value %dx%d%s - cross "
4183 "feed resolution must be positive (RFC 2911 "
4184 "section 4.1.13).", attr
->name
,
4185 attr
->values
[i
].resolution
.xres
,
4186 attr
->values
[i
].resolution
.yres
,
4187 attr
->values
[i
].resolution
.units
==
4188 IPP_RES_PER_INCH
? "dpi" :
4189 attr
->values
[i
].resolution
.units
==
4190 IPP_RES_PER_CM
? "dpc" : "unknown");
4195 if (attr
->values
[i
].resolution
.yres
<= 0)
4200 print_test_error("\"%s\": Bad resolution value %dx%d%s - feed "
4201 "resolution must be positive (RFC 2911 section "
4202 "4.1.13).", attr
->name
,
4203 attr
->values
[i
].resolution
.xres
,
4204 attr
->values
[i
].resolution
.yres
,
4205 attr
->values
[i
].resolution
.units
==
4206 IPP_RES_PER_INCH
? "dpi" :
4207 attr
->values
[i
].resolution
.units
==
4208 IPP_RES_PER_CM
? "dpc" : "unknown");
4213 if (attr
->values
[i
].resolution
.units
!= IPP_RES_PER_INCH
&&
4214 attr
->values
[i
].resolution
.units
!= IPP_RES_PER_CM
)
4219 print_test_error("\"%s\": Bad resolution value %dx%d%s - bad "
4220 "units value (RFC 2911 section 4.1.13).",
4221 attr
->name
, attr
->values
[i
].resolution
.xres
,
4222 attr
->values
[i
].resolution
.yres
,
4223 attr
->values
[i
].resolution
.units
==
4224 IPP_RES_PER_INCH
? "dpi" :
4225 attr
->values
[i
].resolution
.units
==
4226 IPP_RES_PER_CM
? "dpc" : "unknown");
4233 case IPP_TAG_RANGE
:
4234 for (i
= 0; i
< attr
->num_values
; i
++)
4236 if (attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
4241 print_test_error("\"%s\": Bad rangeOfInteger value %d-%d - lower "
4242 "greater than upper (RFC 2911 section 4.1.13).",
4243 attr
->name
, attr
->values
[i
].range
.lower
,
4244 attr
->values
[i
].range
.upper
);
4251 case IPP_TAG_BEGIN_COLLECTION
:
4252 for (i
= 0; i
< attr
->num_values
; i
++)
4254 for (colattr
= attr
->values
[i
].collection
->attrs
;
4256 colattr
= colattr
->next
)
4258 if (!validate_attr(colattr
, 0))
4265 if (colattr
&& print
)
4267 print_test_error("\"%s\": Bad collection value.", attr
->name
);
4271 validate_attr(colattr
, print
);
4272 colattr
= colattr
->next
;
4279 case IPP_TAG_TEXTLANG
:
4280 for (i
= 0; i
< attr
->num_values
; i
++)
4282 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4284 if ((*ptr
& 0xe0) == 0xc0)
4287 if ((*ptr
& 0xc0) != 0x80)
4290 else if ((*ptr
& 0xf0) == 0xe0)
4293 if ((*ptr
& 0xc0) != 0x80)
4296 if ((*ptr
& 0xc0) != 0x80)
4299 else if ((*ptr
& 0xf8) == 0xf0)
4302 if ((*ptr
& 0xc0) != 0x80)
4305 if ((*ptr
& 0xc0) != 0x80)
4308 if ((*ptr
& 0xc0) != 0x80)
4311 else if (*ptr
& 0x80)
4320 print_test_error("\"%s\": Bad text value \"%s\" - bad UTF-8 "
4321 "sequence (RFC 2911 section 4.1.1).", attr
->name
,
4322 attr
->values
[i
].string
.text
);
4327 if ((ptr
- attr
->values
[i
].string
.text
) > 1023)
4332 print_test_error("\"%s\": Bad text value \"%s\" - bad length %d "
4333 "(RFC 2911 section 4.1.1).", attr
->name
,
4334 attr
->values
[i
].string
.text
,
4335 (int)strlen(attr
->values
[i
].string
.text
));
4343 case IPP_TAG_NAMELANG
:
4344 for (i
= 0; i
< attr
->num_values
; i
++)
4346 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4348 if ((*ptr
& 0xe0) == 0xc0)
4351 if ((*ptr
& 0xc0) != 0x80)
4354 else if ((*ptr
& 0xf0) == 0xe0)
4357 if ((*ptr
& 0xc0) != 0x80)
4360 if ((*ptr
& 0xc0) != 0x80)
4363 else if ((*ptr
& 0xf8) == 0xf0)
4366 if ((*ptr
& 0xc0) != 0x80)
4369 if ((*ptr
& 0xc0) != 0x80)
4372 if ((*ptr
& 0xc0) != 0x80)
4375 else if (*ptr
& 0x80)
4384 print_test_error("\"%s\": Bad name value \"%s\" - bad UTF-8 "
4385 "sequence (RFC 2911 section 4.1.2).", attr
->name
,
4386 attr
->values
[i
].string
.text
);
4391 if ((ptr
- attr
->values
[i
].string
.text
) > 1023)
4396 print_test_error("\"%s\": Bad name value \"%s\" - bad length %d "
4397 "(RFC 2911 section 4.1.2).", attr
->name
,
4398 attr
->values
[i
].string
.text
,
4399 (int)strlen(attr
->values
[i
].string
.text
));
4406 case IPP_TAG_KEYWORD
:
4407 for (i
= 0; i
< attr
->num_values
; i
++)
4409 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4410 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' &&
4414 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
4419 print_test_error("\"%s\": Bad keyword value \"%s\" - invalid "
4420 "character (RFC 2911 section 4.1.3).",
4421 attr
->name
, attr
->values
[i
].string
.text
);
4426 if ((ptr
- attr
->values
[i
].string
.text
) > 255)
4431 print_test_error("\"%s\": Bad keyword value \"%s\" - bad "
4432 "length %d (RFC 2911 section 4.1.3).",
4433 attr
->name
, attr
->values
[i
].string
.text
,
4434 (int)strlen(attr
->values
[i
].string
.text
));
4442 for (i
= 0; i
< attr
->num_values
; i
++)
4444 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
,
4445 attr
->values
[i
].string
.text
,
4446 scheme
, sizeof(scheme
),
4447 userpass
, sizeof(userpass
),
4448 hostname
, sizeof(hostname
),
4449 &port
, resource
, sizeof(resource
));
4451 if (uri_status
< HTTP_URI_OK
)
4456 print_test_error("\"%s\": Bad URI value \"%s\" - %s "
4457 "(RFC 2911 section 4.1.5).", attr
->name
,
4458 attr
->values
[i
].string
.text
,
4459 URIStatusStrings
[uri_status
-
4460 HTTP_URI_OVERFLOW
]);
4465 if (strlen(attr
->values
[i
].string
.text
) > 1023)
4470 print_test_error("\"%s\": Bad URI value \"%s\" - bad length %d "
4471 "(RFC 2911 section 4.1.5).", attr
->name
,
4472 attr
->values
[i
].string
.text
,
4473 (int)strlen(attr
->values
[i
].string
.text
));
4480 case IPP_TAG_URISCHEME
:
4481 for (i
= 0; i
< attr
->num_values
; i
++)
4483 ptr
= attr
->values
[i
].string
.text
;
4484 if (islower(*ptr
& 255))
4486 for (ptr
++; *ptr
; ptr
++)
4487 if (!islower(*ptr
& 255) && !isdigit(*ptr
& 255) &&
4488 *ptr
!= '+' && *ptr
!= '-' && *ptr
!= '.')
4492 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
4497 print_test_error("\"%s\": Bad uriScheme value \"%s\" - bad "
4498 "characters (RFC 2911 section 4.1.6).",
4499 attr
->name
, attr
->values
[i
].string
.text
);
4504 if ((ptr
- attr
->values
[i
].string
.text
) > 63)
4509 print_test_error("\"%s\": Bad uriScheme value \"%s\" - bad "
4510 "length %d (RFC 2911 section 4.1.6).",
4511 attr
->name
, attr
->values
[i
].string
.text
,
4512 (int)strlen(attr
->values
[i
].string
.text
));
4519 case IPP_TAG_CHARSET
:
4520 for (i
= 0; i
< attr
->num_values
; i
++)
4522 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4523 if (!isprint(*ptr
& 255) || isupper(*ptr
& 255) ||
4524 isspace(*ptr
& 255))
4527 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
4532 print_test_error("\"%s\": Bad charset value \"%s\" - bad "
4533 "characters (RFC 2911 section 4.1.7).",
4534 attr
->name
, attr
->values
[i
].string
.text
);
4539 if ((ptr
- attr
->values
[i
].string
.text
) > 40)
4544 print_test_error("\"%s\": Bad charset value \"%s\" - bad "
4545 "length %d (RFC 2911 section 4.1.7).",
4546 attr
->name
, attr
->values
[i
].string
.text
,
4547 (int)strlen(attr
->values
[i
].string
.text
));
4554 case IPP_TAG_LANGUAGE
:
4556 * The following regular expression is derived from the ABNF for
4557 * language tags in RFC 4646. All I can say is that this is the
4558 * easiest way to check the values...
4561 if ((i
= regcomp(&re
,
4563 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
4565 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
4566 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
4567 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
4568 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
4569 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
4571 "x(-[a-z0-9]{1,8})+" /* privateuse */
4573 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
4575 REG_NOSUB
| REG_EXTENDED
)) != 0)
4577 char temp
[256]; /* Temporary error string */
4579 regerror(i
, &re
, temp
, sizeof(temp
));
4580 print_fatal_error("Unable to compile naturalLanguage regular "
4581 "expression: %s.", temp
);
4584 for (i
= 0; i
< attr
->num_values
; i
++)
4586 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
4591 print_test_error("\"%s\": Bad naturalLanguage value \"%s\" - bad "
4592 "characters (RFC 2911 section 4.1.8).",
4593 attr
->name
, attr
->values
[i
].string
.text
);
4598 if (strlen(attr
->values
[i
].string
.text
) > 63)
4603 print_test_error("\"%s\": Bad naturalLanguage value \"%s\" - bad "
4604 "length %d (RFC 2911 section 4.1.8).",
4605 attr
->name
, attr
->values
[i
].string
.text
,
4606 (int)strlen(attr
->values
[i
].string
.text
));
4615 case IPP_TAG_MIMETYPE
:
4617 * The following regular expression is derived from the ABNF for
4618 * language tags in RFC 2045 and 4288. All I can say is that this is
4619 * the easiest way to check the values...
4622 if ((i
= regcomp(&re
,
4624 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
4626 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
4627 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
4628 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
4631 REG_NOSUB
| REG_EXTENDED
)) != 0)
4633 char temp
[256]; /* Temporary error string */
4635 regerror(i
, &re
, temp
, sizeof(temp
));
4636 print_fatal_error("Unable to compile mimeMediaType regular "
4637 "expression: %s.", temp
);
4640 for (i
= 0; i
< attr
->num_values
; i
++)
4642 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
4647 print_test_error("\"%s\": Bad mimeMediaType value \"%s\" - bad "
4648 "characters (RFC 2911 section 4.1.9).",
4649 attr
->name
, attr
->values
[i
].string
.text
);
4654 if (strlen(attr
->values
[i
].string
.text
) > 255)
4659 print_test_error("\"%s\": Bad mimeMediaType value \"%s\" - bad "
4660 "length %d (RFC 2911 section 4.1.9).",
4661 attr
->name
, attr
->values
[i
].string
.text
,
4662 (int)strlen(attr
->values
[i
].string
.text
));
4678 * 'with_value()' - Test a WITH-VALUE predicate.
4681 static int /* O - 1 on match, 0 on non-match */
4682 with_value(char *value
, /* I - Value string */
4683 int regex
, /* I - Value is a regular expression */
4684 ipp_attribute_t
*attr
, /* I - Attribute to compare */
4685 int report
) /* I - 1 = report failures */
4687 int i
; /* Looping var */
4688 char *valptr
; /* Pointer into value */
4692 * NULL matches everything.
4695 if (!value
|| !*value
)
4699 * Compare the value string to the attribute value.
4702 switch (attr
->value_tag
)
4704 case IPP_TAG_INTEGER
:
4706 for (i
= 0; i
< attr
->num_values
; i
++)
4708 char op
, /* Comparison operator */
4709 *nextptr
; /* Next pointer */
4710 int intvalue
; /* Integer value */
4714 if (!strncmp(valptr
, "no-value,", 9))
4717 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
4718 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
4719 *valptr
== '=' || *valptr
== '>')
4722 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
4724 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
4732 intvalue
= strtol(valptr
, &nextptr
, 0);
4733 if (nextptr
== valptr
)
4740 if (attr
->values
[i
].integer
== intvalue
)
4744 if (attr
->values
[i
].integer
< intvalue
)
4748 if (attr
->values
[i
].integer
> intvalue
)
4757 for (i
= 0; i
< attr
->num_values
; i
++)
4758 print_test_error("GOT: %s=%d", attr
->name
, attr
->values
[i
].integer
);
4762 case IPP_TAG_RANGE
:
4763 for (i
= 0; i
< attr
->num_values
; i
++)
4765 char op
, /* Comparison operator */
4766 *nextptr
; /* Next pointer */
4767 int intvalue
; /* Integer value */
4771 if (!strncmp(valptr
, "no-value,", 9))
4774 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
4775 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
4776 *valptr
== '=' || *valptr
== '>')
4779 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
4781 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
4789 intvalue
= strtol(valptr
, &nextptr
, 0);
4790 if (nextptr
== valptr
)
4797 if (attr
->values
[i
].range
.lower
== intvalue
||
4798 attr
->values
[i
].range
.upper
== intvalue
)
4802 if (attr
->values
[i
].range
.upper
< intvalue
)
4806 if (attr
->values
[i
].range
.upper
> intvalue
)
4815 for (i
= 0; i
< attr
->num_values
; i
++)
4816 print_test_error("GOT: %s=%d-%d", attr
->name
,
4817 attr
->values
[i
].range
.lower
,
4818 attr
->values
[i
].range
.upper
);
4822 case IPP_TAG_BOOLEAN
:
4823 for (i
= 0; i
< attr
->num_values
; i
++)
4825 if (!strcmp(value
, "true") == attr
->values
[i
].boolean
)
4831 for (i
= 0; i
< attr
->num_values
; i
++)
4832 print_test_error("GOT: %s=%s", attr
->name
,
4833 attr
->values
[i
].boolean
? "true" : "false");
4837 case IPP_TAG_NOVALUE
:
4838 return (!strcmp(value
, "no-value") || !strncmp(value
, "no-value,", 9));
4840 case IPP_TAG_CHARSET
:
4841 case IPP_TAG_KEYWORD
:
4842 case IPP_TAG_LANGUAGE
:
4843 case IPP_TAG_MIMETYPE
:
4845 case IPP_TAG_NAMELANG
:
4847 case IPP_TAG_TEXTLANG
:
4849 case IPP_TAG_URISCHEME
:
4853 * Value is an extended, case-sensitive POSIX regular expression...
4856 regex_t re
; /* Regular expression */
4858 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
4860 char temp
[256]; /* Temporary string */
4862 regerror(i
, &re
, temp
, sizeof(temp
));
4864 print_fatal_error("Unable to compile WITH-VALUE regular expression "
4865 "\"%s\" - %s", value
, temp
);
4870 * See if ALL of the values match the given regular expression.
4873 for (i
= 0; i
< attr
->num_values
; i
++)
4875 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
4878 print_test_error("GOT: %s=\"%s\"", attr
->name
,
4879 attr
->values
[i
].string
.text
);
4887 return (i
== attr
->num_values
);
4892 * Value is a literal string, see if at least one value matches the
4896 for (i
= 0; i
< attr
->num_values
; i
++)
4898 if (!strcmp(value
, attr
->values
[i
].string
.text
))
4904 for (i
= 0; i
< attr
->num_values
; i
++)
4905 print_test_error("GOT: %s=\"%s\"", attr
->name
,
4906 attr
->values
[i
].string
.text
);