4 * ipptool command for CUPS.
6 * Copyright 2007-2010 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/".
17 * main() - Parse options and do tests.
18 * compare_vars() - Compare two variables.
19 * do_tests() - Do tests as specified in the test file.
20 * expand_variables() - Expand variables in a string.
21 * expect_matches() - Return true if the tag matches the specification.
22 * get_collection() - Get a collection value from the current test file.
23 * get_filename() - Get a filename based on the current test file.
24 * get_token() - Get a token from a file.
25 * get_variable() - Get the value of a variable.
26 * iso_date() - Return an ISO 8601 date/time string for the given IPP
28 * password_cb() - Password callback for authenticated tests.
29 * print_attr() - Print an attribute on the screen.
30 * print_col() - Print a collection attribute on the screen.
31 * print_csv() - Print a line of CSV text.
32 * print_fatal_error() - Print a fatal error message.
33 * print_line() - Print a line of formatted or CSV text.
34 * print_test_error() - Print a test error message.
35 * print_xml_header() - Print a standard XML plist header.
36 * print_xml_string() - Print an XML string with escaping.
37 * print_xml_trailer() - Print the XML trailer with success/fail value.
38 * set_variable() - Set a variable value.
39 * usage() - Show program usage.
40 * validate_attr() - Determine whether an attribute is valid.
41 * with_value() - Test a WITH-VALUE predicate.
45 * Include necessary headers...
48 #include <cups/cups-private.h>
49 #include <cups/file-private.h>
54 #endif /* !O_BINARY */
61 typedef enum _cups_transfer_e
/**** How to send request data ****/
63 _CUPS_TRANSFER_AUTO
, /* Chunk for files, length for static */
64 _CUPS_TRANSFER_CHUNKED
, /* Chunk always */
65 _CUPS_TRANSFER_LENGTH
/* Length always */
68 typedef enum _cups_output_e
/**** Output mode ****/
70 _CUPS_OUTPUT_QUIET
, /* No output */
71 _CUPS_OUTPUT_TEST
, /* Traditional CUPS test output */
72 _CUPS_OUTPUT_PLIST
, /* XML plist test output */
73 _CUPS_OUTPUT_LIST
, /* Tabular list output */
74 _CUPS_OUTPUT_CSV
/* Comma-separated values output */
77 typedef struct _cups_expect_s
/**** Expected attribute info ****/
79 int optional
, /* Optional attribute? */
80 not_expect
; /* Don't expect attribute? */
81 char *name
, /* Attribute name */
82 *of_type
, /* Type name */
83 *same_count_as
, /* Parallel attribute name */
84 *if_defined
, /* Only required if variable defined */
85 *if_not_defined
, /* Only required if variable is not defined */
86 *with_value
, /* Attribute must include this value */
87 *define_match
, /* Variable to define on match */
88 *define_no_match
, /* Variable to define on no-match */
89 *define_value
; /* Variable to define with value */
90 int with_regex
, /* WITH-VALUE is a regular expression */
91 count
; /* Expected count if > 0 */
92 ipp_tag_t in_group
; /* IN-GROUP value */
95 typedef struct _cups_status_s
/**** Status info ****/
97 ipp_status_t status
; /* Expected status code */
98 char *if_defined
, /* Only if variable is defined */
99 *if_not_defined
; /* Only if variable is not defined */
102 typedef struct _cups_var_s
/**** Variable ****/
104 char *name
, /* Name of variable */
105 *value
; /* Value of variable */
108 typedef struct _cups_vars_s
/**** Set of variables ****/
110 const char *uri
, /* URI for printer */
111 *filename
; /* Filename */
112 char scheme
[64], /* Scheme from URI */
113 userpass
[256], /* Username/password from URI */
114 hostname
[256], /* Hostname from URI */
115 resource
[1024]; /* Resource path from URI */
116 int port
; /* Port number from URI */
117 http_encryption_t encryption
; /* Encryption for connection? */
118 cups_array_t
*vars
; /* Array of variables */
126 _cups_transfer_t Transfer
= _CUPS_TRANSFER_AUTO
;
127 /* How to transfer requests */
128 _cups_output_t Output
= _CUPS_OUTPUT_LIST
;
130 int IgnoreErrors
= 0, /* Ignore errors? */
131 Verbosity
= 0, /* Show all attributes? */
132 Version
= 11, /* Default IPP version */
133 XMLHeader
= 0; /* 1 if header is written */
134 char *Password
= NULL
; /* Password from URI */
135 const char * const URIStatusStrings
[] = /* URI status strings */
138 "Bad arguments to function",
139 "Bad resource in URI",
140 "Bad port number in URI",
141 "Bad hostname/address in URI",
142 "Bad username in URI",
146 "Missing scheme in URI",
147 "Unknown scheme in URI",
148 "Missing resource in URI"
156 static int compare_vars(_cups_var_t
*a
, _cups_var_t
*b
);
157 static int do_tests(_cups_vars_t
*vars
, const char *testfile
);
158 static void expand_variables(_cups_vars_t
*vars
, char *dst
, const char *src
,
161 __attribute((nonnull(1,2,3)))
162 #endif /* __GNUC__ */
164 static int expect_matches(_cups_expect_t
*expect
, ipp_tag_t value_tag
);
165 static ipp_t
*get_collection(_cups_vars_t
*vars
, FILE *fp
, int *linenum
);
166 static char *get_filename(const char *testfile
, char *dst
, const char *src
,
168 static char *get_token(FILE *fp
, char *buf
, int buflen
,
170 static char *get_variable(_cups_vars_t
*vars
, const char *name
);
171 static char *iso_date(ipp_uchar_t
*date
);
172 static const char *password_cb(const char *prompt
);
173 static void print_attr(ipp_attribute_t
*attr
);
174 static void print_col(ipp_t
*col
);
175 static void print_csv(ipp_attribute_t
*attr
, int num_displayed
,
176 char **displayed
, size_t *widths
);
177 static void print_fatal_error(const char *s
, ...)
179 __attribute__ ((__format__ (__printf__
, 1, 2)))
180 #endif /* __GNUC__ */
182 static void print_line(ipp_attribute_t
*attr
, int num_displayed
,
183 char **displayed
, size_t *widths
);
184 static void print_test_error(const char *s
, ...)
186 __attribute__ ((__format__ (__printf__
, 1, 2)))
187 #endif /* __GNUC__ */
189 static void print_xml_header(void);
190 static void print_xml_string(const char *element
, const char *s
);
191 static void print_xml_trailer(int success
, const char *message
);
192 static void set_variable(_cups_vars_t
*vars
, const char *name
,
194 static void usage(void);
195 static int validate_attr(ipp_attribute_t
*attr
, int print
);
196 static int with_value(char *value
, int regex
, ipp_attribute_t
*attr
,
201 * 'main()' - Parse options and do tests.
204 int /* O - Exit status */
205 main(int argc
, /* I - Number of command-line args */
206 char *argv
[]) /* I - Command-line arguments */
208 int i
; /* Looping var */
209 int status
; /* Status of tests... */
210 char *opt
, /* Current option */
211 name
[1024], /* Name/value buffer */
212 *value
, /* Pointer to value */
213 filename
[1024], /* Real filename */
214 testname
[1024]; /* Real test filename */
215 const char *testfile
; /* Test file to use */
216 int interval
, /* Test interval */
217 repeat
; /* Repeat count */
218 _cups_vars_t vars
; /* Variables */
219 http_uri_status_t uri_status
; /* URI separation status */
220 _cups_globals_t
*cg
= _cupsGlobals();
226 * Initialize the locale and variables...
229 _cupsSetLocale(argv
);
231 memset(&vars
, 0, sizeof(vars
));
232 vars
.vars
= cupsArrayNew((cups_array_func_t
)compare_vars
, NULL
);
237 * ipptool URI testfile
245 for (i
= 1; i
< argc
; i
++)
247 if (argv
[i
][0] == '-')
249 for (opt
= argv
[i
] + 1; *opt
; opt
++)
253 case 'C' : /* Enable HTTP chunking */
254 Transfer
= _CUPS_TRANSFER_CHUNKED
;
257 case 'E' : /* Encrypt with TLS */
259 vars
.encryption
= HTTP_ENCRYPT_REQUIRED
;
261 _cupsLangPrintf(stderr
,
262 _("%s: Sorry, no encryption support compiled in\n"),
264 #endif /* HAVE_SSL */
267 case 'I' : /* Ignore errors */
271 case 'L' : /* Disable HTTP chunking */
272 Transfer
= _CUPS_TRANSFER_LENGTH
;
275 case 'S' : /* Encrypt with SSL */
277 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
279 _cupsLangPrintf(stderr
,
280 _("%s: Sorry, no encryption support compiled in\n"),
282 #endif /* HAVE_SSL */
285 case 'V' : /* Set IPP version */
290 _cupsLangPuts(stderr
,
291 _("ipptool: Missing version for \"-V\".\n"));
295 if (!strcmp(argv
[i
], "1.0"))
297 else if (!strcmp(argv
[i
], "1.1"))
299 else if (!strcmp(argv
[i
], "2.0"))
301 else if (!strcmp(argv
[i
], "2.1"))
303 else if (!strcmp(argv
[i
], "2.2"))
307 _cupsLangPrintf(stderr
,
308 _("ipptool: Bad version %s for \"-V\".\n"),
314 case 'X' : /* Produce XML output */
315 Output
= _CUPS_OUTPUT_PLIST
;
317 if (interval
|| repeat
)
319 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are "
320 "incompatible with -X\".\n"));
325 case 'c' : /* CSV output */
326 Output
= _CUPS_OUTPUT_CSV
;
329 case 'd' : /* Define a variable */
334 _cupsLangPuts(stderr
,
335 _("ipptool: Missing name=value for \"-d\".\n"));
339 strlcpy(name
, argv
[i
], sizeof(name
));
340 if ((value
= strchr(name
, '=')) != NULL
)
343 value
= name
+ strlen(name
);
345 set_variable(&vars
, name
, value
);
348 case 'f' : /* Set the default test filename */
353 _cupsLangPuts(stderr
,
354 _("ipptool: Missing filename for \"-f\".\n"));
358 if (access(argv
[i
], 0) && argv
[i
][0] != '/')
360 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s",
361 cg
->cups_datadir
, argv
[i
]);
362 if (access(argv
[i
], 0))
363 vars
.filename
= argv
[i
];
365 vars
.filename
= filename
;
368 vars
.filename
= argv
[i
];
371 case 'i' : /* Test every N seconds */
376 _cupsLangPuts(stderr
,
377 _("ipptool: Missing seconds for \"-i\".\n"));
381 interval
= atoi(argv
[i
]);
383 if (Output
== _CUPS_OUTPUT_PLIST
&& interval
)
385 _cupsLangPuts(stderr
, _("ipptool: \"-i\" is incompatible with "
391 case 'l' : /* List as a table */
392 Output
= _CUPS_OUTPUT_LIST
;
395 case 'n' : /* Repeat count */
400 _cupsLangPuts(stderr
,
401 _("ipptool: Missing count for \"-n\".\n"));
405 repeat
= atoi(argv
[i
]);
407 if (Output
== _CUPS_OUTPUT_PLIST
&& repeat
)
409 _cupsLangPuts(stderr
, _("ipptool: \"-n\" is incompatible with "
415 case 'q' : /* Be quiet */
416 Output
= _CUPS_OUTPUT_QUIET
;
419 case 't' : /* CUPS test output */
420 Output
= _CUPS_OUTPUT_TEST
;
423 case 'v' : /* Be verbose */
428 _cupsLangPrintf(stderr
, _("ipptool: Unknown option \"-%c\".\n"),
435 else if (!strncmp(argv
[i
], "ipp://", 6) || !strncmp(argv
[i
], "http://", 7)
437 || !strncmp(argv
[i
], "ipps://", 7)
438 || !strncmp(argv
[i
], "https://", 8)
439 #endif /* HAVE_SSL */
448 _cupsLangPuts(stderr
, _("ipptool: May only specify a single URI.\n"));
453 if (!strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8))
454 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
455 #endif /* HAVE_SSL */
458 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, vars
.uri
,
459 vars
.scheme
, sizeof(vars
.scheme
),
460 vars
.userpass
, sizeof(vars
.userpass
),
461 vars
.hostname
, sizeof(vars
.hostname
),
463 vars
.resource
, sizeof(vars
.resource
));
465 if (uri_status
!= HTTP_URI_OK
)
467 _cupsLangPrintf(stderr
, _("ipptool: Bad URI - %s.\n"),
468 URIStatusStrings
[uri_status
- HTTP_URI_OVERFLOW
]);
472 if (vars
.userpass
[0])
474 if ((Password
= strchr(vars
.userpass
, ':')) != NULL
)
477 cupsSetUser(vars
.userpass
);
478 cupsSetPasswordCB(password_cb
);
479 set_variable(&vars
, "uriuser", vars
.userpass
);
490 _cupsLangPuts(stderr
, _("ipptool: URI required before test file."));
494 if (access(argv
[i
], 0) && argv
[i
][0] != '/')
496 snprintf(testname
, sizeof(testname
), "%s/ipptool/%s", cg
->cups_datadir
,
498 if (access(testname
, 0))
506 if (!do_tests(&vars
, testfile
))
511 if (!vars
.uri
|| !testfile
)
515 * Loop if the interval is set...
518 if (Output
== _CUPS_OUTPUT_PLIST
)
519 print_xml_trailer(!status
, NULL
);
520 else if (interval
&& repeat
> 0)
525 do_tests(&vars
, testfile
);
534 do_tests(&vars
, testfile
);
547 * 'compare_vars()' - Compare two variables.
550 static int /* O - Result of comparison */
551 compare_vars(_cups_var_t
*a
, /* I - First variable */
552 _cups_var_t
*b
) /* I - Second variable */
554 return (strcasecmp(a
->name
, b
->name
));
559 * 'do_tests()' - Do tests as specified in the test file.
562 static int /* 1 = success, 0 = failure */
563 do_tests(_cups_vars_t
*vars
, /* I - Variables */
564 const char *testfile
) /* I - Test file to use */
566 int i
, /* Looping var */
567 linenum
, /* Current line number */
568 pass
, /* Did we pass the test? */
569 prev_pass
= 1, /* Did we pass the previous test? */
570 request_id
, /* Current request ID */
571 show_header
= 1, /* Show the test header? */
572 ignore_errors
, /* Ignore test failures? */
573 skip_previous
= 0; /* Skip on previous test failure? */
574 http_t
*http
= NULL
; /* HTTP connection to server */
575 FILE *fp
= NULL
; /* Test file */
576 char resource
[512], /* Resource for request */
577 token
[1024], /* Token from file */
578 *tokenptr
, /* Pointer into token */
579 temp
[1024]; /* Temporary string */
580 ipp_t
*request
= NULL
; /* IPP request */
581 ipp_t
*response
= NULL
; /* IPP response */
582 char attr
[128]; /* Attribute name */
583 ipp_op_t op
; /* Operation */
584 ipp_tag_t group
; /* Current group */
585 ipp_tag_t value
; /* Current value type */
586 ipp_attribute_t
*attrptr
, /* Attribute pointer */
587 *found
, /* Found attribute */
588 *lastcol
= NULL
; /* Last collection attribute */
589 char name
[1024]; /* Name of test */
590 char filename
[1024]; /* Filename */
591 _cups_transfer_t transfer
; /* To chunk or not to chunk */
592 int version
, /* IPP version number to use */
593 skip_test
; /* Skip this test? */
594 int num_statuses
= 0; /* Number of valid status codes */
595 _cups_status_t statuses
[100], /* Valid status codes */
596 *last_status
; /* Last STATUS (for predicates) */
597 int num_expects
= 0; /* Number of expected attributes */
598 _cups_expect_t expects
[200], /* Expected attributes */
599 *expect
, /* Current expected attribute */
600 *last_expect
; /* Last EXPECT (for predicates) */
601 int num_displayed
= 0; /* Number of displayed attributes */
602 char *displayed
[200]; /* Displayed attributes */
603 size_t widths
[200]; /* Width of columns */
607 * Open the test file...
610 if ((fp
= fopen(testfile
, "r")) == NULL
)
612 print_fatal_error("Unable to open test file %s - %s", testfile
,
619 * Connect to the server...
622 if ((http
= httpConnectEncrypt(vars
->hostname
, vars
->port
,
623 vars
->encryption
)) == NULL
)
625 print_fatal_error("Unable to connect to %s on port %d - %s", vars
->hostname
,
626 vars
->port
, strerror(errno
));
635 CUPS_SRAND(time(NULL
));
639 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
641 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
644 * Expect an open brace...
647 if (!strcmp(token
, "DEFINE"))
653 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
654 get_token(fp
, temp
, sizeof(temp
), &linenum
))
656 expand_variables(vars
, token
, temp
, sizeof(token
));
657 set_variable(vars
, attr
, token
);
661 print_fatal_error("Missing DEFINE name and/or value on line %d.",
669 else if (!strcmp(token
, "IGNORE-ERRORS"))
676 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
677 (!strcasecmp(temp
, "yes") || !strcasecmp(temp
, "no")))
679 IgnoreErrors
= !strcasecmp(temp
, "yes");
683 print_fatal_error("Missing IGNORE-ERRORS value on line %d.", linenum
);
690 else if (!strcmp(token
, "INCLUDE"))
697 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
700 * Map the filename to and then run the tests...
703 if (!do_tests(vars
, get_filename(testfile
, filename
, temp
,
714 print_fatal_error("Missing INCLUDE filename on line %d.", linenum
);
722 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
725 * SKIP-IF-DEFINED variable
728 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
730 if (get_variable(vars
, temp
))
735 print_fatal_error("Missing SKIP-IF-DEFINED value on line %d.", linenum
);
740 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
743 * SKIP-IF-NOT-DEFINED variable
746 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
748 if (!get_variable(vars
, temp
))
753 print_fatal_error("Missing SKIP-IF-NOT-DEFINED value on line %d.",
759 else if (!strcmp(token
, "TRANSFER"))
767 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
769 if (!strcmp(temp
, "auto"))
770 Transfer
= _CUPS_TRANSFER_AUTO
;
771 else if (!strcmp(temp
, "chunked"))
772 Transfer
= _CUPS_TRANSFER_CHUNKED
;
773 else if (!strcmp(temp
, "length"))
774 Transfer
= _CUPS_TRANSFER_LENGTH
;
777 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp
,
785 print_fatal_error("Missing TRANSFER value on line %d.", linenum
);
792 else if (!strcmp(token
, "VERSION"))
794 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
796 if (!strcmp(temp
, "1.0"))
798 else if (!strcmp(temp
, "1.1"))
800 else if (!strcmp(temp
, "2.0"))
802 else if (!strcmp(temp
, "2.1"))
804 else if (!strcmp(temp
, "2.2"))
808 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp
, linenum
);
815 print_fatal_error("Missing VERSION number on line %d.", linenum
);
822 else if (strcmp(token
, "{"))
824 print_fatal_error("Unexpected token %s seen on line %d.", token
, linenum
);
830 * Initialize things...
835 if (Output
== _CUPS_OUTPUT_PLIST
)
837 else if (Output
== _CUPS_OUTPUT_TEST
)
838 printf("\"%s\":\n", testfile
);
843 strlcpy(resource
, vars
->resource
, sizeof(resource
));
848 group
= IPP_TAG_ZERO
;
849 ignore_errors
= IgnoreErrors
;
857 strlcpy(name
, testfile
, sizeof(name
));
858 if (strrchr(name
, '.') != NULL
)
859 *strrchr(name
, '.') = '\0';
862 * Parse until we see a close brace...
865 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
867 if (strcasecmp(token
, "COUNT") &&
868 strcasecmp(token
, "DEFINE-MATCH") &&
869 strcasecmp(token
, "DEFINE-NO-MATCH") &&
870 strcasecmp(token
, "DEFINE-VALUE") &&
871 strcasecmp(token
, "IF-DEFINED") &&
872 strcasecmp(token
, "IF-NOT-DEFINED") &&
873 strcasecmp(token
, "IN-GROUP") &&
874 strcasecmp(token
, "OF-TYPE") &&
875 strcasecmp(token
, "SAME-COUNT-AS") &&
876 strcasecmp(token
, "WITH-VALUE"))
879 if (strcasecmp(token
, "IF-DEFINED") &&
880 strcasecmp(token
, "IF-NOT-DEFINED"))
883 if (!strcmp(token
, "}"))
885 else if (!strcmp(token
, "{") && lastcol
)
888 * Another collection value
891 ipp_t
*col
= get_collection(vars
, fp
, &linenum
);
892 /* Collection value */
896 ipp_attribute_t
*tempcol
; /* Pointer to new buffer */
900 * Reallocate memory...
903 if ((tempcol
= realloc(lastcol
, sizeof(ipp_attribute_t
) +
904 (lastcol
->num_values
+ 1) *
905 sizeof(ipp_value_t
))) == NULL
)
907 print_fatal_error("Unable to allocate memory on line %d.", linenum
);
912 if (tempcol
!= lastcol
)
915 * Reset pointers in the list...
919 request
->prev
->next
= tempcol
;
921 request
->attrs
= tempcol
;
923 lastcol
= request
->current
= request
->last
= tempcol
;
926 lastcol
->values
[lastcol
->num_values
].collection
= col
;
927 lastcol
->num_values
++;
935 else if (!strcmp(token
, "DEFINE"))
941 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
942 get_token(fp
, temp
, sizeof(temp
), &linenum
))
944 expand_variables(vars
, token
, temp
, sizeof(token
));
945 set_variable(vars
, attr
, token
);
949 print_fatal_error("Missing DEFINE name and/or value on line %d.",
955 else if (!strcmp(token
, "IGNORE-ERRORS"))
962 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
963 (!strcasecmp(temp
, "yes") || !strcasecmp(temp
, "no")))
965 ignore_errors
= !strcasecmp(temp
, "yes");
969 print_fatal_error("Missing IGNORE-ERRORS value on line %d.", linenum
);
976 else if (!strcasecmp(token
, "NAME"))
982 get_token(fp
, name
, sizeof(name
), &linenum
);
984 else if (!strcmp(token
, "REQUEST-ID"))
991 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
993 if (isdigit(temp
[0] & 255))
994 request_id
= atoi(temp
);
995 else if (!strcasecmp(temp
, "random"))
996 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
999 print_fatal_error("Bad REQUEST-ID value \"%s\" on line %d.", temp
,
1007 print_fatal_error("Missing REQUEST-ID value on line %d.", linenum
);
1012 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1015 * SKIP-IF-DEFINED variable
1018 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1020 if (get_variable(vars
, temp
))
1025 print_fatal_error("Missing SKIP-IF-DEFINED value on line %d.",
1031 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1034 * SKIP-IF-NOT-DEFINED variable
1037 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1039 if (!get_variable(vars
, temp
))
1044 print_fatal_error("Missing SKIP-IF-NOT-DEFINED value on line %d.",
1050 else if (!strcmp(token
, "SKIP-PREVIOUS-ERROR"))
1053 * SKIP-PREVIOUS-ERROR yes
1054 * SKIP-PREVIOUS-ERROR no
1057 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1058 (!strcasecmp(temp
, "yes") || !strcasecmp(temp
, "no")))
1060 skip_previous
= !strcasecmp(temp
, "yes");
1064 print_fatal_error("Missing SKIP-PREVIOUS-ERROR value on line %d.", linenum
);
1071 else if (!strcmp(token
, "TRANSFER"))
1079 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1081 if (!strcmp(temp
, "auto"))
1082 transfer
= _CUPS_TRANSFER_AUTO
;
1083 else if (!strcmp(temp
, "chunked"))
1084 transfer
= _CUPS_TRANSFER_CHUNKED
;
1085 else if (!strcmp(temp
, "length"))
1086 transfer
= _CUPS_TRANSFER_LENGTH
;
1089 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp
,
1097 print_fatal_error("Missing TRANSFER value on line %d.", linenum
);
1102 else if (!strcasecmp(token
, "VERSION"))
1104 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1106 if (!strcmp(temp
, "0.0"))
1108 else if (!strcmp(temp
, "1.0"))
1110 else if (!strcmp(temp
, "1.1"))
1112 else if (!strcmp(temp
, "2.0"))
1114 else if (!strcmp(temp
, "2.1"))
1116 else if (!strcmp(temp
, "2.2"))
1120 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1127 print_fatal_error("Missing VERSION number on line %d.", linenum
);
1132 else if (!strcasecmp(token
, "RESOURCE"))
1138 if (!get_token(fp
, resource
, sizeof(resource
), &linenum
))
1140 print_fatal_error("Missing RESOURCE path on line %d.", linenum
);
1145 else if (!strcasecmp(token
, "OPERATION"))
1151 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1153 print_fatal_error("Missing OPERATION code on line %d.", linenum
);
1158 if ((op
= ippOpValue(token
)) < 0 && (op
= strtol(token
, NULL
, 0)) == 0)
1160 print_fatal_error("Bad OPERATION code \"%s\" on line %d.", token
,
1166 else if (!strcasecmp(token
, "GROUP"))
1169 * Attribute group...
1172 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1174 print_fatal_error("Missing GROUP tag on line %d.", linenum
);
1179 if ((value
= ippTagValue(token
)) < 0)
1181 print_fatal_error("Bad GROUP tag \"%s\" on line %d.", token
, linenum
);
1187 ippAddSeparator(request
);
1191 else if (!strcasecmp(token
, "DELAY"))
1194 * Delay before operation...
1199 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1201 print_fatal_error("Missing DELAY value on line %d.", linenum
);
1206 if ((delay
= atoi(token
)) <= 0)
1208 print_fatal_error("Bad DELAY value \"%s\" on line %d.", token
,
1215 if (Output
== _CUPS_OUTPUT_TEST
)
1216 printf(" [%d second delay]\n", delay
);
1221 else if (!strcasecmp(token
, "ATTR"))
1227 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1229 print_fatal_error("Missing ATTR value tag on line %d.", linenum
);
1234 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
1236 print_fatal_error("Bad ATTR value tag \"%s\" on line %d.", token
,
1242 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
1244 print_fatal_error("Missing ATTR name on line %d.", linenum
);
1249 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1251 print_fatal_error("Missing ATTR value on line %d.", linenum
);
1256 expand_variables(vars
, token
, temp
, sizeof(token
));
1260 case IPP_TAG_BOOLEAN
:
1261 if (!strcasecmp(token
, "true"))
1262 ippAddBoolean(request
, group
, attr
, 1);
1264 ippAddBoolean(request
, group
, attr
, atoi(token
));
1267 case IPP_TAG_INTEGER
:
1269 ippAddInteger(request
, group
, value
, attr
, atoi(token
));
1272 case IPP_TAG_RESOLUTION
:
1274 int xres
, /* X resolution */
1275 yres
; /* Y resolution */
1276 char *ptr
; /* Pointer into value */
1278 xres
= yres
= strtol(token
, (char **)&ptr
, 10);
1279 if (ptr
> token
&& xres
> 0)
1282 yres
= strtol(ptr
+ 1, (char **)&ptr
, 10);
1285 if (ptr
<= token
|| xres
<= 0 || yres
<= 0 || !ptr
||
1286 (strcasecmp(ptr
, "dpi") && strcasecmp(ptr
, "dpc") &&
1287 strcasecmp(ptr
, "other")))
1289 print_fatal_error("Bad resolution value \"%s\" on line %d.",
1295 if (!strcasecmp(ptr
, "dpi"))
1296 ippAddResolution(request
, group
, attr
, IPP_RES_PER_INCH
,
1298 else if (!strcasecmp(ptr
, "dpc"))
1299 ippAddResolution(request
, group
, attr
, IPP_RES_PER_CM
,
1302 ippAddResolution(request
, group
, attr
, (ipp_res_t
)0,
1307 case IPP_TAG_RANGE
:
1309 int lowers
[4], /* Lower value */
1310 uppers
[4], /* Upper values */
1311 num_vals
; /* Number of values */
1314 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
1315 lowers
+ 0, uppers
+ 0,
1316 lowers
+ 1, uppers
+ 1,
1317 lowers
+ 2, uppers
+ 2,
1318 lowers
+ 3, uppers
+ 3);
1320 if ((num_vals
& 1) || num_vals
== 0)
1322 print_fatal_error("Bad rangeOfInteger value \"%s\" on line "
1323 "%d.", token
, linenum
);
1328 ippAddRanges(request
, group
, attr
, num_vals
/ 2, lowers
,
1333 case IPP_TAG_BEGIN_COLLECTION
:
1334 if (!strcmp(token
, "{"))
1336 ipp_t
*col
= get_collection(vars
, fp
, &linenum
);
1337 /* Collection value */
1341 lastcol
= ippAddCollection(request
, group
, attr
, col
);
1352 print_fatal_error("Bad ATTR collection value on line %d.",
1360 print_fatal_error("Unsupported ATTR value tag %s on line %d.",
1361 ippTagString(value
), linenum
);
1365 case IPP_TAG_TEXTLANG
:
1366 case IPP_TAG_NAMELANG
:
1369 case IPP_TAG_KEYWORD
:
1371 case IPP_TAG_URISCHEME
:
1372 case IPP_TAG_CHARSET
:
1373 case IPP_TAG_LANGUAGE
:
1374 case IPP_TAG_MIMETYPE
:
1375 if (!strchr(token
, ','))
1376 ippAddString(request
, group
, value
, attr
, NULL
, token
);
1380 * Multiple string values...
1383 int num_values
; /* Number of values */
1384 char *values
[100], /* Values */
1385 *ptr
; /* Pointer to next value */
1391 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
1394 values
[num_values
] = ptr
;
1398 ippAddStrings(request
, group
, value
, attr
, num_values
,
1399 NULL
, (const char **)values
);
1404 else if (!strcasecmp(token
, "FILE"))
1410 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1412 print_fatal_error("Missing FILE filename on line %d.", linenum
);
1417 expand_variables(vars
, token
, temp
, sizeof(token
));
1418 get_filename(testfile
, filename
, token
, sizeof(filename
));
1420 else if (!strcasecmp(token
, "STATUS"))
1426 if (num_statuses
>= (int)(sizeof(statuses
) / sizeof(statuses
[0])))
1428 print_fatal_error("Too many STATUS's on line %d.", linenum
);
1433 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1435 print_fatal_error("Missing STATUS code on line %d.", linenum
);
1440 if ((statuses
[num_statuses
].status
= ippErrorValue(token
)) < 0 &&
1441 (statuses
[num_statuses
].status
= strtol(token
, NULL
, 0)) == 0)
1443 print_fatal_error("Bad STATUS code \"%s\" on line %d.", token
,
1449 last_status
= statuses
+ num_statuses
;
1452 last_status
->if_defined
= NULL
;
1453 last_status
->if_not_defined
= NULL
;
1455 else if (!strcasecmp(token
, "EXPECT"))
1458 * Expected attributes...
1461 if (num_expects
>= (int)(sizeof(expects
) / sizeof(expects
[0])))
1463 print_fatal_error("Too many EXPECT's on line %d.", linenum
);
1468 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1470 print_fatal_error("Missing EXPECT name on line %d.", linenum
);
1475 last_expect
= expects
+ num_expects
;
1478 memset(last_expect
, 0, sizeof(_cups_expect_t
));
1480 if (token
[0] == '!')
1482 last_expect
->not_expect
= 1;
1483 last_expect
->name
= strdup(token
+ 1);
1485 else if (token
[0] == '?')
1487 last_expect
->optional
= 1;
1488 last_expect
->name
= strdup(token
+ 1);
1491 last_expect
->name
= strdup(token
);
1493 else if (!strcasecmp(token
, "COUNT"))
1495 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1497 print_fatal_error("Missing COUNT number on line %d.", linenum
);
1502 if ((i
= atoi(token
)) <= 0)
1504 print_fatal_error("Bad COUNT \"%s\" on line %d.", token
, linenum
);
1510 last_expect
->count
= i
;
1513 print_fatal_error("COUNT without a preceding EXPECT on line %d.",
1519 else if (!strcasecmp(token
, "DEFINE-MATCH"))
1521 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1523 print_fatal_error("Missing DEFINE-MATCH variable on line %d.",
1530 last_expect
->define_match
= strdup(token
);
1533 print_fatal_error("DEFINE-MATCH without a preceding EXPECT on line "
1539 else if (!strcasecmp(token
, "DEFINE-NO-MATCH"))
1541 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1543 print_fatal_error("Missing DEFINE-NO-MATCH variable on line %d.",
1550 last_expect
->define_no_match
= strdup(token
);
1553 print_fatal_error("DEFINE-NO-MATCH without a preceding EXPECT on "
1554 "line %d.", linenum
);
1559 else if (!strcasecmp(token
, "DEFINE-VALUE"))
1561 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1563 print_fatal_error("Missing DEFINE-VALUE variable on line %d.",
1570 last_expect
->define_value
= strdup(token
);
1573 print_fatal_error("DEFINE-VALUE without a preceding EXPECT on line "
1579 else if (!strcasecmp(token
, "OF-TYPE"))
1581 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1583 print_fatal_error("Missing OF-TYPE value tag(s) on line %d.",
1590 last_expect
->of_type
= strdup(token
);
1593 print_fatal_error("OF-TYPE without a preceding EXPECT on line %d.",
1599 else if (!strcasecmp(token
, "IN-GROUP"))
1601 ipp_tag_t in_group
; /* IN-GROUP value */
1604 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1606 print_fatal_error("Missing IN-GROUP group tag on line %d.", linenum
);
1611 if ((in_group
= ippTagValue(token
)) == (ipp_tag_t
)-1)
1614 else if (last_expect
)
1615 last_expect
->in_group
= in_group
;
1618 print_fatal_error("IN-GROUP without a preceding EXPECT on line %d.",
1624 else if (!strcasecmp(token
, "SAME-COUNT-AS"))
1626 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1628 print_fatal_error("Missing SAME-COUNT-AS name on line %d.", linenum
);
1634 last_expect
->same_count_as
= strdup(token
);
1637 print_fatal_error("SAME-COUNT-AS without a preceding EXPECT on line "
1643 else if (!strcasecmp(token
, "IF-DEFINED"))
1645 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1647 print_fatal_error("Missing IF-DEFINED name on line %d.", linenum
);
1653 last_expect
->if_defined
= strdup(token
);
1654 else if (last_status
)
1655 last_status
->if_defined
= strdup(token
);
1658 print_fatal_error("IF-DEFINED without a preceding EXPECT or STATUS "
1659 "on line %d.", linenum
);
1664 else if (!strcasecmp(token
, "IF-NOT-DEFINED"))
1666 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1668 print_fatal_error("Missing IF-NOT-DEFINED name on line %d.", linenum
);
1674 last_expect
->if_not_defined
= strdup(token
);
1675 else if (last_status
)
1676 last_status
->if_not_defined
= strdup(token
);
1679 print_fatal_error("IF-NOT-DEFINED without a preceding EXPECT or STATUS "
1680 "on line %d.", linenum
);
1685 else if (!strcasecmp(token
, "WITH-VALUE"))
1687 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1689 print_fatal_error("Missing WITH-VALUE value on line %d.", linenum
);
1697 * Expand any variables in the value and then save it.
1700 expand_variables(vars
, token
, temp
, sizeof(token
));
1702 tokenptr
= token
+ strlen(token
) - 1;
1704 if (token
[0] == '/' && tokenptr
> token
&& *tokenptr
== '/')
1707 * WITH-VALUE is a POSIX extended regular expression.
1710 last_expect
->with_value
= calloc(1, tokenptr
- token
);
1711 last_expect
->with_regex
= 1;
1713 if (last_expect
->with_value
)
1714 memcpy(last_expect
->with_value
, token
+ 1, tokenptr
- token
- 1);
1719 * WITH-VALUE is a literal value...
1722 last_expect
->with_value
= strdup(token
);
1727 print_fatal_error("WITH-VALUE without a preceding EXPECT on line %d.",
1733 else if (!strcasecmp(token
, "DISPLAY"))
1736 * Display attributes...
1739 if (num_displayed
>= (int)(sizeof(displayed
) / sizeof(displayed
[0])))
1741 print_fatal_error("Too many DISPLAY's on line %d", linenum
);
1746 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1748 print_fatal_error("Missing DISPLAY name on line %d.", linenum
);
1753 displayed
[num_displayed
] = strdup(token
);
1758 print_fatal_error("Unexpected token %s seen on line %d.", token
,
1766 * Submit the IPP request...
1769 request
->request
.op
.version
[0] = version
/ 10;
1770 request
->request
.op
.version
[1] = version
% 10;
1771 request
->request
.op
.operation_id
= op
;
1772 request
->request
.op
.request_id
= request_id
;
1774 if (Output
== _CUPS_OUTPUT_PLIST
)
1777 puts("<key>Name</key>");
1778 print_xml_string("string", name
);
1779 puts("<key>Operation</key>");
1780 print_xml_string("string", ippOpString(op
));
1781 puts("<key>RequestAttributes</key>");
1783 for (attrptr
= request
->attrs
; attrptr
; attrptr
= attrptr
->next
)
1784 print_attr(attrptr
);
1787 else if (Output
== _CUPS_OUTPUT_TEST
)
1791 printf(" %s:\n", ippOpString(op
));
1793 for (attrptr
= request
->attrs
; attrptr
; attrptr
= attrptr
->next
)
1794 print_attr(attrptr
);
1797 printf(" %-69.69s [", name
);
1801 if ((skip_previous
&& !prev_pass
) || skip_test
)
1806 if (Output
== _CUPS_OUTPUT_PLIST
)
1808 puts("<key>Successful</key>");
1810 puts("<key>StatusCode</key>");
1811 print_xml_string("string", "skip");
1812 puts("<key>ResponseAttributes</key>");
1816 else if (Output
== _CUPS_OUTPUT_TEST
)
1822 if (transfer
== _CUPS_TRANSFER_CHUNKED
||
1823 (transfer
== _CUPS_TRANSFER_AUTO
&& filename
[0]))
1826 * Send request using chunking...
1829 http_status_t status
= cupsSendRequest(http
, request
, resource
, 0);
1831 if (status
== HTTP_CONTINUE
&& filename
[0])
1833 int fd
; /* File to send */
1834 char buffer
[8192]; /* Copy buffer */
1835 ssize_t bytes
; /* Bytes read/written */
1837 if ((fd
= open(filename
, O_RDONLY
| O_BINARY
)) >= 0)
1839 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
1840 if ((status
= cupsWriteRequestData(http
, buffer
,
1841 bytes
)) != HTTP_CONTINUE
)
1846 snprintf(buffer
, sizeof(buffer
), "%s: %s", filename
, strerror(errno
));
1847 _cupsSetError(IPP_INTERNAL_ERROR
, buffer
, 0);
1849 status
= HTTP_ERROR
;
1855 if (status
== HTTP_CONTINUE
)
1856 response
= cupsGetResponse(http
, resource
);
1860 else if (filename
[0])
1861 response
= cupsDoFileRequest(http
, request
, resource
, filename
);
1863 response
= cupsDoRequest(http
, request
, resource
);
1869 prev_pass
= pass
= 0;
1872 if (http
->version
!= HTTP_1_1
)
1873 prev_pass
= pass
= 0;
1875 if (response
->request
.status
.request_id
!= request_id
)
1876 prev_pass
= pass
= 0;
1879 (response
->request
.status
.version
[0] != (version
/ 10) ||
1880 response
->request
.status
.version
[1] != (version
% 10)))
1881 prev_pass
= pass
= 0;
1883 if ((attrptr
= ippFindAttribute(response
, "job-id",
1884 IPP_TAG_INTEGER
)) != NULL
)
1886 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
1887 set_variable(vars
, "job-id", temp
);
1890 if ((attrptr
= ippFindAttribute(response
, "job-uri",
1891 IPP_TAG_URI
)) != NULL
)
1892 set_variable(vars
, "job-uri", attrptr
->values
[0].string
.text
);
1894 if ((attrptr
= ippFindAttribute(response
, "notify-subscription-id",
1895 IPP_TAG_INTEGER
)) != NULL
)
1897 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
1898 set_variable(vars
, "notify-subscription-id", temp
);
1901 attrptr
= response
->attrs
;
1902 if (!attrptr
|| !attrptr
->name
||
1903 attrptr
->value_tag
!= IPP_TAG_CHARSET
||
1904 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
1905 attrptr
->num_values
!= 1 ||
1906 strcmp(attrptr
->name
, "attributes-charset"))
1907 prev_pass
= pass
= 0;
1911 attrptr
= attrptr
->next
;
1912 if (!attrptr
|| !attrptr
->name
||
1913 attrptr
->value_tag
!= IPP_TAG_LANGUAGE
||
1914 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
1915 attrptr
->num_values
!= 1 ||
1916 strcmp(attrptr
->name
, "attributes-natural-language"))
1917 prev_pass
= pass
= 0;
1920 if ((attrptr
= ippFindAttribute(response
, "status-message",
1921 IPP_TAG_ZERO
)) != NULL
&&
1922 (attrptr
->value_tag
!= IPP_TAG_TEXT
||
1923 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
1924 attrptr
->num_values
!= 1 ||
1925 (attrptr
->value_tag
== IPP_TAG_TEXT
&&
1926 strlen(attrptr
->values
[0].string
.text
) > 255)))
1927 prev_pass
= pass
= 0;
1929 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
1930 IPP_TAG_ZERO
)) != NULL
&&
1931 (attrptr
->value_tag
!= IPP_TAG_TEXT
||
1932 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
1933 attrptr
->num_values
!= 1 ||
1934 (attrptr
->value_tag
== IPP_TAG_TEXT
&&
1935 strlen(attrptr
->values
[0].string
.text
) > 1023)))
1936 prev_pass
= pass
= 0;
1938 for (attrptr
= response
->attrs
, group
= attrptr
->group_tag
;
1940 attrptr
= attrptr
->next
)
1942 if (attrptr
->group_tag
< group
&& attrptr
->group_tag
!= IPP_TAG_ZERO
)
1944 prev_pass
= pass
= 0;
1948 if (!validate_attr(attrptr
, 0))
1950 prev_pass
= pass
= 0;
1955 for (i
= 0; i
< num_statuses
; i
++)
1957 if (statuses
[i
].if_defined
&&
1958 !get_variable(vars
, statuses
[i
].if_defined
))
1961 if (statuses
[i
].if_not_defined
&&
1962 get_variable(vars
, statuses
[i
].if_not_defined
))
1965 if (response
->request
.status
.status_code
== statuses
[i
].status
)
1969 if (i
== num_statuses
&& num_statuses
> 0)
1970 prev_pass
= pass
= 0;
1973 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
1975 if (expect
->if_defined
&& !get_variable(vars
, expect
->if_defined
))
1978 if (expect
->if_not_defined
&&
1979 get_variable(vars
, expect
->if_not_defined
))
1982 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
1984 if ((found
&& expect
->not_expect
) ||
1985 (!found
&& !(expect
->not_expect
|| expect
->optional
)) ||
1986 (found
&& !expect_matches(expect
, found
->value_tag
)) ||
1987 (found
&& expect
->in_group
&&
1988 found
->group_tag
!= expect
->in_group
))
1990 if (expect
->define_no_match
)
1991 set_variable(vars
, expect
->define_no_match
, "1");
1992 else if (!expect
->define_match
)
1993 prev_pass
= pass
= 0;
1999 !with_value(expect
->with_value
, expect
->with_regex
, found
, 0))
2001 if (expect
->define_no_match
)
2002 set_variable(vars
, expect
->define_no_match
, "1");
2003 else if (!expect
->define_match
)
2004 prev_pass
= pass
= 0;
2009 if (found
&& expect
->count
> 0 && found
->num_values
!= expect
->count
)
2011 if (expect
->define_no_match
)
2012 set_variable(vars
, expect
->define_no_match
, "1");
2013 else if (!expect
->define_match
)
2014 prev_pass
= pass
= 0;
2019 if (found
&& expect
->same_count_as
)
2021 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
2024 if (!attrptr
|| attrptr
->num_values
!= found
->num_values
)
2026 if (expect
->define_no_match
)
2027 set_variable(vars
, expect
->define_no_match
, "1");
2028 else if (!expect
->define_match
)
2029 prev_pass
= pass
= 0;
2035 if (found
&& expect
->define_match
)
2036 set_variable(vars
, expect
->define_match
, "1");
2038 if (found
&& expect
->define_value
)
2040 _ippAttrString(found
, token
, sizeof(token
));
2041 set_variable(vars
, expect
->define_value
, token
);
2047 if (Output
== _CUPS_OUTPUT_PLIST
)
2049 puts("<key>Successful</key>");
2050 puts(prev_pass
? "<true />" : "<false />");
2051 puts("<key>StatusCode</key>");
2052 print_xml_string("string", ippErrorString(cupsLastError()));
2053 puts("<key>ResponseAttributes</key>");
2055 for (attrptr
= response
? response
->attrs
: NULL
;
2057 attrptr
= attrptr
->next
)
2058 print_attr(attrptr
);
2061 else if (Output
== _CUPS_OUTPUT_TEST
)
2063 puts(prev_pass
? "PASS]" : "FAIL]");
2065 if (Verbosity
&& response
)
2067 printf(" RECEIVED: %lu bytes in response\n",
2068 (unsigned long)ippLength(response
));
2069 printf(" status-code = %x (%s)\n", cupsLastError(),
2070 ippErrorString(cupsLastError()));
2072 for (attrptr
= response
->attrs
;
2074 attrptr
= attrptr
->next
)
2076 print_attr(attrptr
);
2080 else if (!prev_pass
)
2081 fprintf(stderr
, "%s\n", cupsLastErrorString());
2083 if (prev_pass
&& Output
!= _CUPS_OUTPUT_PLIST
&&
2084 Output
!= _CUPS_OUTPUT_QUIET
&& !Verbosity
&& num_displayed
> 0)
2086 if (Output
>= _CUPS_OUTPUT_LIST
)
2088 size_t width
; /* Length of value */
2091 for (i
= 0; i
< num_displayed
; i
++)
2093 widths
[i
] = strlen(displayed
[i
]);
2095 for (attrptr
= ippFindAttribute(response
, displayed
[i
], IPP_TAG_ZERO
);
2097 attrptr
= ippFindNextAttribute(response
, displayed
[i
],
2100 width
= _ippAttrString(attrptr
, NULL
, 0);
2101 if (width
> widths
[i
])
2106 if (Output
== _CUPS_OUTPUT_CSV
)
2107 print_csv(NULL
, num_displayed
, displayed
, widths
);
2109 print_line(NULL
, num_displayed
, displayed
, widths
);
2111 attrptr
= response
->attrs
;
2115 while (attrptr
&& attrptr
->group_tag
<= IPP_TAG_OPERATION
)
2116 attrptr
= attrptr
->next
;
2120 if (Output
== _CUPS_OUTPUT_CSV
)
2121 print_csv(attrptr
, num_displayed
, displayed
, widths
);
2123 print_line(attrptr
, num_displayed
, displayed
, widths
);
2125 while (attrptr
&& attrptr
->group_tag
> IPP_TAG_OPERATION
)
2126 attrptr
= attrptr
->next
;
2132 for (attrptr
= response
->attrs
;
2134 attrptr
= attrptr
->next
)
2138 for (i
= 0; i
< num_displayed
; i
++)
2140 if (!strcmp(displayed
[i
], attrptr
->name
))
2142 print_attr(attrptr
);
2150 else if (!prev_pass
)
2152 if (Output
== _CUPS_OUTPUT_PLIST
)
2154 puts("<key>Errors</key>");
2158 if (http
->version
!= HTTP_1_1
)
2159 print_test_error("Bad HTTP version (%d.%d)", http
->version
/ 100,
2160 http
->version
% 100);
2163 print_test_error("IPP request failed with status %s (%s)",
2164 ippErrorString(cupsLastError()),
2165 cupsLastErrorString());
2169 (response
->request
.status
.version
[0] != (version
/ 10) ||
2170 response
->request
.status
.version
[1] != (version
% 10)))
2171 print_test_error("Bad version %d.%d in response - expected %d.%d "
2172 "(RFC 2911 section 3.1.8).",
2173 response
->request
.status
.version
[0],
2174 response
->request
.status
.version
[1],
2175 version
/ 10, version
% 10);
2177 if (response
->request
.status
.request_id
!= request_id
)
2178 print_test_error("Bad request ID %d in response - expected %d "
2179 "(RFC 2911 section 3.1.1)",
2180 response
->request
.status
.request_id
, request_id
);
2182 attrptr
= response
->attrs
;
2184 print_test_error("Missing first attribute \"attributes-charset "
2185 "(charset)\" in group operation-attributes-tag "
2186 "(RFC 2911 section 3.1.4).");
2189 if (!attrptr
->name
||
2190 attrptr
->value_tag
!= IPP_TAG_CHARSET
||
2191 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2192 attrptr
->num_values
!= 1 ||
2193 strcmp(attrptr
->name
, "attributes-charset"))
2194 print_test_error("Bad first attribute \"%s (%s%s)\" in group %s, "
2195 "expected \"attributes-charset (charset)\" in "
2196 "group operation-attributes-tag (RFC 2911 section "
2198 attrptr
->name
? attrptr
->name
: "(null)",
2199 attrptr
->num_values
> 1 ? "1setOf " : "",
2200 ippTagString(attrptr
->value_tag
),
2201 ippTagString(attrptr
->group_tag
));
2203 attrptr
= attrptr
->next
;
2205 print_test_error("Missing second attribute \"attributes-natural-"
2206 "language (naturalLanguage)\" in group "
2207 "operation-attributes-tag (RFC 2911 section "
2209 else if (!attrptr
->name
||
2210 attrptr
->value_tag
!= IPP_TAG_LANGUAGE
||
2211 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2212 attrptr
->num_values
!= 1 ||
2213 strcmp(attrptr
->name
, "attributes-natural-language"))
2214 print_test_error("Bad first attribute \"%s (%s%s)\" in group %s, "
2215 "expected \"attributes-natural-language "
2216 "(naturalLanguage)\" in group "
2217 "operation-attributes-tag (RFC 2911 section "
2219 attrptr
->name
? attrptr
->name
: "(null)",
2220 attrptr
->num_values
> 1 ? "1setOf " : "",
2221 ippTagString(attrptr
->value_tag
),
2222 ippTagString(attrptr
->group_tag
));
2225 if ((attrptr
= ippFindAttribute(response
, "status-message",
2226 IPP_TAG_ZERO
)) != NULL
)
2228 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2229 print_test_error("status-message (text(255)) has wrong value tag "
2230 "%s (RFC 2911 section 3.1.6.2).",
2231 ippTagString(attrptr
->value_tag
));
2232 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2233 print_test_error("status-message (text(255)) has wrong group tag "
2234 "%s (RFC 2911 section 3.1.6.2).",
2235 ippTagString(attrptr
->group_tag
));
2236 if (attrptr
->num_values
!= 1)
2237 print_test_error("status-message (text(255)) has %d values "
2238 "(RFC 2911 section 3.1.6.2).",
2239 attrptr
->num_values
);
2240 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2241 strlen(attrptr
->values
[0].string
.text
) > 255)
2242 print_test_error("status-message (text(255)) has bad length %d"
2243 " (RFC 2911 section 3.1.6.2).",
2244 (int)strlen(attrptr
->values
[0].string
.text
));
2247 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
2248 IPP_TAG_ZERO
)) != NULL
)
2250 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2251 print_test_error("detailed-status-message (text(MAX)) has wrong "
2252 "value tag %s (RFC 2911 section 3.1.6.3).",
2253 ippTagString(attrptr
->value_tag
));
2254 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2255 print_test_error("detailed-status-message (text(MAX)) has wrong "
2256 "group tag %s (RFC 2911 section 3.1.6.3).",
2257 ippTagString(attrptr
->group_tag
));
2258 if (attrptr
->num_values
!= 1)
2259 print_test_error("detailed-status-message (text(MAX)) has %d values"
2260 " (RFC 2911 section 3.1.6.3).",
2261 attrptr
->num_values
);
2262 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2263 strlen(attrptr
->values
[0].string
.text
) > 1023)
2264 print_test_error("detailed-status-message (text(MAX)) has bad "
2265 "length %d (RFC 2911 section 3.1.6.3).",
2266 (int)strlen(attrptr
->values
[0].string
.text
));
2269 for (attrptr
= response
->attrs
, group
= attrptr
->group_tag
;
2271 attrptr
= attrptr
->next
)
2273 if (attrptr
->group_tag
< group
&& attrptr
->group_tag
!= IPP_TAG_ZERO
)
2274 print_test_error("Attribute groups out of order (%s < %s)",
2275 ippTagString(attrptr
->group_tag
),
2276 ippTagString(group
));
2278 validate_attr(attrptr
, 1);
2281 for (i
= 0; i
< num_statuses
; i
++)
2283 if (statuses
[i
].if_defined
&&
2284 !get_variable(vars
, statuses
[i
].if_defined
))
2287 if (statuses
[i
].if_not_defined
&&
2288 get_variable(vars
, statuses
[i
].if_not_defined
))
2291 if (response
->request
.status
.status_code
== statuses
[i
].status
)
2295 if (i
== num_statuses
&& num_statuses
> 0)
2297 print_test_error("Bad status-code (%s)",
2298 ippErrorString(cupsLastError()));
2299 print_test_error("status-message=\"%s\"", cupsLastErrorString());
2302 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
2304 if (expect
->define_match
|| expect
->define_no_match
)
2307 if (expect
->if_defined
&& !get_variable(vars
, expect
->if_defined
))
2310 if (expect
->if_not_defined
&&
2311 get_variable(vars
, expect
->if_not_defined
))
2314 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
2316 if (found
&& expect
->not_expect
)
2317 print_test_error("NOT EXPECTED: %s", expect
->name
);
2318 else if (!found
&& !(expect
->not_expect
|| expect
->optional
))
2319 print_test_error("EXPECTED: %s", expect
->name
);
2322 if (!expect_matches(expect
, found
->value_tag
))
2323 print_test_error("EXPECTED: %s OF-TYPE %s (got %s)",
2324 expect
->name
, expect
->of_type
,
2325 ippTagString(found
->value_tag
));
2327 if (expect
->in_group
&& found
->group_tag
!= expect
->in_group
)
2328 print_test_error("EXPECTED: %s IN-GROUP %s (got %s).",
2329 expect
->name
, ippTagString(expect
->in_group
),
2330 ippTagString(found
->group_tag
));
2332 if (!with_value(expect
->with_value
, expect
->with_regex
, found
, 0))
2334 if (expect
->with_regex
)
2335 print_test_error("EXPECTED: %s WITH-VALUE /%s/",
2336 expect
->name
, expect
->with_value
);
2338 print_test_error("EXPECTED: %s WITH-VALUE \"%s\"",
2339 expect
->name
, expect
->with_value
);
2341 with_value(expect
->with_value
, expect
->with_regex
, found
, 1);
2344 if (expect
->count
> 0 && found
->num_values
!= expect
->count
)
2346 print_test_error("EXPECTED: %s COUNT %d (got %d)", expect
->name
,
2347 expect
->count
, found
->num_values
);
2350 if (expect
->same_count_as
)
2352 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
2356 print_test_error("EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2357 "(not returned)", expect
->name
,
2358 found
->num_values
, expect
->same_count_as
);
2359 else if (attrptr
->num_values
!= found
->num_values
)
2360 print_test_error("EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2361 "(%d values)", expect
->name
, found
->num_values
,
2362 expect
->same_count_as
, attrptr
->num_values
);
2368 if (Output
== _CUPS_OUTPUT_PLIST
)
2372 if (Output
== _CUPS_OUTPUT_PLIST
)
2377 ippDelete(response
);
2380 for (i
= 0; i
< num_statuses
; i
++)
2382 if (statuses
[i
].if_defined
)
2383 free(statuses
[i
].if_defined
);
2384 if (statuses
[i
].if_not_defined
)
2385 free(statuses
[i
].if_not_defined
);
2389 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
2392 if (expect
->of_type
)
2393 free(expect
->of_type
);
2394 if (expect
->same_count_as
)
2395 free(expect
->same_count_as
);
2396 if (expect
->if_defined
)
2397 free(expect
->if_defined
);
2398 if (expect
->if_not_defined
)
2399 free(expect
->if_not_defined
);
2400 if (expect
->with_value
)
2401 free(expect
->with_value
);
2402 if (expect
->define_match
)
2403 free(expect
->define_match
);
2404 if (expect
->define_no_match
)
2405 free(expect
->define_no_match
);
2406 if (expect
->define_value
)
2407 free(expect
->define_value
);
2411 for (i
= 0; i
< num_displayed
; i
++)
2415 if (!ignore_errors
&& !prev_pass
)
2426 ippDelete(response
);
2428 for (i
= 0; i
< num_statuses
; i
++)
2430 if (statuses
[i
].if_defined
)
2431 free(statuses
[i
].if_defined
);
2432 if (statuses
[i
].if_not_defined
)
2433 free(statuses
[i
].if_not_defined
);
2436 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
2439 if (expect
->of_type
)
2440 free(expect
->of_type
);
2441 if (expect
->same_count_as
)
2442 free(expect
->same_count_as
);
2443 if (expect
->if_defined
)
2444 free(expect
->if_defined
);
2445 if (expect
->if_not_defined
)
2446 free(expect
->if_not_defined
);
2447 if (expect
->with_value
)
2448 free(expect
->with_value
);
2449 if (expect
->define_match
)
2450 free(expect
->define_match
);
2451 if (expect
->define_no_match
)
2452 free(expect
->define_no_match
);
2453 if (expect
->define_value
)
2454 free(expect
->define_value
);
2457 for (i
= 0; i
< num_displayed
; i
++)
2465 * 'expand_variables()' - Expand variables in a string.
2469 expand_variables(_cups_vars_t
*vars
, /* I - Variables */
2470 char *dst
, /* I - Destination string buffer */
2471 const char *src
, /* I - Source string */
2472 size_t dstsize
) /* I - Size of destination buffer */
2474 char *dstptr
, /* Pointer into destination */
2475 *dstend
, /* End of destination */
2476 temp
[256], /* Temporary string */
2477 *tempptr
; /* Pointer into temporary string */
2478 const char *value
; /* Value to substitute */
2482 dstend
= dst
+ dstsize
- 1;
2484 while (*src
&& dstptr
< dstend
)
2489 * Substitute a string/number...
2492 if (!strncmp(src
, "$$", 2))
2497 else if (!strncmp(src
, "$ENV[", 5))
2499 strlcpy(temp
, src
+ 5, sizeof(temp
));
2501 for (tempptr
= temp
; *tempptr
; tempptr
++)
2502 if (*tempptr
== ']')
2508 value
= getenv(temp
);
2509 src
+= tempptr
- temp
+ 5;
2513 strlcpy(temp
, src
+ 1, sizeof(temp
));
2515 for (tempptr
= temp
; *tempptr
; tempptr
++)
2516 if (!isalnum(*tempptr
& 255) && *tempptr
!= '-' && *tempptr
!= '_')
2522 if (!strcmp(temp
, "uri"))
2524 else if (!strcmp(temp
, "filename"))
2525 value
= vars
->filename
;
2526 else if (!strcmp(temp
, "scheme") || !strcmp(temp
, "method"))
2527 value
= vars
->scheme
;
2528 else if (!strcmp(temp
, "username"))
2529 value
= vars
->userpass
;
2530 else if (!strcmp(temp
, "hostname"))
2531 value
= vars
->hostname
;
2532 else if (!strcmp(temp
, "port"))
2534 snprintf(temp
, sizeof(temp
), "%d", vars
->port
);
2537 else if (!strcmp(temp
, "resource"))
2538 value
= vars
->resource
;
2539 else if (!strcmp(temp
, "user"))
2542 value
= get_variable(vars
, temp
);
2544 src
+= tempptr
- temp
+ 1;
2554 strlcpy(dstptr
, value
, dstend
- dstptr
+ 1);
2555 dstptr
+= strlen(dstptr
);
2567 * 'expect_matches()' - Return true if the tag matches the specification.
2570 static int /* O - 1 if matches, 0 otherwise */
2572 _cups_expect_t
*expect
, /* I - Expected attribute */
2573 ipp_tag_t value_tag
) /* I - Value tag for attribute */
2575 int match
; /* Match? */
2576 char *of_type
, /* Type name to match */
2577 *next
, /* Next name to match */
2578 sep
; /* Separator character */
2582 * If we don't expect a particular type, return immediately...
2585 if (!expect
->of_type
)
2589 * Parse the "of_type" value since the string can contain multiple attribute
2590 * types separated by "," or "|"...
2593 for (of_type
= expect
->of_type
, match
= 0; !match
&& *of_type
; of_type
= next
)
2596 * Find the next separator, and set it (temporarily) to nul if present.
2599 for (next
= of_type
; *next
&& *next
!= '|' && *next
!= ','; next
++);
2601 if ((sep
= *next
) != '\0')
2605 * Support some meta-types to make it easier to write the test file.
2608 if (!strcmp(of_type
, "text"))
2609 match
= value_tag
== IPP_TAG_TEXTLANG
|| value_tag
== IPP_TAG_TEXT
;
2610 else if (!strcmp(of_type
, "name"))
2611 match
= value_tag
== IPP_TAG_NAMELANG
|| value_tag
== IPP_TAG_NAME
;
2612 else if (!strcmp(of_type
, "collection"))
2613 match
= value_tag
== IPP_TAG_BEGIN_COLLECTION
;
2615 match
= value_tag
== ippTagValue(of_type
);
2618 * Restore the separator if we have one...
2630 * 'get_collection()' - Get a collection value from the current test file.
2633 static ipp_t
* /* O - Collection value */
2634 get_collection(_cups_vars_t
*vars
, /* I - Variables */
2635 FILE *fp
, /* I - File to read from */
2636 int *linenum
) /* IO - Line number */
2638 char token
[1024], /* Token from file */
2639 temp
[1024], /* Temporary string */
2640 attr
[128]; /* Attribute name */
2641 ipp_tag_t value
; /* Current value type */
2642 ipp_t
*col
= ippNew(); /* Collection value */
2643 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
2646 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
2648 if (!strcmp(token
, "}"))
2650 else if (!strcmp(token
, "{") && lastcol
)
2653 * Another collection value
2656 ipp_t
*subcol
= get_collection(vars
, fp
, linenum
);
2657 /* Collection value */
2661 ipp_attribute_t
*tempcol
; /* Pointer to new buffer */
2665 * Reallocate memory...
2668 if ((tempcol
= realloc(lastcol
, sizeof(ipp_attribute_t
) +
2669 (lastcol
->num_values
+ 1) *
2670 sizeof(ipp_value_t
))) == NULL
)
2672 print_fatal_error("Unable to allocate memory on line %d.", *linenum
);
2676 if (tempcol
!= lastcol
)
2679 * Reset pointers in the list...
2683 col
->prev
->next
= tempcol
;
2685 col
->attrs
= tempcol
;
2687 lastcol
= col
->current
= col
->last
= tempcol
;
2690 lastcol
->values
[lastcol
->num_values
].collection
= subcol
;
2691 lastcol
->num_values
++;
2696 else if (!strcasecmp(token
, "MEMBER"))
2704 if (!get_token(fp
, token
, sizeof(token
), linenum
))
2706 print_fatal_error("Missing MEMBER value tag on line %d.", *linenum
);
2710 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
2712 print_fatal_error("Bad MEMBER value tag \"%s\" on line %d.", token
,
2717 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
2719 print_fatal_error("Missing MEMBER name on line %d.", *linenum
);
2723 if (!get_token(fp
, temp
, sizeof(temp
), linenum
))
2725 print_fatal_error("Missing MEMBER value on line %d.", *linenum
);
2729 expand_variables(vars
, token
, temp
, sizeof(token
));
2733 case IPP_TAG_BOOLEAN
:
2734 if (!strcasecmp(token
, "true"))
2735 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
2737 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, atoi(token
));
2740 case IPP_TAG_INTEGER
:
2742 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
2745 case IPP_TAG_RESOLUTION
:
2747 int xres
, /* X resolution */
2748 yres
; /* Y resolution */
2749 char units
[6]; /* Units */
2751 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
2752 (strcasecmp(units
, "dpi") && strcasecmp(units
, "dpc") &&
2753 strcasecmp(units
, "other")))
2755 print_fatal_error("Bad resolution value \"%s\" on line %d.",
2760 if (!strcasecmp(units
, "dpi"))
2761 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
2763 else if (!strcasecmp(units
, "dpc"))
2764 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
2767 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
2772 case IPP_TAG_RANGE
:
2774 int lowers
[4], /* Lower value */
2775 uppers
[4], /* Upper values */
2776 num_vals
; /* Number of values */
2779 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
2780 lowers
+ 0, uppers
+ 0,
2781 lowers
+ 1, uppers
+ 1,
2782 lowers
+ 2, uppers
+ 2,
2783 lowers
+ 3, uppers
+ 3);
2785 if ((num_vals
& 1) || num_vals
== 0)
2787 print_fatal_error("Bad rangeOfInteger value \"%s\" on line %d.",
2792 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
2797 case IPP_TAG_BEGIN_COLLECTION
:
2798 if (!strcmp(token
, "{"))
2800 ipp_t
*subcol
= get_collection(vars
, fp
, linenum
);
2801 /* Collection value */
2805 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
2813 print_fatal_error("Bad collection value on line %d.", *linenum
);
2819 if (!strchr(token
, ','))
2820 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
2824 * Multiple string values...
2827 int num_values
; /* Number of values */
2828 char *values
[100], /* Values */
2829 *ptr
; /* Pointer to next value */
2835 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
2838 values
[num_values
] = ptr
;
2842 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
2843 NULL
, (const char **)values
);
2853 * If we get here there was a parse error; free memory and return.
2865 * 'get_filename()' - Get a filename based on the current test file.
2868 static char * /* O - Filename */
2869 get_filename(const char *testfile
, /* I - Current test file */
2870 char *dst
, /* I - Destination filename */
2871 const char *src
, /* I - Source filename */
2872 size_t dstsize
) /* I - Size of destination buffer */
2874 char *dstptr
; /* Pointer into destination */
2875 _cups_globals_t
*cg
= _cupsGlobals();
2879 if (*src
== '<' && src
[strlen(src
) - 1] == '>')
2882 * Map <filename> to CUPS_DATADIR/ipptool/filename...
2885 snprintf(dst
, dstsize
, "%s/ipptool/%s", cg
->cups_datadir
, src
+ 1);
2886 dstptr
= dst
+ strlen(dst
) - 1;
2890 else if (*src
== '/' || !strchr(testfile
, '/'))
2893 * Use the path as-is...
2896 strlcpy(dst
, src
, dstsize
);
2901 * Make path relative to testfile...
2904 strlcpy(dst
, testfile
, dstsize
);
2905 if ((dstptr
= strrchr(dst
, '/')) != NULL
)
2908 dstptr
= dst
; /* Should never happen */
2910 strlcpy(dstptr
, src
, dstsize
- (dstptr
- dst
));
2918 * 'get_token()' - Get a token from a file.
2921 static char * /* O - Token from file or NULL on EOF */
2922 get_token(FILE *fp
, /* I - File to read from */
2923 char *buf
, /* I - Buffer to read into */
2924 int buflen
, /* I - Length of buffer */
2925 int *linenum
) /* IO - Current line number */
2927 int ch
, /* Character from file */
2928 quote
; /* Quoting character */
2929 char *bufptr
, /* Pointer into buffer */
2930 *bufend
; /* End of buffer */
2936 * Skip whitespace...
2939 while (isspace(ch
= getc(fp
)))
2951 else if (ch
== '\'' || ch
== '\"')
2954 * Quoted text or regular expression...
2959 bufend
= buf
+ buflen
- 1;
2961 while ((ch
= getc(fp
)) != EOF
)
2966 * Escape next character...
2969 if (bufptr
< bufend
)
2972 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
2975 else if (ch
== quote
)
2977 else if (bufptr
< bufend
)
2991 while ((ch
= getc(fp
)) != EOF
)
3000 * Whitespace delimited text...
3006 bufend
= buf
+ buflen
- 1;
3008 while ((ch
= getc(fp
)) != EOF
)
3009 if (isspace(ch
) || ch
== '#')
3011 else if (bufptr
< bufend
)
3016 else if (ch
== '\n')
3028 * 'get_variable()' - Get the value of a variable.
3031 static char * /* O - Value or NULL */
3032 get_variable(_cups_vars_t
*vars
, /* I - Variables */
3033 const char *name
) /* I - Variable name */
3035 _cups_var_t key
, /* Search key */
3036 *match
; /* Matching variable, if any */
3039 key
.name
= (char *)name
;
3040 match
= cupsArrayFind(vars
->vars
, &key
);
3042 return (match
? match
->value
: NULL
);
3047 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
3051 static char * /* O - ISO 8601 date/time string */
3052 iso_date(ipp_uchar_t
*date
) /* I - IPP (RFC 1903) date/time value */
3054 unsigned year
= (date
[0] << 8) + date
[1];
3056 static char buffer
[255]; /* String buffer */
3059 if (date
[9] == 0 && date
[10] == 0)
3060 snprintf(buffer
, sizeof(buffer
), "%04u-%02u-%02uT%02u:%02u:%02uZ",
3061 year
, date
[2], date
[3], date
[4], date
[5], date
[6]);
3063 snprintf(buffer
, sizeof(buffer
), "%04u-%02u-%02uT%02u:%02u:%02u%c%02u%02u",
3064 year
, date
[2], date
[3], date
[4], date
[5], date
[6],
3065 date
[8], date
[9], date
[10]);
3072 * 'password_cb()' - Password callback for authenticated tests.
3075 static const char * /* O - Password */
3076 password_cb(const char *prompt
) /* I - Prompt (unused) */
3085 * 'print_attr()' - Print an attribute on the screen.
3089 print_attr(ipp_attribute_t
*attr
) /* I - Attribute to print */
3091 int i
; /* Looping var */
3092 ipp_attribute_t
*colattr
; /* Collection attribute */
3095 if (Output
== _CUPS_OUTPUT_PLIST
)
3099 printf("<key>%s</key>\n<true />\n", ippTagString(attr
->group_tag
));
3103 print_xml_string("key", attr
->name
);
3104 if (attr
->num_values
> 1)
3107 else if (Output
== _CUPS_OUTPUT_TEST
)
3111 puts(" -- separator --");
3115 printf(" %s (%s%s) = ", attr
->name
,
3116 attr
->num_values
> 1 ? "1setOf " : "",
3117 ippTagString(attr
->value_tag
));
3120 switch (attr
->value_tag
)
3122 case IPP_TAG_INTEGER
:
3124 for (i
= 0; i
< attr
->num_values
; i
++)
3125 if (Output
== _CUPS_OUTPUT_PLIST
)
3126 printf("<integer>%d</integer>\n", attr
->values
[i
].integer
);
3128 printf("%d ", attr
->values
[i
].integer
);
3131 case IPP_TAG_BOOLEAN
:
3132 for (i
= 0; i
< attr
->num_values
; i
++)
3133 if (Output
== _CUPS_OUTPUT_PLIST
)
3134 puts(attr
->values
[i
].boolean
? "<true />" : "<false />");
3135 else if (attr
->values
[i
].boolean
)
3136 fputs("true ", stdout
);
3138 fputs("false ", stdout
);
3141 case IPP_TAG_RANGE
:
3142 for (i
= 0; i
< attr
->num_values
; i
++)
3143 if (Output
== _CUPS_OUTPUT_PLIST
)
3144 printf("<dict><key>lower</key><integer>%d</integer>"
3145 "<key>upper</key><integer>%d</integer></dict>\n",
3146 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
3148 printf("%d-%d ", attr
->values
[i
].range
.lower
,
3149 attr
->values
[i
].range
.upper
);
3152 case IPP_TAG_RESOLUTION
:
3153 for (i
= 0; i
< attr
->num_values
; i
++)
3154 if (Output
== _CUPS_OUTPUT_PLIST
)
3155 printf("<dict><key>xres</key><integer>%d</integer>"
3156 "<key>yres</key><integer>%d</integer>"
3157 "<key>units</key><string>%s</string></dict>\n",
3158 attr
->values
[i
].resolution
.xres
,
3159 attr
->values
[i
].resolution
.yres
,
3160 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
3163 printf("%dx%d%s ", attr
->values
[i
].resolution
.xres
,
3164 attr
->values
[i
].resolution
.yres
,
3165 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
3170 for (i
= 0; i
< attr
->num_values
; i
++)
3171 if (Output
== _CUPS_OUTPUT_PLIST
)
3172 printf("<date>%s</date>\n", iso_date(attr
->values
[i
].date
));
3174 printf("%s ", iso_date(attr
->values
[i
].date
));
3177 case IPP_TAG_STRING
:
3180 case IPP_TAG_KEYWORD
:
3181 case IPP_TAG_CHARSET
:
3183 case IPP_TAG_MIMETYPE
:
3184 case IPP_TAG_LANGUAGE
:
3185 for (i
= 0; i
< attr
->num_values
; i
++)
3186 if (Output
== _CUPS_OUTPUT_PLIST
)
3187 print_xml_string("string", attr
->values
[i
].string
.text
);
3189 printf("\"%s\" ", attr
->values
[i
].string
.text
);
3192 case IPP_TAG_TEXTLANG
:
3193 case IPP_TAG_NAMELANG
:
3194 for (i
= 0; i
< attr
->num_values
; i
++)
3195 if (Output
== _CUPS_OUTPUT_PLIST
)
3197 fputs("<dict><key>language</key><string>", stdout
);
3198 print_xml_string(NULL
, attr
->values
[i
].string
.charset
);
3199 fputs("</string><key>string</key><string>", stdout
);
3200 print_xml_string(NULL
, attr
->values
[i
].string
.text
);
3201 puts("</string></dict>");
3204 printf("\"%s\",%s ", attr
->values
[i
].string
.text
,
3205 attr
->values
[i
].string
.charset
);
3208 case IPP_TAG_BEGIN_COLLECTION
:
3209 for (i
= 0; i
< attr
->num_values
; i
++)
3211 if (Output
== _CUPS_OUTPUT_PLIST
)
3214 for (colattr
= attr
->values
[i
].collection
->attrs
;
3216 colattr
= colattr
->next
)
3217 print_attr(colattr
);
3225 print_col(attr
->values
[i
].collection
);
3231 if (Output
== _CUPS_OUTPUT_PLIST
)
3232 printf("<string><<%s>></string>\n",
3233 ippTagString(attr
->value_tag
));
3235 fputs(ippTagString(attr
->value_tag
), stdout
);
3239 if (Output
== _CUPS_OUTPUT_PLIST
)
3241 if (attr
->num_values
> 1)
3250 * 'print_col()' - Print a collection attribute on the screen.
3254 print_col(ipp_t
*col
) /* I - Collection attribute to print */
3256 int i
; /* Looping var */
3257 ipp_attribute_t
*attr
; /* Current attribute in collection */
3260 fputs("{ ", stdout
);
3261 for (attr
= col
->attrs
; attr
; attr
= attr
->next
)
3263 printf("%s (%s%s) = ", attr
->name
, attr
->num_values
> 1 ? "1setOf " : "",
3264 ippTagString(attr
->value_tag
));
3266 switch (attr
->value_tag
)
3268 case IPP_TAG_INTEGER
:
3270 for (i
= 0; i
< attr
->num_values
; i
++)
3271 printf("%d ", attr
->values
[i
].integer
);
3274 case IPP_TAG_BOOLEAN
:
3275 for (i
= 0; i
< attr
->num_values
; i
++)
3276 if (attr
->values
[i
].boolean
)
3282 case IPP_TAG_NOVALUE
:
3286 case IPP_TAG_RANGE
:
3287 for (i
= 0; i
< attr
->num_values
; i
++)
3288 printf("%d-%d ", attr
->values
[i
].range
.lower
,
3289 attr
->values
[i
].range
.upper
);
3292 case IPP_TAG_RESOLUTION
:
3293 for (i
= 0; i
< attr
->num_values
; i
++)
3294 printf("%dx%d%s ", attr
->values
[i
].resolution
.xres
,
3295 attr
->values
[i
].resolution
.yres
,
3296 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
3300 case IPP_TAG_STRING
:
3303 case IPP_TAG_KEYWORD
:
3304 case IPP_TAG_CHARSET
:
3306 case IPP_TAG_MIMETYPE
:
3307 case IPP_TAG_LANGUAGE
:
3308 for (i
= 0; i
< attr
->num_values
; i
++)
3309 printf("\"%s\" ", attr
->values
[i
].string
.text
);
3312 case IPP_TAG_TEXTLANG
:
3313 case IPP_TAG_NAMELANG
:
3314 for (i
= 0; i
< attr
->num_values
; i
++)
3315 printf("\"%s\",%s ", attr
->values
[i
].string
.text
,
3316 attr
->values
[i
].string
.charset
);
3319 case IPP_TAG_BEGIN_COLLECTION
:
3320 for (i
= 0; i
< attr
->num_values
; i
++)
3322 print_col(attr
->values
[i
].collection
);
3328 break; /* anti-compiler-warning-code */
3337 * 'print_csv()' - Print a line of CSV text.
3342 ipp_attribute_t
*attr
, /* I - First attribute for line */
3343 int num_displayed
, /* I - Number of attributes to display */
3344 char **displayed
, /* I - Attributes to display */
3345 size_t *widths
) /* I - Column widths */
3347 int i
; /* Looping var */
3348 size_t maxlength
; /* Max length of all columns */
3349 char *buffer
, /* String buffer */
3350 *bufptr
; /* Pointer into buffer */
3351 ipp_attribute_t
*current
; /* Current attribute */
3355 * Get the maximum string length we have to show and allocate...
3358 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
3359 if (widths
[i
] > maxlength
)
3360 maxlength
= widths
[i
];
3364 if ((buffer
= malloc(maxlength
)) == NULL
)
3368 * Loop through the attributes to display...
3373 for (i
= 0; i
< num_displayed
; i
++)
3380 for (current
= attr
; current
; current
= current
->next
)
3384 else if (!strcmp(current
->name
, displayed
[i
]))
3386 _ippAttrString(current
, buffer
, maxlength
);
3391 if (strchr(buffer
, ',') != NULL
|| strchr(buffer
, '\"') != NULL
||
3392 strchr(buffer
, '\\') != NULL
)
3395 for (bufptr
= buffer
; *bufptr
; bufptr
++)
3397 if (*bufptr
== '\\' || *bufptr
== '\"')
3404 fputs(buffer
, stdout
);
3410 for (i
= 0; i
< num_displayed
; i
++)
3415 fputs(displayed
[i
], stdout
);
3425 * 'print_fatal_error()' - Print a fatal error message.
3429 print_fatal_error(const char *s
, /* I - Printf-style format string */
3430 ...) /* I - Additional arguments as needed */
3432 char buffer
[10240]; /* Format buffer */
3433 va_list ap
; /* Pointer to arguments */
3437 * Format the error message...
3441 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
3448 if (Output
== _CUPS_OUTPUT_PLIST
)
3451 print_xml_trailer(0, buffer
);
3454 _cupsLangPrintf(stderr
, "ipptool: %s\n", buffer
);
3459 * 'print_line()' - Print a line of formatted or CSV text.
3464 ipp_attribute_t
*attr
, /* I - First attribute for line */
3465 int num_displayed
, /* I - Number of attributes to display */
3466 char **displayed
, /* I - Attributes to display */
3467 size_t *widths
) /* I - Column widths */
3469 int i
; /* Looping var */
3470 size_t maxlength
; /* Max length of all columns */
3471 char *buffer
; /* String buffer */
3472 ipp_attribute_t
*current
; /* Current attribute */
3476 * Get the maximum string length we have to show and allocate...
3479 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
3480 if (widths
[i
] > maxlength
)
3481 maxlength
= widths
[i
];
3485 if ((buffer
= malloc(maxlength
)) == NULL
)
3489 * Loop through the attributes to display...
3494 for (i
= 0; i
< num_displayed
; i
++)
3501 for (current
= attr
; current
; current
= current
->next
)
3505 else if (!strcmp(current
->name
, displayed
[i
]))
3507 _ippAttrString(current
, buffer
, maxlength
);
3512 printf("%*s", (int)-widths
[i
], buffer
);
3518 for (i
= 0; i
< num_displayed
; i
++)
3523 printf("%*s", (int)-widths
[i
], displayed
[i
]);
3527 for (i
= 0; i
< num_displayed
; i
++)
3532 memset(buffer
, '-', widths
[i
]);
3533 buffer
[widths
[i
]] = '\0';
3534 fputs(buffer
, stdout
);
3544 * 'print_test_error()' - Print a test error message.
3548 print_test_error(const char *s
, /* I - Printf-style format string */
3549 ...) /* I - Additional arguments as needed */
3551 char buffer
[10240]; /* Format buffer */
3552 va_list ap
; /* Pointer to arguments */
3556 * Format the error message...
3560 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
3567 if (Output
== _CUPS_OUTPUT_PLIST
)
3568 print_xml_string("string", buffer
);
3570 printf(" %s\n", buffer
);
3575 * 'print_xml_header()' - Print a standard XML plist header.
3579 print_xml_header(void)
3583 puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
3584 puts("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
3585 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
3586 puts("<plist version=\"1.0\">");
3588 puts("<key>Transfer</key>");
3589 printf("<string>%s</string>\n",
3590 Transfer
== _CUPS_TRANSFER_AUTO
? "auto" :
3591 Transfer
== _CUPS_TRANSFER_CHUNKED
? "chunked" : "length");
3592 puts("<key>Tests</key>");
3601 * 'print_xml_string()' - Print an XML string with escaping.
3605 print_xml_string(const char *element
, /* I - Element name or NULL */
3606 const char *s
) /* I - String to print */
3609 printf("<%s>", element
);
3614 fputs("&", stdout
);
3616 fputs("<", stdout
);
3618 fputs(">", stdout
);
3626 printf("</%s>\n", element
);
3631 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
3635 print_xml_trailer(int success
, /* I - 1 on success, 0 on failure */
3636 const char *message
) /* I - Error message or NULL */
3641 puts("<key>Successful</key>");
3642 puts(success
? "<true />" : "<false />");
3645 puts("<key>ErrorMessage</key>");
3646 print_xml_string("string", message
);
3657 * 'set_variable()' - Set a variable value.
3661 set_variable(_cups_vars_t
*vars
, /* I - Variables */
3662 const char *name
, /* I - Variable name */
3663 const char *value
) /* I - Value string */
3665 _cups_var_t key
, /* Search key */
3666 *var
; /* New variable */
3669 key
.name
= (char *)name
;
3670 if ((var
= cupsArrayFind(vars
->vars
, &key
)) != NULL
)
3673 var
->value
= strdup(value
);
3675 else if ((var
= malloc(sizeof(_cups_var_t
))) == NULL
)
3677 print_fatal_error("Unable to allocate memory for variable \"%s\".", name
);
3682 var
->name
= strdup(name
);
3683 var
->value
= strdup(value
);
3685 cupsArrayAdd(vars
->vars
, var
);
3691 * 'usage()' - Show program usage.
3697 _cupsLangPuts(stderr
,
3698 _("Usage: ipptool [options] URI filename [ ... "
3703 "-C Send requests using chunking (default)\n"
3704 "-E Test with TLS encryption.\n"
3705 "-I Ignore errors\n"
3706 "-L Send requests using content-length\n"
3707 "-S Test with SSL encryption.\n"
3708 "-V version Set default IPP version.\n"
3709 "-X Produce XML plist instead of plain text.\n"
3710 "-d name=value Define variable.\n"
3711 "-f filename Set default request filename.\n"
3712 "-i seconds Repeat the last file with the given time "
3714 "-n count Repeat the last file the given number of "
3716 "-q Be quiet - no output except errors.\n"
3717 "-t Produce a test report.\n"
3718 "-v Show all attributes sent and received.\n"));
3725 * 'validate_attr()' - Determine whether an attribute is valid.
3728 static int /* O - 1 if valid, 0 otherwise */
3729 validate_attr(ipp_attribute_t
*attr
, /* I - Attribute to validate */
3730 int print
) /* I - 1 = report issues to stdout */
3732 int i
; /* Looping var */
3733 char scheme
[64], /* Scheme from URI */
3734 userpass
[256], /* Username/password from URI */
3735 hostname
[256], /* Hostname from URI */
3736 resource
[1024]; /* Resource from URI */
3737 int port
, /* Port number from URI */
3738 uri_status
, /* URI separation status */
3739 valid
= 1; /* Is the attribute valid? */
3740 const char *ptr
; /* Pointer into string */
3741 ipp_attribute_t
*colattr
; /* Collection attribute */
3742 regex_t re
; /* Regular expression */
3743 ipp_uchar_t
*date
; /* Current date value */
3754 * Validate the attribute name.
3757 for (ptr
= attr
->name
; *ptr
; ptr
++)
3758 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' && *ptr
!= '_')
3761 if (*ptr
|| ptr
== attr
->name
)
3766 print_test_error("\"%s\": Bad attribute name - invalid character (RFC "
3767 "2911 section 4.1.3).", attr
->name
);
3770 if ((ptr
- attr
->name
) > 255)
3775 print_test_error("\"%s\": Bad attribute name - bad length (RFC 2911 "
3776 "section 4.1.3).", attr
->name
);
3779 switch (attr
->value_tag
)
3781 case IPP_TAG_INTEGER
:
3784 case IPP_TAG_BOOLEAN
:
3785 for (i
= 0; i
< attr
->num_values
; i
++)
3787 if (attr
->values
[i
].boolean
!= 0 &&
3788 attr
->values
[i
].boolean
!= 1)
3793 print_test_error("\"%s\": Bad boolen value %d (RFC 2911 section "
3794 "4.1.10).", attr
->name
, attr
->values
[i
].boolean
);
3802 for (i
= 0; i
< attr
->num_values
; i
++)
3804 if (attr
->values
[i
].integer
< 1)
3809 print_test_error("\"%s\": Bad enum value %d - out of range "
3810 "(RFC 2911 section 4.1.4).", attr
->name
,
3811 attr
->values
[i
].integer
);
3818 case IPP_TAG_STRING
:
3819 for (i
= 0; i
< attr
->num_values
; i
++)
3821 if (attr
->values
[i
].unknown
.length
> 1023)
3826 print_test_error("\"%s\": Bad octetString value - bad length %d "
3827 "(RFC 2911 section 4.1.10).", attr
->name
,
3828 attr
->values
[i
].unknown
.length
);
3836 for (i
= 0; i
< attr
->num_values
; i
++)
3838 date
= attr
->values
[i
].date
;
3840 if (date
[2] < 1 || date
[2] > 12)
3845 print_test_error("\"%s\": Bad dateTime month %u (RFC 2911 "
3846 "section 4.1.13).", attr
->name
, date
[2]);
3851 if (date
[3] < 1 || date
[3] > 31)
3856 print_test_error("\"%s\": Bad dateTime day %u (RFC 2911 "
3857 "section 4.1.13).", attr
->name
, date
[3]);
3867 print_test_error("\"%s\": Bad dateTime hours %u (RFC 2911 "
3868 "section 4.1.13).", attr
->name
, date
[4]);
3878 print_test_error("\"%s\": Bad dateTime minutes %u (RFC 2911 "
3879 "section 4.1.13).", attr
->name
, date
[5]);
3889 print_test_error("\"%s\": Bad dateTime seconds %u (RFC 2911 "
3890 "section 4.1.13).", attr
->name
, date
[6]);
3900 print_test_error("\"%s\": Bad dateTime deciseconds %u (RFC 2911 "
3901 "section 4.1.13).", attr
->name
, date
[7]);
3906 if (date
[8] != '-' && date
[8] != '+')
3911 print_test_error("\"%s\": Bad dateTime UTC sign '%c' (RFC 2911 "
3912 "section 4.1.13).", attr
->name
, date
[8]);
3922 print_test_error("\"%s\": Bad dateTime UTC hours %u (RFC 2911 "
3923 "section 4.1.13).", attr
->name
, date
[9]);
3933 print_test_error("\"%s\": Bad dateTime UTC minutes %u (RFC 2911 "
3934 "section 4.1.13).", attr
->name
, date
[10]);
3941 case IPP_TAG_RESOLUTION
:
3942 for (i
= 0; i
< attr
->num_values
; i
++)
3944 if (attr
->values
[i
].resolution
.xres
<= 0)
3949 print_test_error("\"%s\": Bad resolution value %dx%d%s - cross "
3950 "feed resolution must be positive (RFC 2911 "
3951 "section 4.1.13).", attr
->name
,
3952 attr
->values
[i
].resolution
.xres
,
3953 attr
->values
[i
].resolution
.yres
,
3954 attr
->values
[i
].resolution
.units
==
3955 IPP_RES_PER_INCH
? "dpi" :
3956 attr
->values
[i
].resolution
.units
==
3957 IPP_RES_PER_CM
? "dpc" : "unknown");
3962 if (attr
->values
[i
].resolution
.yres
<= 0)
3967 print_test_error("\"%s\": Bad resolution value %dx%d%s - feed "
3968 "resolution must be positive (RFC 2911 section "
3969 "4.1.13).", attr
->name
,
3970 attr
->values
[i
].resolution
.xres
,
3971 attr
->values
[i
].resolution
.yres
,
3972 attr
->values
[i
].resolution
.units
==
3973 IPP_RES_PER_INCH
? "dpi" :
3974 attr
->values
[i
].resolution
.units
==
3975 IPP_RES_PER_CM
? "dpc" : "unknown");
3980 if (attr
->values
[i
].resolution
.units
!= IPP_RES_PER_INCH
&&
3981 attr
->values
[i
].resolution
.units
!= IPP_RES_PER_CM
)
3986 print_test_error("\"%s\": Bad resolution value %dx%d%s - bad "
3987 "units value (RFC 2911 section 4.1.13).",
3988 attr
->name
, attr
->values
[i
].resolution
.xres
,
3989 attr
->values
[i
].resolution
.yres
,
3990 attr
->values
[i
].resolution
.units
==
3991 IPP_RES_PER_INCH
? "dpi" :
3992 attr
->values
[i
].resolution
.units
==
3993 IPP_RES_PER_CM
? "dpc" : "unknown");
4000 case IPP_TAG_RANGE
:
4001 for (i
= 0; i
< attr
->num_values
; i
++)
4003 if (attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
4008 print_test_error("\"%s\": Bad rangeOfInteger value %d-%d - lower "
4009 "greater than upper (RFC 2911 section 4.1.13).",
4010 attr
->name
, attr
->values
[i
].range
.lower
,
4011 attr
->values
[i
].range
.upper
);
4018 case IPP_TAG_BEGIN_COLLECTION
:
4019 for (i
= 0; i
< attr
->num_values
; i
++)
4021 for (colattr
= attr
->values
[i
].collection
->attrs
;
4023 colattr
= colattr
->next
)
4025 if (!validate_attr(colattr
, 0))
4032 if (colattr
&& print
)
4034 print_test_error("\"%s\": Bad collection value.", attr
->name
);
4038 validate_attr(colattr
, print
);
4039 colattr
= colattr
->next
;
4046 case IPP_TAG_TEXTLANG
:
4047 for (i
= 0; i
< attr
->num_values
; i
++)
4049 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4051 if ((*ptr
& 0xe0) == 0xc0)
4054 if ((*ptr
& 0xc0) != 0x80)
4057 else if ((*ptr
& 0xf0) == 0xe0)
4060 if ((*ptr
& 0xc0) != 0x80)
4063 if ((*ptr
& 0xc0) != 0x80)
4066 else if ((*ptr
& 0xf8) == 0xf0)
4069 if ((*ptr
& 0xc0) != 0x80)
4072 if ((*ptr
& 0xc0) != 0x80)
4075 if ((*ptr
& 0xc0) != 0x80)
4078 else if (*ptr
& 0x80)
4087 print_test_error("\"%s\": Bad text value \"%s\" - bad UTF-8 "
4088 "sequence (RFC 2911 section 4.1.1).", attr
->name
,
4089 attr
->values
[i
].string
.text
);
4094 if ((ptr
- attr
->values
[i
].string
.text
) > 1023)
4099 print_test_error("\"%s\": Bad text value \"%s\" - bad length %d "
4100 "(RFC 2911 section 4.1.1).", attr
->name
,
4101 attr
->values
[i
].string
.text
,
4102 (int)strlen(attr
->values
[i
].string
.text
));
4110 case IPP_TAG_NAMELANG
:
4111 for (i
= 0; i
< attr
->num_values
; i
++)
4113 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4115 if ((*ptr
& 0xe0) == 0xc0)
4118 if ((*ptr
& 0xc0) != 0x80)
4121 else if ((*ptr
& 0xf0) == 0xe0)
4124 if ((*ptr
& 0xc0) != 0x80)
4127 if ((*ptr
& 0xc0) != 0x80)
4130 else if ((*ptr
& 0xf8) == 0xf0)
4133 if ((*ptr
& 0xc0) != 0x80)
4136 if ((*ptr
& 0xc0) != 0x80)
4139 if ((*ptr
& 0xc0) != 0x80)
4142 else if (*ptr
& 0x80)
4151 print_test_error("\"%s\": Bad name value \"%s\" - bad UTF-8 "
4152 "sequence (RFC 2911 section 4.1.2).", attr
->name
,
4153 attr
->values
[i
].string
.text
);
4158 if ((ptr
- attr
->values
[i
].string
.text
) > 1023)
4163 print_test_error("\"%s\": Bad name value \"%s\" - bad length %d "
4164 "(RFC 2911 section 4.1.2).", attr
->name
,
4165 attr
->values
[i
].string
.text
,
4166 (int)strlen(attr
->values
[i
].string
.text
));
4173 case IPP_TAG_KEYWORD
:
4174 for (i
= 0; i
< attr
->num_values
; i
++)
4176 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4177 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' &&
4181 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
4186 print_test_error("\"%s\": Bad keyword value \"%s\" - invalid "
4187 "character (RFC 2911 section 4.1.3).",
4188 attr
->name
, attr
->values
[i
].string
.text
);
4193 if ((ptr
- attr
->values
[i
].string
.text
) > 255)
4198 print_test_error("\"%s\": Bad keyword value \"%s\" - bad "
4199 "length %d (RFC 2911 section 4.1.3).",
4200 attr
->name
, attr
->values
[i
].string
.text
,
4201 (int)strlen(attr
->values
[i
].string
.text
));
4209 for (i
= 0; i
< attr
->num_values
; i
++)
4211 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
,
4212 attr
->values
[i
].string
.text
,
4213 scheme
, sizeof(scheme
),
4214 userpass
, sizeof(userpass
),
4215 hostname
, sizeof(hostname
),
4216 &port
, resource
, sizeof(resource
));
4218 if (uri_status
< HTTP_URI_OK
)
4223 print_test_error("\"%s\": Bad URI value \"%s\" - %s "
4224 "(RFC 2911 section 4.1.5).", attr
->name
,
4225 attr
->values
[i
].string
.text
,
4226 URIStatusStrings
[uri_status
-
4227 HTTP_URI_OVERFLOW
]);
4232 if (strlen(attr
->values
[i
].string
.text
) > 1023)
4237 print_test_error("\"%s\": Bad URI value \"%s\" - bad length %d "
4238 "(RFC 2911 section 4.1.5).", attr
->name
,
4239 attr
->values
[i
].string
.text
,
4240 (int)strlen(attr
->values
[i
].string
.text
));
4247 case IPP_TAG_URISCHEME
:
4248 for (i
= 0; i
< attr
->num_values
; i
++)
4250 ptr
= attr
->values
[i
].string
.text
;
4251 if (islower(*ptr
& 255))
4253 for (ptr
++; *ptr
; ptr
++)
4254 if (!islower(*ptr
& 255) && !isdigit(*ptr
& 255) &&
4255 *ptr
!= '+' && *ptr
!= '-' && *ptr
!= '.')
4259 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
4264 print_test_error("\"%s\": Bad uriScheme value \"%s\" - bad "
4265 "characters (RFC 2911 section 4.1.6).",
4266 attr
->name
, attr
->values
[i
].string
.text
);
4271 if ((ptr
- attr
->values
[i
].string
.text
) > 63)
4276 print_test_error("\"%s\": Bad uriScheme value \"%s\" - bad "
4277 "length %d (RFC 2911 section 4.1.6).",
4278 attr
->name
, attr
->values
[i
].string
.text
,
4279 (int)strlen(attr
->values
[i
].string
.text
));
4286 case IPP_TAG_CHARSET
:
4287 for (i
= 0; i
< attr
->num_values
; i
++)
4289 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4290 if (!isprint(*ptr
& 255) || isupper(*ptr
& 255) ||
4291 isspace(*ptr
& 255))
4294 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
4299 print_test_error("\"%s\": Bad charset value \"%s\" - bad "
4300 "characters (RFC 2911 section 4.1.7).",
4301 attr
->name
, attr
->values
[i
].string
.text
);
4306 if ((ptr
- attr
->values
[i
].string
.text
) > 40)
4311 print_test_error("\"%s\": Bad charset value \"%s\" - bad "
4312 "length %d (RFC 2911 section 4.1.7).",
4313 attr
->name
, attr
->values
[i
].string
.text
,
4314 (int)strlen(attr
->values
[i
].string
.text
));
4321 case IPP_TAG_LANGUAGE
:
4323 * The following regular expression is derived from the ABNF for
4324 * language tags in RFC 4646. All I can say is that this is the
4325 * easiest way to check the values...
4328 if ((i
= regcomp(&re
,
4330 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
4332 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
4333 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
4334 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
4335 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
4336 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
4338 "x(-[a-z0-9]{1,8})+" /* privateuse */
4340 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
4342 REG_NOSUB
| REG_EXTENDED
)) != 0)
4344 char temp
[256]; /* Temporary error string */
4346 regerror(i
, &re
, temp
, sizeof(temp
));
4347 print_fatal_error("Unable to compile naturalLanguage regular "
4348 "expression: %s.", temp
);
4351 for (i
= 0; i
< attr
->num_values
; i
++)
4353 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
4358 print_test_error("\"%s\": Bad naturalLanguage value \"%s\" - bad "
4359 "characters (RFC 2911 section 4.1.8).",
4360 attr
->name
, attr
->values
[i
].string
.text
);
4365 if (strlen(attr
->values
[i
].string
.text
) > 63)
4370 print_test_error("\"%s\": Bad naturalLanguage value \"%s\" - bad "
4371 "length %d (RFC 2911 section 4.1.8).",
4372 attr
->name
, attr
->values
[i
].string
.text
,
4373 (int)strlen(attr
->values
[i
].string
.text
));
4382 case IPP_TAG_MIMETYPE
:
4384 * The following regular expression is derived from the ABNF for
4385 * language tags in RFC 2045 and 4288. All I can say is that this is
4386 * the easiest way to check the values...
4389 if ((i
= regcomp(&re
,
4391 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
4393 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
4394 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
4395 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
4398 REG_NOSUB
| REG_EXTENDED
)) != 0)
4400 char temp
[256]; /* Temporary error string */
4402 regerror(i
, &re
, temp
, sizeof(temp
));
4403 print_fatal_error("Unable to compile mimeMediaType regular "
4404 "expression: %s.", temp
);
4407 for (i
= 0; i
< attr
->num_values
; i
++)
4409 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
4414 print_test_error("\"%s\": Bad mimeMediaType value \"%s\" - bad "
4415 "characters (RFC 2911 section 4.1.9).",
4416 attr
->name
, attr
->values
[i
].string
.text
);
4421 if (strlen(attr
->values
[i
].string
.text
) > 255)
4426 print_test_error("\"%s\": Bad mimeMediaType value \"%s\" - bad "
4427 "length %d (RFC 2911 section 4.1.9).",
4428 attr
->name
, attr
->values
[i
].string
.text
,
4429 (int)strlen(attr
->values
[i
].string
.text
));
4445 * 'with_value()' - Test a WITH-VALUE predicate.
4448 static int /* O - 1 on match, 0 on non-match */
4449 with_value(char *value
, /* I - Value string */
4450 int regex
, /* I - Value is a regular expression */
4451 ipp_attribute_t
*attr
, /* I - Attribute to compare */
4452 int report
) /* I - 1 = report failures */
4454 int i
; /* Looping var */
4455 char *valptr
; /* Pointer into value */
4459 * NULL matches everything.
4462 if (!value
|| !*value
)
4466 * Compare the value string to the attribute value.
4469 switch (attr
->value_tag
)
4471 case IPP_TAG_INTEGER
:
4473 for (i
= 0; i
< attr
->num_values
; i
++)
4475 char op
, /* Comparison operator */
4476 *nextptr
; /* Next pointer */
4477 int intvalue
; /* Integer value */
4481 if (!strncmp(valptr
, "no-value,", 9))
4484 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
4485 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
4486 *valptr
== '=' || *valptr
== '>')
4489 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
4491 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
4499 intvalue
= strtol(valptr
, &nextptr
, 0);
4500 if (nextptr
== valptr
)
4507 if (attr
->values
[i
].integer
== intvalue
)
4511 if (attr
->values
[i
].integer
< intvalue
)
4515 if (attr
->values
[i
].integer
> intvalue
)
4524 for (i
= 0; i
< attr
->num_values
; i
++)
4525 print_test_error("GOT: %s=%d", attr
->name
, attr
->values
[i
].integer
);
4529 case IPP_TAG_BOOLEAN
:
4530 for (i
= 0; i
< attr
->num_values
; i
++)
4532 if (!strcmp(value
, "true") == attr
->values
[i
].boolean
)
4538 for (i
= 0; i
< attr
->num_values
; i
++)
4539 print_test_error("GOT: %s=%s", attr
->name
,
4540 attr
->values
[i
].boolean
? "true" : "false");
4544 case IPP_TAG_NOVALUE
:
4545 return (!strcmp(value
, "no-value") || !strncmp(value
, "no-value,", 9));
4547 case IPP_TAG_CHARSET
:
4548 case IPP_TAG_KEYWORD
:
4549 case IPP_TAG_LANGUAGE
:
4550 case IPP_TAG_MIMETYPE
:
4552 case IPP_TAG_NAMELANG
:
4554 case IPP_TAG_TEXTLANG
:
4556 case IPP_TAG_URISCHEME
:
4560 * Value is an extended, case-sensitive POSIX regular expression...
4563 regex_t re
; /* Regular expression */
4565 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
4567 char temp
[256]; /* Temporary string */
4569 regerror(i
, &re
, temp
, sizeof(temp
));
4571 print_fatal_error("Unable to compile WITH-VALUE regular expression "
4572 "\"%s\" - %s", value
, temp
);
4577 * See if ALL of the values match the given regular expression.
4580 for (i
= 0; i
< attr
->num_values
; i
++)
4582 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
4585 print_test_error("GOT: %s=\"%s\"", attr
->name
,
4586 attr
->values
[i
].string
.text
);
4594 return (i
== attr
->num_values
);
4599 * Value is a literal string, see if at least one value matches the
4603 for (i
= 0; i
< attr
->num_values
; i
++)
4605 if (!strcmp(value
, attr
->values
[i
].string
.text
))
4611 for (i
= 0; i
< attr
->num_values
; i
++)
4612 print_test_error("GOT: %s=\"%s\"", attr
->name
,
4613 attr
->values
[i
].string
.text
);