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 * timeout_cb() - Handle HTTP timeouts.
40 * usage() - Show program usage.
41 * validate_attr() - Determine whether an attribute is valid.
42 * with_value() - Test a WITH-VALUE predicate.
46 * Include necessary headers...
49 #include <cups/cups-private.h>
50 #include <cups/file-private.h>
55 #endif /* !O_BINARY */
62 typedef enum _cups_transfer_e
/**** How to send request data ****/
64 _CUPS_TRANSFER_AUTO
, /* Chunk for files, length for static */
65 _CUPS_TRANSFER_CHUNKED
, /* Chunk always */
66 _CUPS_TRANSFER_LENGTH
/* Length always */
69 typedef enum _cups_output_e
/**** Output mode ****/
71 _CUPS_OUTPUT_QUIET
, /* No output */
72 _CUPS_OUTPUT_TEST
, /* Traditional CUPS test output */
73 _CUPS_OUTPUT_PLIST
, /* XML plist test output */
74 _CUPS_OUTPUT_LIST
, /* Tabular list output */
75 _CUPS_OUTPUT_CSV
/* Comma-separated values output */
78 typedef struct _cups_expect_s
/**** Expected attribute info ****/
80 int optional
, /* Optional attribute? */
81 not_expect
; /* Don't expect attribute? */
82 char *name
, /* Attribute name */
83 *of_type
, /* Type name */
84 *same_count_as
, /* Parallel attribute name */
85 *if_defined
, /* Only required if variable defined */
86 *if_not_defined
, /* Only required if variable is not defined */
87 *with_value
, /* Attribute must include this value */
88 *define_match
, /* Variable to define on match */
89 *define_no_match
, /* Variable to define on no-match */
90 *define_value
; /* Variable to define with value */
91 int with_regex
, /* WITH-VALUE is a regular expression */
92 count
; /* Expected count if > 0 */
93 ipp_tag_t in_group
; /* IN-GROUP value */
96 typedef struct _cups_status_s
/**** Status info ****/
98 ipp_status_t status
; /* Expected status code */
99 char *if_defined
, /* Only if variable is defined */
100 *if_not_defined
; /* Only if variable is not defined */
103 typedef struct _cups_var_s
/**** Variable ****/
105 char *name
, /* Name of variable */
106 *value
; /* Value of variable */
109 typedef struct _cups_vars_s
/**** Set of variables ****/
111 const char *uri
, /* URI for printer */
112 *filename
; /* Filename */
113 char scheme
[64], /* Scheme from URI */
114 userpass
[256], /* Username/password from URI */
115 hostname
[256], /* Hostname from URI */
116 resource
[1024]; /* Resource path from URI */
117 int port
; /* Port number from URI */
118 http_encryption_t encryption
; /* Encryption for connection? */
119 double timeout
; /* Timeout for connection */
120 cups_array_t
*vars
; /* Array of variables */
128 _cups_transfer_t Transfer
= _CUPS_TRANSFER_AUTO
;
129 /* How to transfer requests */
130 _cups_output_t Output
= _CUPS_OUTPUT_LIST
;
132 int IgnoreErrors
= 0, /* Ignore errors? */
133 Verbosity
= 0, /* Show all attributes? */
134 Version
= 11, /* Default IPP version */
135 XMLHeader
= 0; /* 1 if header is written */
136 char *Password
= NULL
; /* Password from URI */
137 const char * const URIStatusStrings
[] = /* URI status strings */
140 "Bad arguments to function",
141 "Bad resource in URI",
142 "Bad port number in URI",
143 "Bad hostname/address in URI",
144 "Bad username in URI",
148 "Missing scheme in URI",
149 "Unknown scheme in URI",
150 "Missing resource in URI"
158 static int compare_vars(_cups_var_t
*a
, _cups_var_t
*b
);
159 static int do_tests(_cups_vars_t
*vars
, const char *testfile
);
160 static void expand_variables(_cups_vars_t
*vars
, char *dst
, const char *src
,
163 __attribute((nonnull(1,2,3)))
164 #endif /* __GNUC__ */
166 static int expect_matches(_cups_expect_t
*expect
, ipp_tag_t value_tag
);
167 static ipp_t
*get_collection(_cups_vars_t
*vars
, FILE *fp
, int *linenum
);
168 static char *get_filename(const char *testfile
, char *dst
, const char *src
,
170 static char *get_token(FILE *fp
, char *buf
, int buflen
,
172 static char *get_variable(_cups_vars_t
*vars
, const char *name
);
173 static char *iso_date(ipp_uchar_t
*date
);
174 static const char *password_cb(const char *prompt
);
175 static void print_attr(ipp_attribute_t
*attr
);
176 static void print_col(ipp_t
*col
);
177 static void print_csv(ipp_attribute_t
*attr
, int num_displayed
,
178 char **displayed
, size_t *widths
);
179 static void print_fatal_error(const char *s
, ...)
181 __attribute__ ((__format__ (__printf__
, 1, 2)))
182 #endif /* __GNUC__ */
184 static void print_line(ipp_attribute_t
*attr
, int num_displayed
,
185 char **displayed
, size_t *widths
);
186 static void print_test_error(const char *s
, ...)
188 __attribute__ ((__format__ (__printf__
, 1, 2)))
189 #endif /* __GNUC__ */
191 static void print_xml_header(void);
192 static void print_xml_string(const char *element
, const char *s
);
193 static void print_xml_trailer(int success
, const char *message
);
194 static void set_variable(_cups_vars_t
*vars
, const char *name
,
196 static int timeout_cb(http_t
*http
, void *user_data
);
197 static void usage(void);
198 static int validate_attr(ipp_attribute_t
*attr
, int print
);
199 static int with_value(char *value
, int regex
, ipp_attribute_t
*attr
,
204 * 'main()' - Parse options and do tests.
207 int /* O - Exit status */
208 main(int argc
, /* I - Number of command-line args */
209 char *argv
[]) /* I - Command-line arguments */
211 int i
; /* Looping var */
212 int status
; /* Status of tests... */
213 char *opt
, /* Current option */
214 name
[1024], /* Name/value buffer */
215 *value
, /* Pointer to value */
216 filename
[1024], /* Real filename */
217 testname
[1024]; /* Real test filename */
218 const char *testfile
; /* Test file to use */
219 int interval
, /* Test interval in microseconds */
220 repeat
; /* Repeat count */
221 _cups_vars_t vars
; /* Variables */
222 http_uri_status_t uri_status
; /* URI separation status */
223 _cups_globals_t
*cg
= _cupsGlobals();
229 * Initialize the locale and variables...
232 _cupsSetLocale(argv
);
234 memset(&vars
, 0, sizeof(vars
));
235 vars
.vars
= cupsArrayNew((cups_array_func_t
)compare_vars
, NULL
);
240 * ipptool URI testfile
248 for (i
= 1; i
< argc
; i
++)
250 if (argv
[i
][0] == '-')
252 for (opt
= argv
[i
] + 1; *opt
; opt
++)
256 case 'C' : /* Enable HTTP chunking */
257 Transfer
= _CUPS_TRANSFER_CHUNKED
;
260 case 'E' : /* Encrypt with TLS */
262 vars
.encryption
= HTTP_ENCRYPT_REQUIRED
;
264 _cupsLangPrintf(stderr
,
265 _("%s: Sorry, no encryption support compiled in\n"),
267 #endif /* HAVE_SSL */
270 case 'I' : /* Ignore errors */
274 case 'L' : /* Disable HTTP chunking */
275 Transfer
= _CUPS_TRANSFER_LENGTH
;
278 case 'S' : /* Encrypt with SSL */
280 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
282 _cupsLangPrintf(stderr
,
283 _("%s: Sorry, no encryption support compiled in\n"),
285 #endif /* HAVE_SSL */
288 case 'T' : /* Set timeout */
293 _cupsLangPuts(stderr
,
294 _("ipptool: Missing timeout for \"-T\".\n"));
298 vars
.timeout
= _cupsStrScand(argv
[i
], NULL
, localeconv());
301 case 'V' : /* Set IPP version */
306 _cupsLangPuts(stderr
,
307 _("ipptool: Missing version for \"-V\".\n"));
311 if (!strcmp(argv
[i
], "1.0"))
313 else if (!strcmp(argv
[i
], "1.1"))
315 else if (!strcmp(argv
[i
], "2.0"))
317 else if (!strcmp(argv
[i
], "2.1"))
319 else if (!strcmp(argv
[i
], "2.2"))
323 _cupsLangPrintf(stderr
,
324 _("ipptool: Bad version %s for \"-V\".\n"),
330 case 'X' : /* Produce XML output */
331 Output
= _CUPS_OUTPUT_PLIST
;
333 if (interval
|| repeat
)
335 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are "
336 "incompatible with -X\".\n"));
341 case 'c' : /* CSV output */
342 Output
= _CUPS_OUTPUT_CSV
;
345 case 'd' : /* Define a variable */
350 _cupsLangPuts(stderr
,
351 _("ipptool: Missing name=value for \"-d\".\n"));
355 strlcpy(name
, argv
[i
], sizeof(name
));
356 if ((value
= strchr(name
, '=')) != NULL
)
359 value
= name
+ strlen(name
);
361 set_variable(&vars
, name
, value
);
364 case 'f' : /* Set the default test filename */
369 _cupsLangPuts(stderr
,
370 _("ipptool: Missing filename for \"-f\".\n"));
374 if (access(argv
[i
], 0) && argv
[i
][0] != '/')
376 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s",
377 cg
->cups_datadir
, argv
[i
]);
378 if (access(argv
[i
], 0))
379 vars
.filename
= argv
[i
];
381 vars
.filename
= filename
;
384 vars
.filename
= argv
[i
];
387 case 'i' : /* Test every N seconds */
392 _cupsLangPuts(stderr
,
393 _("ipptool: Missing seconds for \"-i\".\n"));
398 interval
= (int)(_cupsStrScand(argv
[i
], NULL
, localeconv()) *
402 _cupsLangPuts(stderr
,
403 _("ipptool: Invalid seconds for \"-i\".\n"));
408 if (Output
== _CUPS_OUTPUT_PLIST
&& interval
)
410 _cupsLangPuts(stderr
, _("ipptool: \"-i\" is incompatible with "
416 case 'l' : /* List as a table */
417 Output
= _CUPS_OUTPUT_LIST
;
420 case 'n' : /* Repeat count */
425 _cupsLangPuts(stderr
,
426 _("ipptool: Missing count for \"-n\".\n"));
430 repeat
= atoi(argv
[i
]);
432 if (Output
== _CUPS_OUTPUT_PLIST
&& repeat
)
434 _cupsLangPuts(stderr
, _("ipptool: \"-n\" is incompatible with "
440 case 'q' : /* Be quiet */
441 Output
= _CUPS_OUTPUT_QUIET
;
444 case 't' : /* CUPS test output */
445 Output
= _CUPS_OUTPUT_TEST
;
448 case 'v' : /* Be verbose */
453 _cupsLangPrintf(stderr
, _("ipptool: Unknown option \"-%c\".\n"),
460 else if (!strncmp(argv
[i
], "ipp://", 6) || !strncmp(argv
[i
], "http://", 7)
462 || !strncmp(argv
[i
], "ipps://", 7)
463 || !strncmp(argv
[i
], "https://", 8)
464 #endif /* HAVE_SSL */
473 _cupsLangPuts(stderr
, _("ipptool: May only specify a single URI.\n"));
478 if (!strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8))
479 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
480 #endif /* HAVE_SSL */
483 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, vars
.uri
,
484 vars
.scheme
, sizeof(vars
.scheme
),
485 vars
.userpass
, sizeof(vars
.userpass
),
486 vars
.hostname
, sizeof(vars
.hostname
),
488 vars
.resource
, sizeof(vars
.resource
));
490 if (uri_status
!= HTTP_URI_OK
)
492 _cupsLangPrintf(stderr
, _("ipptool: Bad URI - %s.\n"),
493 URIStatusStrings
[uri_status
- HTTP_URI_OVERFLOW
]);
497 if (vars
.userpass
[0])
499 if ((Password
= strchr(vars
.userpass
, ':')) != NULL
)
502 cupsSetUser(vars
.userpass
);
503 cupsSetPasswordCB(password_cb
);
504 set_variable(&vars
, "uriuser", vars
.userpass
);
515 _cupsLangPuts(stderr
, _("ipptool: URI required before test file."));
519 if (access(argv
[i
], 0) && argv
[i
][0] != '/')
521 snprintf(testname
, sizeof(testname
), "%s/ipptool/%s", cg
->cups_datadir
,
523 if (access(testname
, 0))
531 if (!do_tests(&vars
, testfile
))
536 if (!vars
.uri
|| !testfile
)
540 * Loop if the interval is set...
543 if (Output
== _CUPS_OUTPUT_PLIST
)
544 print_xml_trailer(!status
, NULL
);
545 else if (interval
> 0 && repeat
> 0)
550 do_tests(&vars
, testfile
);
554 else if (interval
> 0)
559 do_tests(&vars
, testfile
);
572 * 'compare_vars()' - Compare two variables.
575 static int /* O - Result of comparison */
576 compare_vars(_cups_var_t
*a
, /* I - First variable */
577 _cups_var_t
*b
) /* I - Second variable */
579 return (strcasecmp(a
->name
, b
->name
));
584 * 'do_tests()' - Do tests as specified in the test file.
587 static int /* 1 = success, 0 = failure */
588 do_tests(_cups_vars_t
*vars
, /* I - Variables */
589 const char *testfile
) /* I - Test file to use */
591 int i
, /* Looping var */
592 linenum
, /* Current line number */
593 pass
, /* Did we pass the test? */
594 prev_pass
= 1, /* Did we pass the previous test? */
595 request_id
, /* Current request ID */
596 show_header
= 1, /* Show the test header? */
597 ignore_errors
, /* Ignore test failures? */
598 skip_previous
= 0; /* Skip on previous test failure? */
599 http_t
*http
= NULL
; /* HTTP connection to server */
600 FILE *fp
= NULL
; /* Test file */
601 char resource
[512], /* Resource for request */
602 token
[1024], /* Token from file */
603 *tokenptr
, /* Pointer into token */
604 temp
[1024]; /* Temporary string */
605 ipp_t
*request
= NULL
; /* IPP request */
606 ipp_t
*response
= NULL
; /* IPP response */
607 char attr
[128]; /* Attribute name */
608 ipp_op_t op
; /* Operation */
609 ipp_tag_t group
; /* Current group */
610 ipp_tag_t value
; /* Current value type */
611 ipp_attribute_t
*attrptr
, /* Attribute pointer */
612 *found
, /* Found attribute */
613 *lastcol
= NULL
; /* Last collection attribute */
614 char name
[1024]; /* Name of test */
615 char filename
[1024]; /* Filename */
616 _cups_transfer_t transfer
; /* To chunk or not to chunk */
617 int version
, /* IPP version number to use */
618 skip_test
; /* Skip this test? */
619 int num_statuses
= 0; /* Number of valid status codes */
620 _cups_status_t statuses
[100], /* Valid status codes */
621 *last_status
; /* Last STATUS (for predicates) */
622 int num_expects
= 0; /* Number of expected attributes */
623 _cups_expect_t expects
[200], /* Expected attributes */
624 *expect
, /* Current expected attribute */
625 *last_expect
; /* Last EXPECT (for predicates) */
626 int num_displayed
= 0; /* Number of displayed attributes */
627 char *displayed
[200]; /* Displayed attributes */
628 size_t widths
[200]; /* Width of columns */
632 * Open the test file...
635 if ((fp
= fopen(testfile
, "r")) == NULL
)
637 print_fatal_error("Unable to open test file %s - %s", testfile
,
644 * Connect to the server...
647 if ((http
= httpConnectEncrypt(vars
->hostname
, vars
->port
,
648 vars
->encryption
)) == NULL
)
650 print_fatal_error("Unable to connect to %s on port %d - %s", vars
->hostname
,
651 vars
->port
, strerror(errno
));
656 if (vars
->timeout
> 0.0)
657 _httpSetTimeout(http
, vars
->timeout
, timeout_cb
, NULL
);
663 CUPS_SRAND(time(NULL
));
667 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
669 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
672 * Expect an open brace...
675 if (!strcmp(token
, "DEFINE"))
681 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
682 get_token(fp
, temp
, sizeof(temp
), &linenum
))
684 expand_variables(vars
, token
, temp
, sizeof(token
));
685 set_variable(vars
, attr
, token
);
689 print_fatal_error("Missing DEFINE name and/or value on line %d.",
697 else if (!strcmp(token
, "IGNORE-ERRORS"))
704 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
705 (!strcasecmp(temp
, "yes") || !strcasecmp(temp
, "no")))
707 IgnoreErrors
= !strcasecmp(temp
, "yes");
711 print_fatal_error("Missing IGNORE-ERRORS value on line %d.", linenum
);
718 else if (!strcmp(token
, "INCLUDE"))
725 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
728 * Map the filename to and then run the tests...
731 if (!do_tests(vars
, get_filename(testfile
, filename
, temp
,
742 print_fatal_error("Missing INCLUDE filename on line %d.", linenum
);
750 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
753 * SKIP-IF-DEFINED variable
756 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
758 if (get_variable(vars
, temp
))
763 print_fatal_error("Missing SKIP-IF-DEFINED value on line %d.", linenum
);
768 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
771 * SKIP-IF-NOT-DEFINED variable
774 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
776 if (!get_variable(vars
, temp
))
781 print_fatal_error("Missing SKIP-IF-NOT-DEFINED value on line %d.",
787 else if (!strcmp(token
, "TRANSFER"))
795 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
797 if (!strcmp(temp
, "auto"))
798 Transfer
= _CUPS_TRANSFER_AUTO
;
799 else if (!strcmp(temp
, "chunked"))
800 Transfer
= _CUPS_TRANSFER_CHUNKED
;
801 else if (!strcmp(temp
, "length"))
802 Transfer
= _CUPS_TRANSFER_LENGTH
;
805 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp
,
813 print_fatal_error("Missing TRANSFER value on line %d.", linenum
);
820 else if (!strcmp(token
, "VERSION"))
822 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
824 if (!strcmp(temp
, "1.0"))
826 else if (!strcmp(temp
, "1.1"))
828 else if (!strcmp(temp
, "2.0"))
830 else if (!strcmp(temp
, "2.1"))
832 else if (!strcmp(temp
, "2.2"))
836 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp
, linenum
);
843 print_fatal_error("Missing VERSION number on line %d.", linenum
);
850 else if (strcmp(token
, "{"))
852 print_fatal_error("Unexpected token %s seen on line %d.", token
, linenum
);
858 * Initialize things...
863 if (Output
== _CUPS_OUTPUT_PLIST
)
865 else if (Output
== _CUPS_OUTPUT_TEST
)
866 printf("\"%s\":\n", testfile
);
871 strlcpy(resource
, vars
->resource
, sizeof(resource
));
876 group
= IPP_TAG_ZERO
;
877 ignore_errors
= IgnoreErrors
;
885 strlcpy(name
, testfile
, sizeof(name
));
886 if (strrchr(name
, '.') != NULL
)
887 *strrchr(name
, '.') = '\0';
890 * Parse until we see a close brace...
893 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
895 if (strcasecmp(token
, "COUNT") &&
896 strcasecmp(token
, "DEFINE-MATCH") &&
897 strcasecmp(token
, "DEFINE-NO-MATCH") &&
898 strcasecmp(token
, "DEFINE-VALUE") &&
899 strcasecmp(token
, "IF-DEFINED") &&
900 strcasecmp(token
, "IF-NOT-DEFINED") &&
901 strcasecmp(token
, "IN-GROUP") &&
902 strcasecmp(token
, "OF-TYPE") &&
903 strcasecmp(token
, "SAME-COUNT-AS") &&
904 strcasecmp(token
, "WITH-VALUE"))
907 if (strcasecmp(token
, "IF-DEFINED") &&
908 strcasecmp(token
, "IF-NOT-DEFINED"))
911 if (!strcmp(token
, "}"))
913 else if (!strcmp(token
, "{") && lastcol
)
916 * Another collection value
919 ipp_t
*col
= get_collection(vars
, fp
, &linenum
);
920 /* Collection value */
924 ipp_attribute_t
*tempcol
; /* Pointer to new buffer */
928 * Reallocate memory...
931 if ((tempcol
= realloc(lastcol
, sizeof(ipp_attribute_t
) +
932 (lastcol
->num_values
+ 1) *
933 sizeof(ipp_value_t
))) == NULL
)
935 print_fatal_error("Unable to allocate memory on line %d.", linenum
);
940 if (tempcol
!= lastcol
)
943 * Reset pointers in the list...
947 request
->prev
->next
= tempcol
;
949 request
->attrs
= tempcol
;
951 lastcol
= request
->current
= request
->last
= tempcol
;
954 lastcol
->values
[lastcol
->num_values
].collection
= col
;
955 lastcol
->num_values
++;
963 else if (!strcmp(token
, "DEFINE"))
969 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
970 get_token(fp
, temp
, sizeof(temp
), &linenum
))
972 expand_variables(vars
, token
, temp
, sizeof(token
));
973 set_variable(vars
, attr
, token
);
977 print_fatal_error("Missing DEFINE name and/or value on line %d.",
983 else if (!strcmp(token
, "IGNORE-ERRORS"))
990 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
991 (!strcasecmp(temp
, "yes") || !strcasecmp(temp
, "no")))
993 ignore_errors
= !strcasecmp(temp
, "yes");
997 print_fatal_error("Missing IGNORE-ERRORS value on line %d.", linenum
);
1004 else if (!strcasecmp(token
, "NAME"))
1010 get_token(fp
, name
, sizeof(name
), &linenum
);
1012 else if (!strcmp(token
, "REQUEST-ID"))
1019 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1021 if (isdigit(temp
[0] & 255))
1022 request_id
= atoi(temp
);
1023 else if (!strcasecmp(temp
, "random"))
1024 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
1027 print_fatal_error("Bad REQUEST-ID value \"%s\" on line %d.", temp
,
1035 print_fatal_error("Missing REQUEST-ID value on line %d.", linenum
);
1040 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1043 * SKIP-IF-DEFINED variable
1046 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1048 if (get_variable(vars
, temp
))
1053 print_fatal_error("Missing SKIP-IF-DEFINED value on line %d.",
1059 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1062 * SKIP-IF-NOT-DEFINED variable
1065 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1067 if (!get_variable(vars
, temp
))
1072 print_fatal_error("Missing SKIP-IF-NOT-DEFINED value on line %d.",
1078 else if (!strcmp(token
, "SKIP-PREVIOUS-ERROR"))
1081 * SKIP-PREVIOUS-ERROR yes
1082 * SKIP-PREVIOUS-ERROR no
1085 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1086 (!strcasecmp(temp
, "yes") || !strcasecmp(temp
, "no")))
1088 skip_previous
= !strcasecmp(temp
, "yes");
1092 print_fatal_error("Missing SKIP-PREVIOUS-ERROR value on line %d.", linenum
);
1099 else if (!strcmp(token
, "TRANSFER"))
1107 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1109 if (!strcmp(temp
, "auto"))
1110 transfer
= _CUPS_TRANSFER_AUTO
;
1111 else if (!strcmp(temp
, "chunked"))
1112 transfer
= _CUPS_TRANSFER_CHUNKED
;
1113 else if (!strcmp(temp
, "length"))
1114 transfer
= _CUPS_TRANSFER_LENGTH
;
1117 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp
,
1125 print_fatal_error("Missing TRANSFER value on line %d.", linenum
);
1130 else if (!strcasecmp(token
, "VERSION"))
1132 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1134 if (!strcmp(temp
, "0.0"))
1136 else if (!strcmp(temp
, "1.0"))
1138 else if (!strcmp(temp
, "1.1"))
1140 else if (!strcmp(temp
, "2.0"))
1142 else if (!strcmp(temp
, "2.1"))
1144 else if (!strcmp(temp
, "2.2"))
1148 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1155 print_fatal_error("Missing VERSION number on line %d.", linenum
);
1160 else if (!strcasecmp(token
, "RESOURCE"))
1166 if (!get_token(fp
, resource
, sizeof(resource
), &linenum
))
1168 print_fatal_error("Missing RESOURCE path on line %d.", linenum
);
1173 else if (!strcasecmp(token
, "OPERATION"))
1179 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1181 print_fatal_error("Missing OPERATION code on line %d.", linenum
);
1186 if ((op
= ippOpValue(token
)) < 0 && (op
= strtol(token
, NULL
, 0)) == 0)
1188 print_fatal_error("Bad OPERATION code \"%s\" on line %d.", token
,
1194 else if (!strcasecmp(token
, "GROUP"))
1197 * Attribute group...
1200 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1202 print_fatal_error("Missing GROUP tag on line %d.", linenum
);
1207 if ((value
= ippTagValue(token
)) < 0)
1209 print_fatal_error("Bad GROUP tag \"%s\" on line %d.", token
, linenum
);
1215 ippAddSeparator(request
);
1219 else if (!strcasecmp(token
, "DELAY"))
1222 * Delay before operation...
1227 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1229 print_fatal_error("Missing DELAY value on line %d.", linenum
);
1234 if ((delay
= _cupsStrScand(token
, NULL
, localeconv())) <= 0.0)
1236 print_fatal_error("Bad DELAY value \"%s\" on line %d.", token
,
1243 if (Output
== _CUPS_OUTPUT_TEST
)
1244 printf(" [%g second delay]\n", delay
);
1246 usleep((int)(1000000.0 * delay
));
1249 else if (!strcasecmp(token
, "ATTR"))
1255 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1257 print_fatal_error("Missing ATTR value tag on line %d.", linenum
);
1262 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
1264 print_fatal_error("Bad ATTR value tag \"%s\" on line %d.", token
,
1270 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
1272 print_fatal_error("Missing ATTR name on line %d.", linenum
);
1277 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1279 print_fatal_error("Missing ATTR value on line %d.", linenum
);
1284 expand_variables(vars
, token
, temp
, sizeof(token
));
1288 case IPP_TAG_BOOLEAN
:
1289 if (!strcasecmp(token
, "true"))
1290 ippAddBoolean(request
, group
, attr
, 1);
1292 ippAddBoolean(request
, group
, attr
, atoi(token
));
1295 case IPP_TAG_INTEGER
:
1297 ippAddInteger(request
, group
, value
, attr
, atoi(token
));
1300 case IPP_TAG_RESOLUTION
:
1302 int xres
, /* X resolution */
1303 yres
; /* Y resolution */
1304 char *ptr
; /* Pointer into value */
1306 xres
= yres
= strtol(token
, (char **)&ptr
, 10);
1307 if (ptr
> token
&& xres
> 0)
1310 yres
= strtol(ptr
+ 1, (char **)&ptr
, 10);
1313 if (ptr
<= token
|| xres
<= 0 || yres
<= 0 || !ptr
||
1314 (strcasecmp(ptr
, "dpi") && strcasecmp(ptr
, "dpc") &&
1315 strcasecmp(ptr
, "other")))
1317 print_fatal_error("Bad resolution value \"%s\" on line %d.",
1323 if (!strcasecmp(ptr
, "dpi"))
1324 ippAddResolution(request
, group
, attr
, IPP_RES_PER_INCH
,
1326 else if (!strcasecmp(ptr
, "dpc"))
1327 ippAddResolution(request
, group
, attr
, IPP_RES_PER_CM
,
1330 ippAddResolution(request
, group
, attr
, (ipp_res_t
)0,
1335 case IPP_TAG_RANGE
:
1337 int lowers
[4], /* Lower value */
1338 uppers
[4], /* Upper values */
1339 num_vals
; /* Number of values */
1342 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
1343 lowers
+ 0, uppers
+ 0,
1344 lowers
+ 1, uppers
+ 1,
1345 lowers
+ 2, uppers
+ 2,
1346 lowers
+ 3, uppers
+ 3);
1348 if ((num_vals
& 1) || num_vals
== 0)
1350 print_fatal_error("Bad rangeOfInteger value \"%s\" on line "
1351 "%d.", token
, linenum
);
1356 ippAddRanges(request
, group
, attr
, num_vals
/ 2, lowers
,
1361 case IPP_TAG_BEGIN_COLLECTION
:
1362 if (!strcmp(token
, "{"))
1364 ipp_t
*col
= get_collection(vars
, fp
, &linenum
);
1365 /* Collection value */
1369 lastcol
= ippAddCollection(request
, group
, attr
, col
);
1380 print_fatal_error("Bad ATTR collection value on line %d.",
1388 print_fatal_error("Unsupported ATTR value tag %s on line %d.",
1389 ippTagString(value
), linenum
);
1393 case IPP_TAG_TEXTLANG
:
1394 case IPP_TAG_NAMELANG
:
1397 case IPP_TAG_KEYWORD
:
1399 case IPP_TAG_URISCHEME
:
1400 case IPP_TAG_CHARSET
:
1401 case IPP_TAG_LANGUAGE
:
1402 case IPP_TAG_MIMETYPE
:
1403 if (!strchr(token
, ','))
1404 ippAddString(request
, group
, value
, attr
, NULL
, token
);
1408 * Multiple string values...
1411 int num_values
; /* Number of values */
1412 char *values
[100], /* Values */
1413 *ptr
; /* Pointer to next value */
1419 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
1422 values
[num_values
] = ptr
;
1426 ippAddStrings(request
, group
, value
, attr
, num_values
,
1427 NULL
, (const char **)values
);
1432 else if (!strcasecmp(token
, "FILE"))
1438 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1440 print_fatal_error("Missing FILE filename on line %d.", linenum
);
1445 expand_variables(vars
, token
, temp
, sizeof(token
));
1446 get_filename(testfile
, filename
, token
, sizeof(filename
));
1448 else if (!strcasecmp(token
, "STATUS"))
1454 if (num_statuses
>= (int)(sizeof(statuses
) / sizeof(statuses
[0])))
1456 print_fatal_error("Too many STATUS's on line %d.", linenum
);
1461 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1463 print_fatal_error("Missing STATUS code on line %d.", linenum
);
1468 if ((statuses
[num_statuses
].status
= ippErrorValue(token
)) < 0 &&
1469 (statuses
[num_statuses
].status
= strtol(token
, NULL
, 0)) == 0)
1471 print_fatal_error("Bad STATUS code \"%s\" on line %d.", token
,
1477 last_status
= statuses
+ num_statuses
;
1480 last_status
->if_defined
= NULL
;
1481 last_status
->if_not_defined
= NULL
;
1483 else if (!strcasecmp(token
, "EXPECT"))
1486 * Expected attributes...
1489 if (num_expects
>= (int)(sizeof(expects
) / sizeof(expects
[0])))
1491 print_fatal_error("Too many EXPECT's on line %d.", linenum
);
1496 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1498 print_fatal_error("Missing EXPECT name on line %d.", linenum
);
1503 last_expect
= expects
+ num_expects
;
1506 memset(last_expect
, 0, sizeof(_cups_expect_t
));
1508 if (token
[0] == '!')
1510 last_expect
->not_expect
= 1;
1511 last_expect
->name
= strdup(token
+ 1);
1513 else if (token
[0] == '?')
1515 last_expect
->optional
= 1;
1516 last_expect
->name
= strdup(token
+ 1);
1519 last_expect
->name
= strdup(token
);
1521 else if (!strcasecmp(token
, "COUNT"))
1523 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1525 print_fatal_error("Missing COUNT number on line %d.", linenum
);
1530 if ((i
= atoi(token
)) <= 0)
1532 print_fatal_error("Bad COUNT \"%s\" on line %d.", token
, linenum
);
1538 last_expect
->count
= i
;
1541 print_fatal_error("COUNT without a preceding EXPECT on line %d.",
1547 else if (!strcasecmp(token
, "DEFINE-MATCH"))
1549 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1551 print_fatal_error("Missing DEFINE-MATCH variable on line %d.",
1558 last_expect
->define_match
= strdup(token
);
1561 print_fatal_error("DEFINE-MATCH without a preceding EXPECT on line "
1567 else if (!strcasecmp(token
, "DEFINE-NO-MATCH"))
1569 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1571 print_fatal_error("Missing DEFINE-NO-MATCH variable on line %d.",
1578 last_expect
->define_no_match
= strdup(token
);
1581 print_fatal_error("DEFINE-NO-MATCH without a preceding EXPECT on "
1582 "line %d.", linenum
);
1587 else if (!strcasecmp(token
, "DEFINE-VALUE"))
1589 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1591 print_fatal_error("Missing DEFINE-VALUE variable on line %d.",
1598 last_expect
->define_value
= strdup(token
);
1601 print_fatal_error("DEFINE-VALUE without a preceding EXPECT on line "
1607 else if (!strcasecmp(token
, "OF-TYPE"))
1609 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1611 print_fatal_error("Missing OF-TYPE value tag(s) on line %d.",
1618 last_expect
->of_type
= strdup(token
);
1621 print_fatal_error("OF-TYPE without a preceding EXPECT on line %d.",
1627 else if (!strcasecmp(token
, "IN-GROUP"))
1629 ipp_tag_t in_group
; /* IN-GROUP value */
1632 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1634 print_fatal_error("Missing IN-GROUP group tag on line %d.", linenum
);
1639 if ((in_group
= ippTagValue(token
)) == (ipp_tag_t
)-1)
1642 else if (last_expect
)
1643 last_expect
->in_group
= in_group
;
1646 print_fatal_error("IN-GROUP without a preceding EXPECT on line %d.",
1652 else if (!strcasecmp(token
, "SAME-COUNT-AS"))
1654 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1656 print_fatal_error("Missing SAME-COUNT-AS name on line %d.", linenum
);
1662 last_expect
->same_count_as
= strdup(token
);
1665 print_fatal_error("SAME-COUNT-AS without a preceding EXPECT on line "
1671 else if (!strcasecmp(token
, "IF-DEFINED"))
1673 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1675 print_fatal_error("Missing IF-DEFINED name on line %d.", linenum
);
1681 last_expect
->if_defined
= strdup(token
);
1682 else if (last_status
)
1683 last_status
->if_defined
= strdup(token
);
1686 print_fatal_error("IF-DEFINED without a preceding EXPECT or STATUS "
1687 "on line %d.", linenum
);
1692 else if (!strcasecmp(token
, "IF-NOT-DEFINED"))
1694 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1696 print_fatal_error("Missing IF-NOT-DEFINED name on line %d.", linenum
);
1702 last_expect
->if_not_defined
= strdup(token
);
1703 else if (last_status
)
1704 last_status
->if_not_defined
= strdup(token
);
1707 print_fatal_error("IF-NOT-DEFINED without a preceding EXPECT or STATUS "
1708 "on line %d.", linenum
);
1713 else if (!strcasecmp(token
, "WITH-VALUE"))
1715 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1717 print_fatal_error("Missing WITH-VALUE value on line %d.", linenum
);
1725 * Expand any variables in the value and then save it.
1728 expand_variables(vars
, token
, temp
, sizeof(token
));
1730 tokenptr
= token
+ strlen(token
) - 1;
1732 if (token
[0] == '/' && tokenptr
> token
&& *tokenptr
== '/')
1735 * WITH-VALUE is a POSIX extended regular expression.
1738 last_expect
->with_value
= calloc(1, tokenptr
- token
);
1739 last_expect
->with_regex
= 1;
1741 if (last_expect
->with_value
)
1742 memcpy(last_expect
->with_value
, token
+ 1, tokenptr
- token
- 1);
1747 * WITH-VALUE is a literal value...
1750 last_expect
->with_value
= strdup(token
);
1755 print_fatal_error("WITH-VALUE without a preceding EXPECT on line %d.",
1761 else if (!strcasecmp(token
, "DISPLAY"))
1764 * Display attributes...
1767 if (num_displayed
>= (int)(sizeof(displayed
) / sizeof(displayed
[0])))
1769 print_fatal_error("Too many DISPLAY's on line %d", linenum
);
1774 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1776 print_fatal_error("Missing DISPLAY name on line %d.", linenum
);
1781 displayed
[num_displayed
] = strdup(token
);
1786 print_fatal_error("Unexpected token %s seen on line %d.", token
,
1794 * Submit the IPP request...
1797 request
->request
.op
.version
[0] = version
/ 10;
1798 request
->request
.op
.version
[1] = version
% 10;
1799 request
->request
.op
.operation_id
= op
;
1800 request
->request
.op
.request_id
= request_id
;
1802 if (Output
== _CUPS_OUTPUT_PLIST
)
1805 puts("<key>Name</key>");
1806 print_xml_string("string", name
);
1807 puts("<key>Operation</key>");
1808 print_xml_string("string", ippOpString(op
));
1809 puts("<key>RequestAttributes</key>");
1811 for (attrptr
= request
->attrs
; attrptr
; attrptr
= attrptr
->next
)
1812 print_attr(attrptr
);
1815 else if (Output
== _CUPS_OUTPUT_TEST
)
1819 printf(" %s:\n", ippOpString(op
));
1821 for (attrptr
= request
->attrs
; attrptr
; attrptr
= attrptr
->next
)
1822 print_attr(attrptr
);
1825 printf(" %-69.69s [", name
);
1829 if ((skip_previous
&& !prev_pass
) || skip_test
)
1834 if (Output
== _CUPS_OUTPUT_PLIST
)
1836 puts("<key>Successful</key>");
1838 puts("<key>StatusCode</key>");
1839 print_xml_string("string", "skip");
1840 puts("<key>ResponseAttributes</key>");
1844 else if (Output
== _CUPS_OUTPUT_TEST
)
1850 if (transfer
== _CUPS_TRANSFER_CHUNKED
||
1851 (transfer
== _CUPS_TRANSFER_AUTO
&& filename
[0]))
1854 * Send request using chunking...
1857 http_status_t status
= cupsSendRequest(http
, request
, resource
, 0);
1859 if (status
== HTTP_CONTINUE
&& filename
[0])
1861 int fd
; /* File to send */
1862 char buffer
[8192]; /* Copy buffer */
1863 ssize_t bytes
; /* Bytes read/written */
1865 if ((fd
= open(filename
, O_RDONLY
| O_BINARY
)) >= 0)
1867 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
1868 if ((status
= cupsWriteRequestData(http
, buffer
,
1869 bytes
)) != HTTP_CONTINUE
)
1874 snprintf(buffer
, sizeof(buffer
), "%s: %s", filename
, strerror(errno
));
1875 _cupsSetError(IPP_INTERNAL_ERROR
, buffer
, 0);
1877 status
= HTTP_ERROR
;
1883 if (status
== HTTP_CONTINUE
)
1884 response
= cupsGetResponse(http
, resource
);
1888 else if (filename
[0])
1889 response
= cupsDoFileRequest(http
, request
, resource
, filename
);
1891 response
= cupsDoRequest(http
, request
, resource
);
1897 prev_pass
= pass
= 0;
1900 if (http
->version
!= HTTP_1_1
)
1901 prev_pass
= pass
= 0;
1903 if (response
->request
.status
.request_id
!= request_id
)
1904 prev_pass
= pass
= 0;
1907 (response
->request
.status
.version
[0] != (version
/ 10) ||
1908 response
->request
.status
.version
[1] != (version
% 10)))
1909 prev_pass
= pass
= 0;
1911 if ((attrptr
= ippFindAttribute(response
, "job-id",
1912 IPP_TAG_INTEGER
)) != NULL
)
1914 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
1915 set_variable(vars
, "job-id", temp
);
1918 if ((attrptr
= ippFindAttribute(response
, "job-uri",
1919 IPP_TAG_URI
)) != NULL
)
1920 set_variable(vars
, "job-uri", attrptr
->values
[0].string
.text
);
1922 if ((attrptr
= ippFindAttribute(response
, "notify-subscription-id",
1923 IPP_TAG_INTEGER
)) != NULL
)
1925 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
1926 set_variable(vars
, "notify-subscription-id", temp
);
1929 attrptr
= response
->attrs
;
1930 if (!attrptr
|| !attrptr
->name
||
1931 attrptr
->value_tag
!= IPP_TAG_CHARSET
||
1932 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
1933 attrptr
->num_values
!= 1 ||
1934 strcmp(attrptr
->name
, "attributes-charset"))
1935 prev_pass
= pass
= 0;
1939 attrptr
= attrptr
->next
;
1940 if (!attrptr
|| !attrptr
->name
||
1941 attrptr
->value_tag
!= IPP_TAG_LANGUAGE
||
1942 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
1943 attrptr
->num_values
!= 1 ||
1944 strcmp(attrptr
->name
, "attributes-natural-language"))
1945 prev_pass
= pass
= 0;
1948 if ((attrptr
= ippFindAttribute(response
, "status-message",
1949 IPP_TAG_ZERO
)) != NULL
&&
1950 (attrptr
->value_tag
!= IPP_TAG_TEXT
||
1951 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
1952 attrptr
->num_values
!= 1 ||
1953 (attrptr
->value_tag
== IPP_TAG_TEXT
&&
1954 strlen(attrptr
->values
[0].string
.text
) > 255)))
1955 prev_pass
= pass
= 0;
1957 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
1958 IPP_TAG_ZERO
)) != NULL
&&
1959 (attrptr
->value_tag
!= IPP_TAG_TEXT
||
1960 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
1961 attrptr
->num_values
!= 1 ||
1962 (attrptr
->value_tag
== IPP_TAG_TEXT
&&
1963 strlen(attrptr
->values
[0].string
.text
) > 1023)))
1964 prev_pass
= pass
= 0;
1966 for (attrptr
= response
->attrs
, group
= attrptr
->group_tag
;
1968 attrptr
= attrptr
->next
)
1970 if (attrptr
->group_tag
< group
&& attrptr
->group_tag
!= IPP_TAG_ZERO
)
1972 prev_pass
= pass
= 0;
1976 if (!validate_attr(attrptr
, 0))
1978 prev_pass
= pass
= 0;
1983 for (i
= 0; i
< num_statuses
; i
++)
1985 if (statuses
[i
].if_defined
&&
1986 !get_variable(vars
, statuses
[i
].if_defined
))
1989 if (statuses
[i
].if_not_defined
&&
1990 get_variable(vars
, statuses
[i
].if_not_defined
))
1993 if (response
->request
.status
.status_code
== statuses
[i
].status
)
1997 if (i
== num_statuses
&& num_statuses
> 0)
1998 prev_pass
= pass
= 0;
2001 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
2003 if (expect
->if_defined
&& !get_variable(vars
, expect
->if_defined
))
2006 if (expect
->if_not_defined
&&
2007 get_variable(vars
, expect
->if_not_defined
))
2010 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
2012 if ((found
&& expect
->not_expect
) ||
2013 (!found
&& !(expect
->not_expect
|| expect
->optional
)) ||
2014 (found
&& !expect_matches(expect
, found
->value_tag
)) ||
2015 (found
&& expect
->in_group
&&
2016 found
->group_tag
!= expect
->in_group
))
2018 if (expect
->define_no_match
)
2019 set_variable(vars
, expect
->define_no_match
, "1");
2020 else if (!expect
->define_match
)
2021 prev_pass
= pass
= 0;
2027 !with_value(expect
->with_value
, expect
->with_regex
, found
, 0))
2029 if (expect
->define_no_match
)
2030 set_variable(vars
, expect
->define_no_match
, "1");
2031 else if (!expect
->define_match
)
2032 prev_pass
= pass
= 0;
2037 if (found
&& expect
->count
> 0 && found
->num_values
!= expect
->count
)
2039 if (expect
->define_no_match
)
2040 set_variable(vars
, expect
->define_no_match
, "1");
2041 else if (!expect
->define_match
)
2042 prev_pass
= pass
= 0;
2047 if (found
&& expect
->same_count_as
)
2049 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
2052 if (!attrptr
|| attrptr
->num_values
!= found
->num_values
)
2054 if (expect
->define_no_match
)
2055 set_variable(vars
, expect
->define_no_match
, "1");
2056 else if (!expect
->define_match
)
2057 prev_pass
= pass
= 0;
2063 if (found
&& expect
->define_match
)
2064 set_variable(vars
, expect
->define_match
, "1");
2066 if (found
&& expect
->define_value
)
2068 _ippAttrString(found
, token
, sizeof(token
));
2069 set_variable(vars
, expect
->define_value
, token
);
2075 if (Output
== _CUPS_OUTPUT_PLIST
)
2077 puts("<key>Successful</key>");
2078 puts(prev_pass
? "<true />" : "<false />");
2079 puts("<key>StatusCode</key>");
2080 print_xml_string("string", ippErrorString(cupsLastError()));
2081 puts("<key>ResponseAttributes</key>");
2083 for (attrptr
= response
? response
->attrs
: NULL
;
2085 attrptr
= attrptr
->next
)
2086 print_attr(attrptr
);
2089 else if (Output
== _CUPS_OUTPUT_TEST
)
2091 puts(prev_pass
? "PASS]" : "FAIL]");
2093 if (Verbosity
&& response
)
2095 printf(" RECEIVED: %lu bytes in response\n",
2096 (unsigned long)ippLength(response
));
2097 printf(" status-code = %x (%s)\n", cupsLastError(),
2098 ippErrorString(cupsLastError()));
2100 for (attrptr
= response
->attrs
;
2102 attrptr
= attrptr
->next
)
2104 print_attr(attrptr
);
2108 else if (!prev_pass
)
2109 fprintf(stderr
, "%s\n", cupsLastErrorString());
2111 if (prev_pass
&& Output
!= _CUPS_OUTPUT_PLIST
&&
2112 Output
!= _CUPS_OUTPUT_QUIET
&& !Verbosity
&& num_displayed
> 0)
2114 if (Output
>= _CUPS_OUTPUT_LIST
)
2116 size_t width
; /* Length of value */
2119 for (i
= 0; i
< num_displayed
; i
++)
2121 widths
[i
] = strlen(displayed
[i
]);
2123 for (attrptr
= ippFindAttribute(response
, displayed
[i
], IPP_TAG_ZERO
);
2125 attrptr
= ippFindNextAttribute(response
, displayed
[i
],
2128 width
= _ippAttrString(attrptr
, NULL
, 0);
2129 if (width
> widths
[i
])
2134 if (Output
== _CUPS_OUTPUT_CSV
)
2135 print_csv(NULL
, num_displayed
, displayed
, widths
);
2137 print_line(NULL
, num_displayed
, displayed
, widths
);
2139 attrptr
= response
->attrs
;
2143 while (attrptr
&& attrptr
->group_tag
<= IPP_TAG_OPERATION
)
2144 attrptr
= attrptr
->next
;
2148 if (Output
== _CUPS_OUTPUT_CSV
)
2149 print_csv(attrptr
, num_displayed
, displayed
, widths
);
2151 print_line(attrptr
, num_displayed
, displayed
, widths
);
2153 while (attrptr
&& attrptr
->group_tag
> IPP_TAG_OPERATION
)
2154 attrptr
= attrptr
->next
;
2160 for (attrptr
= response
->attrs
;
2162 attrptr
= attrptr
->next
)
2166 for (i
= 0; i
< num_displayed
; i
++)
2168 if (!strcmp(displayed
[i
], attrptr
->name
))
2170 print_attr(attrptr
);
2178 else if (!prev_pass
)
2180 if (Output
== _CUPS_OUTPUT_PLIST
)
2182 puts("<key>Errors</key>");
2186 if (http
->version
!= HTTP_1_1
)
2187 print_test_error("Bad HTTP version (%d.%d)", http
->version
/ 100,
2188 http
->version
% 100);
2191 print_test_error("IPP request failed with status %s (%s)",
2192 ippErrorString(cupsLastError()),
2193 cupsLastErrorString());
2197 (response
->request
.status
.version
[0] != (version
/ 10) ||
2198 response
->request
.status
.version
[1] != (version
% 10)))
2199 print_test_error("Bad version %d.%d in response - expected %d.%d "
2200 "(RFC 2911 section 3.1.8).",
2201 response
->request
.status
.version
[0],
2202 response
->request
.status
.version
[1],
2203 version
/ 10, version
% 10);
2205 if (response
->request
.status
.request_id
!= request_id
)
2206 print_test_error("Bad request ID %d in response - expected %d "
2207 "(RFC 2911 section 3.1.1)",
2208 response
->request
.status
.request_id
, request_id
);
2210 attrptr
= response
->attrs
;
2212 print_test_error("Missing first attribute \"attributes-charset "
2213 "(charset)\" in group operation-attributes-tag "
2214 "(RFC 2911 section 3.1.4).");
2217 if (!attrptr
->name
||
2218 attrptr
->value_tag
!= IPP_TAG_CHARSET
||
2219 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2220 attrptr
->num_values
!= 1 ||
2221 strcmp(attrptr
->name
, "attributes-charset"))
2222 print_test_error("Bad first attribute \"%s (%s%s)\" in group %s, "
2223 "expected \"attributes-charset (charset)\" in "
2224 "group operation-attributes-tag (RFC 2911 section "
2226 attrptr
->name
? attrptr
->name
: "(null)",
2227 attrptr
->num_values
> 1 ? "1setOf " : "",
2228 ippTagString(attrptr
->value_tag
),
2229 ippTagString(attrptr
->group_tag
));
2231 attrptr
= attrptr
->next
;
2233 print_test_error("Missing second attribute \"attributes-natural-"
2234 "language (naturalLanguage)\" in group "
2235 "operation-attributes-tag (RFC 2911 section "
2237 else if (!attrptr
->name
||
2238 attrptr
->value_tag
!= IPP_TAG_LANGUAGE
||
2239 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2240 attrptr
->num_values
!= 1 ||
2241 strcmp(attrptr
->name
, "attributes-natural-language"))
2242 print_test_error("Bad first attribute \"%s (%s%s)\" in group %s, "
2243 "expected \"attributes-natural-language "
2244 "(naturalLanguage)\" in group "
2245 "operation-attributes-tag (RFC 2911 section "
2247 attrptr
->name
? attrptr
->name
: "(null)",
2248 attrptr
->num_values
> 1 ? "1setOf " : "",
2249 ippTagString(attrptr
->value_tag
),
2250 ippTagString(attrptr
->group_tag
));
2253 if ((attrptr
= ippFindAttribute(response
, "status-message",
2254 IPP_TAG_ZERO
)) != NULL
)
2256 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2257 print_test_error("status-message (text(255)) has wrong value tag "
2258 "%s (RFC 2911 section 3.1.6.2).",
2259 ippTagString(attrptr
->value_tag
));
2260 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2261 print_test_error("status-message (text(255)) has wrong group tag "
2262 "%s (RFC 2911 section 3.1.6.2).",
2263 ippTagString(attrptr
->group_tag
));
2264 if (attrptr
->num_values
!= 1)
2265 print_test_error("status-message (text(255)) has %d values "
2266 "(RFC 2911 section 3.1.6.2).",
2267 attrptr
->num_values
);
2268 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2269 strlen(attrptr
->values
[0].string
.text
) > 255)
2270 print_test_error("status-message (text(255)) has bad length %d"
2271 " (RFC 2911 section 3.1.6.2).",
2272 (int)strlen(attrptr
->values
[0].string
.text
));
2275 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
2276 IPP_TAG_ZERO
)) != NULL
)
2278 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2279 print_test_error("detailed-status-message (text(MAX)) has wrong "
2280 "value tag %s (RFC 2911 section 3.1.6.3).",
2281 ippTagString(attrptr
->value_tag
));
2282 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2283 print_test_error("detailed-status-message (text(MAX)) has wrong "
2284 "group tag %s (RFC 2911 section 3.1.6.3).",
2285 ippTagString(attrptr
->group_tag
));
2286 if (attrptr
->num_values
!= 1)
2287 print_test_error("detailed-status-message (text(MAX)) has %d values"
2288 " (RFC 2911 section 3.1.6.3).",
2289 attrptr
->num_values
);
2290 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2291 strlen(attrptr
->values
[0].string
.text
) > 1023)
2292 print_test_error("detailed-status-message (text(MAX)) has bad "
2293 "length %d (RFC 2911 section 3.1.6.3).",
2294 (int)strlen(attrptr
->values
[0].string
.text
));
2297 for (attrptr
= response
->attrs
, group
= attrptr
->group_tag
;
2299 attrptr
= attrptr
->next
)
2301 if (attrptr
->group_tag
< group
&& attrptr
->group_tag
!= IPP_TAG_ZERO
)
2302 print_test_error("Attribute groups out of order (%s < %s)",
2303 ippTagString(attrptr
->group_tag
),
2304 ippTagString(group
));
2306 validate_attr(attrptr
, 1);
2309 for (i
= 0; i
< num_statuses
; i
++)
2311 if (statuses
[i
].if_defined
&&
2312 !get_variable(vars
, statuses
[i
].if_defined
))
2315 if (statuses
[i
].if_not_defined
&&
2316 get_variable(vars
, statuses
[i
].if_not_defined
))
2319 if (response
->request
.status
.status_code
== statuses
[i
].status
)
2323 if (i
== num_statuses
&& num_statuses
> 0)
2325 print_test_error("Bad status-code (%s)",
2326 ippErrorString(cupsLastError()));
2327 print_test_error("status-message=\"%s\"", cupsLastErrorString());
2330 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
2332 if (expect
->define_match
|| expect
->define_no_match
)
2335 if (expect
->if_defined
&& !get_variable(vars
, expect
->if_defined
))
2338 if (expect
->if_not_defined
&&
2339 get_variable(vars
, expect
->if_not_defined
))
2342 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
2344 if (found
&& expect
->not_expect
)
2345 print_test_error("NOT EXPECTED: %s", expect
->name
);
2346 else if (!found
&& !(expect
->not_expect
|| expect
->optional
))
2347 print_test_error("EXPECTED: %s", expect
->name
);
2350 if (!expect_matches(expect
, found
->value_tag
))
2351 print_test_error("EXPECTED: %s OF-TYPE %s (got %s)",
2352 expect
->name
, expect
->of_type
,
2353 ippTagString(found
->value_tag
));
2355 if (expect
->in_group
&& found
->group_tag
!= expect
->in_group
)
2356 print_test_error("EXPECTED: %s IN-GROUP %s (got %s).",
2357 expect
->name
, ippTagString(expect
->in_group
),
2358 ippTagString(found
->group_tag
));
2360 if (!with_value(expect
->with_value
, expect
->with_regex
, found
, 0))
2362 if (expect
->with_regex
)
2363 print_test_error("EXPECTED: %s WITH-VALUE /%s/",
2364 expect
->name
, expect
->with_value
);
2366 print_test_error("EXPECTED: %s WITH-VALUE \"%s\"",
2367 expect
->name
, expect
->with_value
);
2369 with_value(expect
->with_value
, expect
->with_regex
, found
, 1);
2372 if (expect
->count
> 0 && found
->num_values
!= expect
->count
)
2374 print_test_error("EXPECTED: %s COUNT %d (got %d)", expect
->name
,
2375 expect
->count
, found
->num_values
);
2378 if (expect
->same_count_as
)
2380 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
2384 print_test_error("EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2385 "(not returned)", expect
->name
,
2386 found
->num_values
, expect
->same_count_as
);
2387 else if (attrptr
->num_values
!= found
->num_values
)
2388 print_test_error("EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2389 "(%d values)", expect
->name
, found
->num_values
,
2390 expect
->same_count_as
, attrptr
->num_values
);
2396 if (Output
== _CUPS_OUTPUT_PLIST
)
2400 if (Output
== _CUPS_OUTPUT_PLIST
)
2405 ippDelete(response
);
2408 for (i
= 0; i
< num_statuses
; i
++)
2410 if (statuses
[i
].if_defined
)
2411 free(statuses
[i
].if_defined
);
2412 if (statuses
[i
].if_not_defined
)
2413 free(statuses
[i
].if_not_defined
);
2417 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
2420 if (expect
->of_type
)
2421 free(expect
->of_type
);
2422 if (expect
->same_count_as
)
2423 free(expect
->same_count_as
);
2424 if (expect
->if_defined
)
2425 free(expect
->if_defined
);
2426 if (expect
->if_not_defined
)
2427 free(expect
->if_not_defined
);
2428 if (expect
->with_value
)
2429 free(expect
->with_value
);
2430 if (expect
->define_match
)
2431 free(expect
->define_match
);
2432 if (expect
->define_no_match
)
2433 free(expect
->define_no_match
);
2434 if (expect
->define_value
)
2435 free(expect
->define_value
);
2439 for (i
= 0; i
< num_displayed
; i
++)
2443 if (!ignore_errors
&& !prev_pass
)
2454 ippDelete(response
);
2456 for (i
= 0; i
< num_statuses
; i
++)
2458 if (statuses
[i
].if_defined
)
2459 free(statuses
[i
].if_defined
);
2460 if (statuses
[i
].if_not_defined
)
2461 free(statuses
[i
].if_not_defined
);
2464 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
2467 if (expect
->of_type
)
2468 free(expect
->of_type
);
2469 if (expect
->same_count_as
)
2470 free(expect
->same_count_as
);
2471 if (expect
->if_defined
)
2472 free(expect
->if_defined
);
2473 if (expect
->if_not_defined
)
2474 free(expect
->if_not_defined
);
2475 if (expect
->with_value
)
2476 free(expect
->with_value
);
2477 if (expect
->define_match
)
2478 free(expect
->define_match
);
2479 if (expect
->define_no_match
)
2480 free(expect
->define_no_match
);
2481 if (expect
->define_value
)
2482 free(expect
->define_value
);
2485 for (i
= 0; i
< num_displayed
; i
++)
2493 * 'expand_variables()' - Expand variables in a string.
2497 expand_variables(_cups_vars_t
*vars
, /* I - Variables */
2498 char *dst
, /* I - Destination string buffer */
2499 const char *src
, /* I - Source string */
2500 size_t dstsize
) /* I - Size of destination buffer */
2502 char *dstptr
, /* Pointer into destination */
2503 *dstend
, /* End of destination */
2504 temp
[256], /* Temporary string */
2505 *tempptr
; /* Pointer into temporary string */
2506 const char *value
; /* Value to substitute */
2510 dstend
= dst
+ dstsize
- 1;
2512 while (*src
&& dstptr
< dstend
)
2517 * Substitute a string/number...
2520 if (!strncmp(src
, "$$", 2))
2525 else if (!strncmp(src
, "$ENV[", 5))
2527 strlcpy(temp
, src
+ 5, sizeof(temp
));
2529 for (tempptr
= temp
; *tempptr
; tempptr
++)
2530 if (*tempptr
== ']')
2536 value
= getenv(temp
);
2537 src
+= tempptr
- temp
+ 5;
2541 strlcpy(temp
, src
+ 1, sizeof(temp
));
2543 for (tempptr
= temp
; *tempptr
; tempptr
++)
2544 if (!isalnum(*tempptr
& 255) && *tempptr
!= '-' && *tempptr
!= '_')
2550 if (!strcmp(temp
, "uri"))
2552 else if (!strcmp(temp
, "filename"))
2553 value
= vars
->filename
;
2554 else if (!strcmp(temp
, "scheme") || !strcmp(temp
, "method"))
2555 value
= vars
->scheme
;
2556 else if (!strcmp(temp
, "username"))
2557 value
= vars
->userpass
;
2558 else if (!strcmp(temp
, "hostname"))
2559 value
= vars
->hostname
;
2560 else if (!strcmp(temp
, "port"))
2562 snprintf(temp
, sizeof(temp
), "%d", vars
->port
);
2565 else if (!strcmp(temp
, "resource"))
2566 value
= vars
->resource
;
2567 else if (!strcmp(temp
, "user"))
2570 value
= get_variable(vars
, temp
);
2572 src
+= tempptr
- temp
+ 1;
2582 strlcpy(dstptr
, value
, dstend
- dstptr
+ 1);
2583 dstptr
+= strlen(dstptr
);
2595 * 'expect_matches()' - Return true if the tag matches the specification.
2598 static int /* O - 1 if matches, 0 otherwise */
2600 _cups_expect_t
*expect
, /* I - Expected attribute */
2601 ipp_tag_t value_tag
) /* I - Value tag for attribute */
2603 int match
; /* Match? */
2604 char *of_type
, /* Type name to match */
2605 *next
, /* Next name to match */
2606 sep
; /* Separator character */
2610 * If we don't expect a particular type, return immediately...
2613 if (!expect
->of_type
)
2617 * Parse the "of_type" value since the string can contain multiple attribute
2618 * types separated by "," or "|"...
2621 for (of_type
= expect
->of_type
, match
= 0; !match
&& *of_type
; of_type
= next
)
2624 * Find the next separator, and set it (temporarily) to nul if present.
2627 for (next
= of_type
; *next
&& *next
!= '|' && *next
!= ','; next
++);
2629 if ((sep
= *next
) != '\0')
2633 * Support some meta-types to make it easier to write the test file.
2636 if (!strcmp(of_type
, "text"))
2637 match
= value_tag
== IPP_TAG_TEXTLANG
|| value_tag
== IPP_TAG_TEXT
;
2638 else if (!strcmp(of_type
, "name"))
2639 match
= value_tag
== IPP_TAG_NAMELANG
|| value_tag
== IPP_TAG_NAME
;
2640 else if (!strcmp(of_type
, "collection"))
2641 match
= value_tag
== IPP_TAG_BEGIN_COLLECTION
;
2643 match
= value_tag
== ippTagValue(of_type
);
2646 * Restore the separator if we have one...
2658 * 'get_collection()' - Get a collection value from the current test file.
2661 static ipp_t
* /* O - Collection value */
2662 get_collection(_cups_vars_t
*vars
, /* I - Variables */
2663 FILE *fp
, /* I - File to read from */
2664 int *linenum
) /* IO - Line number */
2666 char token
[1024], /* Token from file */
2667 temp
[1024], /* Temporary string */
2668 attr
[128]; /* Attribute name */
2669 ipp_tag_t value
; /* Current value type */
2670 ipp_t
*col
= ippNew(); /* Collection value */
2671 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
2674 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
2676 if (!strcmp(token
, "}"))
2678 else if (!strcmp(token
, "{") && lastcol
)
2681 * Another collection value
2684 ipp_t
*subcol
= get_collection(vars
, fp
, linenum
);
2685 /* Collection value */
2689 ipp_attribute_t
*tempcol
; /* Pointer to new buffer */
2693 * Reallocate memory...
2696 if ((tempcol
= realloc(lastcol
, sizeof(ipp_attribute_t
) +
2697 (lastcol
->num_values
+ 1) *
2698 sizeof(ipp_value_t
))) == NULL
)
2700 print_fatal_error("Unable to allocate memory on line %d.", *linenum
);
2704 if (tempcol
!= lastcol
)
2707 * Reset pointers in the list...
2711 col
->prev
->next
= tempcol
;
2713 col
->attrs
= tempcol
;
2715 lastcol
= col
->current
= col
->last
= tempcol
;
2718 lastcol
->values
[lastcol
->num_values
].collection
= subcol
;
2719 lastcol
->num_values
++;
2724 else if (!strcasecmp(token
, "MEMBER"))
2732 if (!get_token(fp
, token
, sizeof(token
), linenum
))
2734 print_fatal_error("Missing MEMBER value tag on line %d.", *linenum
);
2738 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
2740 print_fatal_error("Bad MEMBER value tag \"%s\" on line %d.", token
,
2745 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
2747 print_fatal_error("Missing MEMBER name on line %d.", *linenum
);
2751 if (!get_token(fp
, temp
, sizeof(temp
), linenum
))
2753 print_fatal_error("Missing MEMBER value on line %d.", *linenum
);
2757 expand_variables(vars
, token
, temp
, sizeof(token
));
2761 case IPP_TAG_BOOLEAN
:
2762 if (!strcasecmp(token
, "true"))
2763 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
2765 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, atoi(token
));
2768 case IPP_TAG_INTEGER
:
2770 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
2773 case IPP_TAG_RESOLUTION
:
2775 int xres
, /* X resolution */
2776 yres
; /* Y resolution */
2777 char units
[6]; /* Units */
2779 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
2780 (strcasecmp(units
, "dpi") && strcasecmp(units
, "dpc") &&
2781 strcasecmp(units
, "other")))
2783 print_fatal_error("Bad resolution value \"%s\" on line %d.",
2788 if (!strcasecmp(units
, "dpi"))
2789 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
2791 else if (!strcasecmp(units
, "dpc"))
2792 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
2795 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
2800 case IPP_TAG_RANGE
:
2802 int lowers
[4], /* Lower value */
2803 uppers
[4], /* Upper values */
2804 num_vals
; /* Number of values */
2807 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
2808 lowers
+ 0, uppers
+ 0,
2809 lowers
+ 1, uppers
+ 1,
2810 lowers
+ 2, uppers
+ 2,
2811 lowers
+ 3, uppers
+ 3);
2813 if ((num_vals
& 1) || num_vals
== 0)
2815 print_fatal_error("Bad rangeOfInteger value \"%s\" on line %d.",
2820 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
2825 case IPP_TAG_BEGIN_COLLECTION
:
2826 if (!strcmp(token
, "{"))
2828 ipp_t
*subcol
= get_collection(vars
, fp
, linenum
);
2829 /* Collection value */
2833 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
2841 print_fatal_error("Bad collection value on line %d.", *linenum
);
2847 if (!strchr(token
, ','))
2848 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
2852 * Multiple string values...
2855 int num_values
; /* Number of values */
2856 char *values
[100], /* Values */
2857 *ptr
; /* Pointer to next value */
2863 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
2866 values
[num_values
] = ptr
;
2870 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
2871 NULL
, (const char **)values
);
2881 * If we get here there was a parse error; free memory and return.
2893 * 'get_filename()' - Get a filename based on the current test file.
2896 static char * /* O - Filename */
2897 get_filename(const char *testfile
, /* I - Current test file */
2898 char *dst
, /* I - Destination filename */
2899 const char *src
, /* I - Source filename */
2900 size_t dstsize
) /* I - Size of destination buffer */
2902 char *dstptr
; /* Pointer into destination */
2903 _cups_globals_t
*cg
= _cupsGlobals();
2907 if (*src
== '<' && src
[strlen(src
) - 1] == '>')
2910 * Map <filename> to CUPS_DATADIR/ipptool/filename...
2913 snprintf(dst
, dstsize
, "%s/ipptool/%s", cg
->cups_datadir
, src
+ 1);
2914 dstptr
= dst
+ strlen(dst
) - 1;
2918 else if (*src
== '/' || !strchr(testfile
, '/'))
2921 * Use the path as-is...
2924 strlcpy(dst
, src
, dstsize
);
2929 * Make path relative to testfile...
2932 strlcpy(dst
, testfile
, dstsize
);
2933 if ((dstptr
= strrchr(dst
, '/')) != NULL
)
2936 dstptr
= dst
; /* Should never happen */
2938 strlcpy(dstptr
, src
, dstsize
- (dstptr
- dst
));
2946 * 'get_token()' - Get a token from a file.
2949 static char * /* O - Token from file or NULL on EOF */
2950 get_token(FILE *fp
, /* I - File to read from */
2951 char *buf
, /* I - Buffer to read into */
2952 int buflen
, /* I - Length of buffer */
2953 int *linenum
) /* IO - Current line number */
2955 int ch
, /* Character from file */
2956 quote
; /* Quoting character */
2957 char *bufptr
, /* Pointer into buffer */
2958 *bufend
; /* End of buffer */
2964 * Skip whitespace...
2967 while (isspace(ch
= getc(fp
)))
2979 else if (ch
== '\'' || ch
== '\"')
2982 * Quoted text or regular expression...
2987 bufend
= buf
+ buflen
- 1;
2989 while ((ch
= getc(fp
)) != EOF
)
2994 * Escape next character...
2997 if (bufptr
< bufend
)
3000 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
3003 else if (ch
== quote
)
3005 else if (bufptr
< bufend
)
3019 while ((ch
= getc(fp
)) != EOF
)
3028 * Whitespace delimited text...
3034 bufend
= buf
+ buflen
- 1;
3036 while ((ch
= getc(fp
)) != EOF
)
3037 if (isspace(ch
) || ch
== '#')
3039 else if (bufptr
< bufend
)
3044 else if (ch
== '\n')
3056 * 'get_variable()' - Get the value of a variable.
3059 static char * /* O - Value or NULL */
3060 get_variable(_cups_vars_t
*vars
, /* I - Variables */
3061 const char *name
) /* I - Variable name */
3063 _cups_var_t key
, /* Search key */
3064 *match
; /* Matching variable, if any */
3067 key
.name
= (char *)name
;
3068 match
= cupsArrayFind(vars
->vars
, &key
);
3070 return (match
? match
->value
: NULL
);
3075 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
3079 static char * /* O - ISO 8601 date/time string */
3080 iso_date(ipp_uchar_t
*date
) /* I - IPP (RFC 1903) date/time value */
3082 unsigned year
= (date
[0] << 8) + date
[1];
3084 static char buffer
[255]; /* String buffer */
3087 if (date
[9] == 0 && date
[10] == 0)
3088 snprintf(buffer
, sizeof(buffer
), "%04u-%02u-%02uT%02u:%02u:%02uZ",
3089 year
, date
[2], date
[3], date
[4], date
[5], date
[6]);
3091 snprintf(buffer
, sizeof(buffer
), "%04u-%02u-%02uT%02u:%02u:%02u%c%02u%02u",
3092 year
, date
[2], date
[3], date
[4], date
[5], date
[6],
3093 date
[8], date
[9], date
[10]);
3100 * 'password_cb()' - Password callback for authenticated tests.
3103 static const char * /* O - Password */
3104 password_cb(const char *prompt
) /* I - Prompt (unused) */
3113 * 'print_attr()' - Print an attribute on the screen.
3117 print_attr(ipp_attribute_t
*attr
) /* I - Attribute to print */
3119 int i
; /* Looping var */
3120 ipp_attribute_t
*colattr
; /* Collection attribute */
3123 if (Output
== _CUPS_OUTPUT_PLIST
)
3127 printf("<key>%s</key>\n<true />\n", ippTagString(attr
->group_tag
));
3131 print_xml_string("key", attr
->name
);
3132 if (attr
->num_values
> 1)
3135 else if (Output
== _CUPS_OUTPUT_TEST
)
3139 puts(" -- separator --");
3143 printf(" %s (%s%s) = ", attr
->name
,
3144 attr
->num_values
> 1 ? "1setOf " : "",
3145 ippTagString(attr
->value_tag
));
3148 switch (attr
->value_tag
)
3150 case IPP_TAG_INTEGER
:
3152 for (i
= 0; i
< attr
->num_values
; i
++)
3153 if (Output
== _CUPS_OUTPUT_PLIST
)
3154 printf("<integer>%d</integer>\n", attr
->values
[i
].integer
);
3156 printf("%d ", attr
->values
[i
].integer
);
3159 case IPP_TAG_BOOLEAN
:
3160 for (i
= 0; i
< attr
->num_values
; i
++)
3161 if (Output
== _CUPS_OUTPUT_PLIST
)
3162 puts(attr
->values
[i
].boolean
? "<true />" : "<false />");
3163 else if (attr
->values
[i
].boolean
)
3164 fputs("true ", stdout
);
3166 fputs("false ", stdout
);
3169 case IPP_TAG_RANGE
:
3170 for (i
= 0; i
< attr
->num_values
; i
++)
3171 if (Output
== _CUPS_OUTPUT_PLIST
)
3172 printf("<dict><key>lower</key><integer>%d</integer>"
3173 "<key>upper</key><integer>%d</integer></dict>\n",
3174 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
3176 printf("%d-%d ", attr
->values
[i
].range
.lower
,
3177 attr
->values
[i
].range
.upper
);
3180 case IPP_TAG_RESOLUTION
:
3181 for (i
= 0; i
< attr
->num_values
; i
++)
3182 if (Output
== _CUPS_OUTPUT_PLIST
)
3183 printf("<dict><key>xres</key><integer>%d</integer>"
3184 "<key>yres</key><integer>%d</integer>"
3185 "<key>units</key><string>%s</string></dict>\n",
3186 attr
->values
[i
].resolution
.xres
,
3187 attr
->values
[i
].resolution
.yres
,
3188 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
3191 printf("%dx%d%s ", attr
->values
[i
].resolution
.xres
,
3192 attr
->values
[i
].resolution
.yres
,
3193 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
3198 for (i
= 0; i
< attr
->num_values
; i
++)
3199 if (Output
== _CUPS_OUTPUT_PLIST
)
3200 printf("<date>%s</date>\n", iso_date(attr
->values
[i
].date
));
3202 printf("%s ", iso_date(attr
->values
[i
].date
));
3205 case IPP_TAG_STRING
:
3208 case IPP_TAG_KEYWORD
:
3209 case IPP_TAG_CHARSET
:
3211 case IPP_TAG_MIMETYPE
:
3212 case IPP_TAG_LANGUAGE
:
3213 for (i
= 0; i
< attr
->num_values
; i
++)
3214 if (Output
== _CUPS_OUTPUT_PLIST
)
3215 print_xml_string("string", attr
->values
[i
].string
.text
);
3217 printf("\"%s\" ", attr
->values
[i
].string
.text
);
3220 case IPP_TAG_TEXTLANG
:
3221 case IPP_TAG_NAMELANG
:
3222 for (i
= 0; i
< attr
->num_values
; i
++)
3223 if (Output
== _CUPS_OUTPUT_PLIST
)
3225 fputs("<dict><key>language</key><string>", stdout
);
3226 print_xml_string(NULL
, attr
->values
[i
].string
.charset
);
3227 fputs("</string><key>string</key><string>", stdout
);
3228 print_xml_string(NULL
, attr
->values
[i
].string
.text
);
3229 puts("</string></dict>");
3232 printf("\"%s\",%s ", attr
->values
[i
].string
.text
,
3233 attr
->values
[i
].string
.charset
);
3236 case IPP_TAG_BEGIN_COLLECTION
:
3237 for (i
= 0; i
< attr
->num_values
; i
++)
3239 if (Output
== _CUPS_OUTPUT_PLIST
)
3242 for (colattr
= attr
->values
[i
].collection
->attrs
;
3244 colattr
= colattr
->next
)
3245 print_attr(colattr
);
3253 print_col(attr
->values
[i
].collection
);
3259 if (Output
== _CUPS_OUTPUT_PLIST
)
3260 printf("<string><<%s>></string>\n",
3261 ippTagString(attr
->value_tag
));
3263 fputs(ippTagString(attr
->value_tag
), stdout
);
3267 if (Output
== _CUPS_OUTPUT_PLIST
)
3269 if (attr
->num_values
> 1)
3278 * 'print_col()' - Print a collection attribute on the screen.
3282 print_col(ipp_t
*col
) /* I - Collection attribute to print */
3284 int i
; /* Looping var */
3285 ipp_attribute_t
*attr
; /* Current attribute in collection */
3288 fputs("{ ", stdout
);
3289 for (attr
= col
->attrs
; attr
; attr
= attr
->next
)
3291 printf("%s (%s%s) = ", attr
->name
, attr
->num_values
> 1 ? "1setOf " : "",
3292 ippTagString(attr
->value_tag
));
3294 switch (attr
->value_tag
)
3296 case IPP_TAG_INTEGER
:
3298 for (i
= 0; i
< attr
->num_values
; i
++)
3299 printf("%d ", attr
->values
[i
].integer
);
3302 case IPP_TAG_BOOLEAN
:
3303 for (i
= 0; i
< attr
->num_values
; i
++)
3304 if (attr
->values
[i
].boolean
)
3310 case IPP_TAG_NOVALUE
:
3314 case IPP_TAG_RANGE
:
3315 for (i
= 0; i
< attr
->num_values
; i
++)
3316 printf("%d-%d ", attr
->values
[i
].range
.lower
,
3317 attr
->values
[i
].range
.upper
);
3320 case IPP_TAG_RESOLUTION
:
3321 for (i
= 0; i
< attr
->num_values
; i
++)
3322 printf("%dx%d%s ", attr
->values
[i
].resolution
.xres
,
3323 attr
->values
[i
].resolution
.yres
,
3324 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
3328 case IPP_TAG_STRING
:
3331 case IPP_TAG_KEYWORD
:
3332 case IPP_TAG_CHARSET
:
3334 case IPP_TAG_MIMETYPE
:
3335 case IPP_TAG_LANGUAGE
:
3336 for (i
= 0; i
< attr
->num_values
; i
++)
3337 printf("\"%s\" ", attr
->values
[i
].string
.text
);
3340 case IPP_TAG_TEXTLANG
:
3341 case IPP_TAG_NAMELANG
:
3342 for (i
= 0; i
< attr
->num_values
; i
++)
3343 printf("\"%s\",%s ", attr
->values
[i
].string
.text
,
3344 attr
->values
[i
].string
.charset
);
3347 case IPP_TAG_BEGIN_COLLECTION
:
3348 for (i
= 0; i
< attr
->num_values
; i
++)
3350 print_col(attr
->values
[i
].collection
);
3356 break; /* anti-compiler-warning-code */
3365 * 'print_csv()' - Print a line of CSV text.
3370 ipp_attribute_t
*attr
, /* I - First attribute for line */
3371 int num_displayed
, /* I - Number of attributes to display */
3372 char **displayed
, /* I - Attributes to display */
3373 size_t *widths
) /* I - Column widths */
3375 int i
; /* Looping var */
3376 size_t maxlength
; /* Max length of all columns */
3377 char *buffer
, /* String buffer */
3378 *bufptr
; /* Pointer into buffer */
3379 ipp_attribute_t
*current
; /* Current attribute */
3383 * Get the maximum string length we have to show and allocate...
3386 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
3387 if (widths
[i
] > maxlength
)
3388 maxlength
= widths
[i
];
3392 if ((buffer
= malloc(maxlength
)) == NULL
)
3396 * Loop through the attributes to display...
3401 for (i
= 0; i
< num_displayed
; i
++)
3408 for (current
= attr
; current
; current
= current
->next
)
3412 else if (!strcmp(current
->name
, displayed
[i
]))
3414 _ippAttrString(current
, buffer
, maxlength
);
3419 if (strchr(buffer
, ',') != NULL
|| strchr(buffer
, '\"') != NULL
||
3420 strchr(buffer
, '\\') != NULL
)
3423 for (bufptr
= buffer
; *bufptr
; bufptr
++)
3425 if (*bufptr
== '\\' || *bufptr
== '\"')
3432 fputs(buffer
, stdout
);
3438 for (i
= 0; i
< num_displayed
; i
++)
3443 fputs(displayed
[i
], stdout
);
3453 * 'print_fatal_error()' - Print a fatal error message.
3457 print_fatal_error(const char *s
, /* I - Printf-style format string */
3458 ...) /* I - Additional arguments as needed */
3460 char buffer
[10240]; /* Format buffer */
3461 va_list ap
; /* Pointer to arguments */
3465 * Format the error message...
3469 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
3476 if (Output
== _CUPS_OUTPUT_PLIST
)
3479 print_xml_trailer(0, buffer
);
3482 _cupsLangPrintf(stderr
, "ipptool: %s\n", buffer
);
3487 * 'print_line()' - Print a line of formatted or CSV text.
3492 ipp_attribute_t
*attr
, /* I - First attribute for line */
3493 int num_displayed
, /* I - Number of attributes to display */
3494 char **displayed
, /* I - Attributes to display */
3495 size_t *widths
) /* I - Column widths */
3497 int i
; /* Looping var */
3498 size_t maxlength
; /* Max length of all columns */
3499 char *buffer
; /* String buffer */
3500 ipp_attribute_t
*current
; /* Current attribute */
3504 * Get the maximum string length we have to show and allocate...
3507 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
3508 if (widths
[i
] > maxlength
)
3509 maxlength
= widths
[i
];
3513 if ((buffer
= malloc(maxlength
)) == NULL
)
3517 * Loop through the attributes to display...
3522 for (i
= 0; i
< num_displayed
; i
++)
3529 for (current
= attr
; current
; current
= current
->next
)
3533 else if (!strcmp(current
->name
, displayed
[i
]))
3535 _ippAttrString(current
, buffer
, maxlength
);
3540 printf("%*s", (int)-widths
[i
], buffer
);
3546 for (i
= 0; i
< num_displayed
; i
++)
3551 printf("%*s", (int)-widths
[i
], displayed
[i
]);
3555 for (i
= 0; i
< num_displayed
; i
++)
3560 memset(buffer
, '-', widths
[i
]);
3561 buffer
[widths
[i
]] = '\0';
3562 fputs(buffer
, stdout
);
3572 * 'print_test_error()' - Print a test error message.
3576 print_test_error(const char *s
, /* I - Printf-style format string */
3577 ...) /* I - Additional arguments as needed */
3579 char buffer
[10240]; /* Format buffer */
3580 va_list ap
; /* Pointer to arguments */
3584 * Format the error message...
3588 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
3595 if (Output
== _CUPS_OUTPUT_PLIST
)
3596 print_xml_string("string", buffer
);
3598 printf(" %s\n", buffer
);
3603 * 'print_xml_header()' - Print a standard XML plist header.
3607 print_xml_header(void)
3611 puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
3612 puts("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
3613 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
3614 puts("<plist version=\"1.0\">");
3616 puts("<key>Transfer</key>");
3617 printf("<string>%s</string>\n",
3618 Transfer
== _CUPS_TRANSFER_AUTO
? "auto" :
3619 Transfer
== _CUPS_TRANSFER_CHUNKED
? "chunked" : "length");
3620 puts("<key>Tests</key>");
3629 * 'print_xml_string()' - Print an XML string with escaping.
3633 print_xml_string(const char *element
, /* I - Element name or NULL */
3634 const char *s
) /* I - String to print */
3637 printf("<%s>", element
);
3642 fputs("&", stdout
);
3644 fputs("<", stdout
);
3646 fputs(">", stdout
);
3654 printf("</%s>\n", element
);
3659 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
3663 print_xml_trailer(int success
, /* I - 1 on success, 0 on failure */
3664 const char *message
) /* I - Error message or NULL */
3669 puts("<key>Successful</key>");
3670 puts(success
? "<true />" : "<false />");
3673 puts("<key>ErrorMessage</key>");
3674 print_xml_string("string", message
);
3685 * 'set_variable()' - Set a variable value.
3689 set_variable(_cups_vars_t
*vars
, /* I - Variables */
3690 const char *name
, /* I - Variable name */
3691 const char *value
) /* I - Value string */
3693 _cups_var_t key
, /* Search key */
3694 *var
; /* New variable */
3697 key
.name
= (char *)name
;
3698 if ((var
= cupsArrayFind(vars
->vars
, &key
)) != NULL
)
3701 var
->value
= strdup(value
);
3703 else if ((var
= malloc(sizeof(_cups_var_t
))) == NULL
)
3705 print_fatal_error("Unable to allocate memory for variable \"%s\".", name
);
3710 var
->name
= strdup(name
);
3711 var
->value
= strdup(value
);
3713 cupsArrayAdd(vars
->vars
, var
);
3719 * 'timeout_cb()' - Handle HTTP timeouts.
3722 static int /* O - 1 to continue, 0 to cancel */
3723 timeout_cb(http_t
*http
, /* I - Connection to server (unused) */
3724 void *user_data
) /* I - User data (unused) */
3734 * 'usage()' - Show program usage.
3740 _cupsLangPuts(stderr
,
3741 _("Usage: ipptool [options] URI filename [ ... "
3746 "-C Send requests using chunking (default)\n"
3747 "-E Test with TLS encryption.\n"
3748 "-I Ignore errors\n"
3749 "-L Send requests using content-length\n"
3750 "-S Test with SSL encryption.\n"
3751 "-T Set the receive/send timeout in seconds.\n"
3752 "-V version Set default IPP version.\n"
3753 "-X Produce XML plist instead of plain text.\n"
3754 "-d name=value Define variable.\n"
3755 "-f filename Set default request filename.\n"
3756 "-i seconds Repeat the last file with the given time "
3758 "-n count Repeat the last file the given number of "
3760 "-q Be quiet - no output except errors.\n"
3761 "-t Produce a test report.\n"
3762 "-v Show all attributes sent and received.\n"));
3769 * 'validate_attr()' - Determine whether an attribute is valid.
3772 static int /* O - 1 if valid, 0 otherwise */
3773 validate_attr(ipp_attribute_t
*attr
, /* I - Attribute to validate */
3774 int print
) /* I - 1 = report issues to stdout */
3776 int i
; /* Looping var */
3777 char scheme
[64], /* Scheme from URI */
3778 userpass
[256], /* Username/password from URI */
3779 hostname
[256], /* Hostname from URI */
3780 resource
[1024]; /* Resource from URI */
3781 int port
, /* Port number from URI */
3782 uri_status
, /* URI separation status */
3783 valid
= 1; /* Is the attribute valid? */
3784 const char *ptr
; /* Pointer into string */
3785 ipp_attribute_t
*colattr
; /* Collection attribute */
3786 regex_t re
; /* Regular expression */
3787 ipp_uchar_t
*date
; /* Current date value */
3798 * Validate the attribute name.
3801 for (ptr
= attr
->name
; *ptr
; ptr
++)
3802 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' && *ptr
!= '_')
3805 if (*ptr
|| ptr
== attr
->name
)
3810 print_test_error("\"%s\": Bad attribute name - invalid character (RFC "
3811 "2911 section 4.1.3).", attr
->name
);
3814 if ((ptr
- attr
->name
) > 255)
3819 print_test_error("\"%s\": Bad attribute name - bad length (RFC 2911 "
3820 "section 4.1.3).", attr
->name
);
3823 switch (attr
->value_tag
)
3825 case IPP_TAG_INTEGER
:
3828 case IPP_TAG_BOOLEAN
:
3829 for (i
= 0; i
< attr
->num_values
; i
++)
3831 if (attr
->values
[i
].boolean
!= 0 &&
3832 attr
->values
[i
].boolean
!= 1)
3837 print_test_error("\"%s\": Bad boolen value %d (RFC 2911 section "
3838 "4.1.10).", attr
->name
, attr
->values
[i
].boolean
);
3846 for (i
= 0; i
< attr
->num_values
; i
++)
3848 if (attr
->values
[i
].integer
< 1)
3853 print_test_error("\"%s\": Bad enum value %d - out of range "
3854 "(RFC 2911 section 4.1.4).", attr
->name
,
3855 attr
->values
[i
].integer
);
3862 case IPP_TAG_STRING
:
3863 for (i
= 0; i
< attr
->num_values
; i
++)
3865 if (attr
->values
[i
].unknown
.length
> 1023)
3870 print_test_error("\"%s\": Bad octetString value - bad length %d "
3871 "(RFC 2911 section 4.1.10).", attr
->name
,
3872 attr
->values
[i
].unknown
.length
);
3880 for (i
= 0; i
< attr
->num_values
; i
++)
3882 date
= attr
->values
[i
].date
;
3884 if (date
[2] < 1 || date
[2] > 12)
3889 print_test_error("\"%s\": Bad dateTime month %u (RFC 2911 "
3890 "section 4.1.13).", attr
->name
, date
[2]);
3895 if (date
[3] < 1 || date
[3] > 31)
3900 print_test_error("\"%s\": Bad dateTime day %u (RFC 2911 "
3901 "section 4.1.13).", attr
->name
, date
[3]);
3911 print_test_error("\"%s\": Bad dateTime hours %u (RFC 2911 "
3912 "section 4.1.13).", attr
->name
, date
[4]);
3922 print_test_error("\"%s\": Bad dateTime minutes %u (RFC 2911 "
3923 "section 4.1.13).", attr
->name
, date
[5]);
3933 print_test_error("\"%s\": Bad dateTime seconds %u (RFC 2911 "
3934 "section 4.1.13).", attr
->name
, date
[6]);
3944 print_test_error("\"%s\": Bad dateTime deciseconds %u (RFC 2911 "
3945 "section 4.1.13).", attr
->name
, date
[7]);
3950 if (date
[8] != '-' && date
[8] != '+')
3955 print_test_error("\"%s\": Bad dateTime UTC sign '%c' (RFC 2911 "
3956 "section 4.1.13).", attr
->name
, date
[8]);
3966 print_test_error("\"%s\": Bad dateTime UTC hours %u (RFC 2911 "
3967 "section 4.1.13).", attr
->name
, date
[9]);
3977 print_test_error("\"%s\": Bad dateTime UTC minutes %u (RFC 2911 "
3978 "section 4.1.13).", attr
->name
, date
[10]);
3985 case IPP_TAG_RESOLUTION
:
3986 for (i
= 0; i
< attr
->num_values
; i
++)
3988 if (attr
->values
[i
].resolution
.xres
<= 0)
3993 print_test_error("\"%s\": Bad resolution value %dx%d%s - cross "
3994 "feed resolution must be positive (RFC 2911 "
3995 "section 4.1.13).", attr
->name
,
3996 attr
->values
[i
].resolution
.xres
,
3997 attr
->values
[i
].resolution
.yres
,
3998 attr
->values
[i
].resolution
.units
==
3999 IPP_RES_PER_INCH
? "dpi" :
4000 attr
->values
[i
].resolution
.units
==
4001 IPP_RES_PER_CM
? "dpc" : "unknown");
4006 if (attr
->values
[i
].resolution
.yres
<= 0)
4011 print_test_error("\"%s\": Bad resolution value %dx%d%s - feed "
4012 "resolution must be positive (RFC 2911 section "
4013 "4.1.13).", attr
->name
,
4014 attr
->values
[i
].resolution
.xres
,
4015 attr
->values
[i
].resolution
.yres
,
4016 attr
->values
[i
].resolution
.units
==
4017 IPP_RES_PER_INCH
? "dpi" :
4018 attr
->values
[i
].resolution
.units
==
4019 IPP_RES_PER_CM
? "dpc" : "unknown");
4024 if (attr
->values
[i
].resolution
.units
!= IPP_RES_PER_INCH
&&
4025 attr
->values
[i
].resolution
.units
!= IPP_RES_PER_CM
)
4030 print_test_error("\"%s\": Bad resolution value %dx%d%s - bad "
4031 "units value (RFC 2911 section 4.1.13).",
4032 attr
->name
, attr
->values
[i
].resolution
.xres
,
4033 attr
->values
[i
].resolution
.yres
,
4034 attr
->values
[i
].resolution
.units
==
4035 IPP_RES_PER_INCH
? "dpi" :
4036 attr
->values
[i
].resolution
.units
==
4037 IPP_RES_PER_CM
? "dpc" : "unknown");
4044 case IPP_TAG_RANGE
:
4045 for (i
= 0; i
< attr
->num_values
; i
++)
4047 if (attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
4052 print_test_error("\"%s\": Bad rangeOfInteger value %d-%d - lower "
4053 "greater than upper (RFC 2911 section 4.1.13).",
4054 attr
->name
, attr
->values
[i
].range
.lower
,
4055 attr
->values
[i
].range
.upper
);
4062 case IPP_TAG_BEGIN_COLLECTION
:
4063 for (i
= 0; i
< attr
->num_values
; i
++)
4065 for (colattr
= attr
->values
[i
].collection
->attrs
;
4067 colattr
= colattr
->next
)
4069 if (!validate_attr(colattr
, 0))
4076 if (colattr
&& print
)
4078 print_test_error("\"%s\": Bad collection value.", attr
->name
);
4082 validate_attr(colattr
, print
);
4083 colattr
= colattr
->next
;
4090 case IPP_TAG_TEXTLANG
:
4091 for (i
= 0; i
< attr
->num_values
; i
++)
4093 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4095 if ((*ptr
& 0xe0) == 0xc0)
4098 if ((*ptr
& 0xc0) != 0x80)
4101 else if ((*ptr
& 0xf0) == 0xe0)
4104 if ((*ptr
& 0xc0) != 0x80)
4107 if ((*ptr
& 0xc0) != 0x80)
4110 else if ((*ptr
& 0xf8) == 0xf0)
4113 if ((*ptr
& 0xc0) != 0x80)
4116 if ((*ptr
& 0xc0) != 0x80)
4119 if ((*ptr
& 0xc0) != 0x80)
4122 else if (*ptr
& 0x80)
4131 print_test_error("\"%s\": Bad text value \"%s\" - bad UTF-8 "
4132 "sequence (RFC 2911 section 4.1.1).", attr
->name
,
4133 attr
->values
[i
].string
.text
);
4138 if ((ptr
- attr
->values
[i
].string
.text
) > 1023)
4143 print_test_error("\"%s\": Bad text value \"%s\" - bad length %d "
4144 "(RFC 2911 section 4.1.1).", attr
->name
,
4145 attr
->values
[i
].string
.text
,
4146 (int)strlen(attr
->values
[i
].string
.text
));
4154 case IPP_TAG_NAMELANG
:
4155 for (i
= 0; i
< attr
->num_values
; i
++)
4157 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4159 if ((*ptr
& 0xe0) == 0xc0)
4162 if ((*ptr
& 0xc0) != 0x80)
4165 else if ((*ptr
& 0xf0) == 0xe0)
4168 if ((*ptr
& 0xc0) != 0x80)
4171 if ((*ptr
& 0xc0) != 0x80)
4174 else if ((*ptr
& 0xf8) == 0xf0)
4177 if ((*ptr
& 0xc0) != 0x80)
4180 if ((*ptr
& 0xc0) != 0x80)
4183 if ((*ptr
& 0xc0) != 0x80)
4186 else if (*ptr
& 0x80)
4195 print_test_error("\"%s\": Bad name value \"%s\" - bad UTF-8 "
4196 "sequence (RFC 2911 section 4.1.2).", attr
->name
,
4197 attr
->values
[i
].string
.text
);
4202 if ((ptr
- attr
->values
[i
].string
.text
) > 1023)
4207 print_test_error("\"%s\": Bad name value \"%s\" - bad length %d "
4208 "(RFC 2911 section 4.1.2).", attr
->name
,
4209 attr
->values
[i
].string
.text
,
4210 (int)strlen(attr
->values
[i
].string
.text
));
4217 case IPP_TAG_KEYWORD
:
4218 for (i
= 0; i
< attr
->num_values
; i
++)
4220 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4221 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' &&
4225 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
4230 print_test_error("\"%s\": Bad keyword value \"%s\" - invalid "
4231 "character (RFC 2911 section 4.1.3).",
4232 attr
->name
, attr
->values
[i
].string
.text
);
4237 if ((ptr
- attr
->values
[i
].string
.text
) > 255)
4242 print_test_error("\"%s\": Bad keyword value \"%s\" - bad "
4243 "length %d (RFC 2911 section 4.1.3).",
4244 attr
->name
, attr
->values
[i
].string
.text
,
4245 (int)strlen(attr
->values
[i
].string
.text
));
4253 for (i
= 0; i
< attr
->num_values
; i
++)
4255 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
,
4256 attr
->values
[i
].string
.text
,
4257 scheme
, sizeof(scheme
),
4258 userpass
, sizeof(userpass
),
4259 hostname
, sizeof(hostname
),
4260 &port
, resource
, sizeof(resource
));
4262 if (uri_status
< HTTP_URI_OK
)
4267 print_test_error("\"%s\": Bad URI value \"%s\" - %s "
4268 "(RFC 2911 section 4.1.5).", attr
->name
,
4269 attr
->values
[i
].string
.text
,
4270 URIStatusStrings
[uri_status
-
4271 HTTP_URI_OVERFLOW
]);
4276 if (strlen(attr
->values
[i
].string
.text
) > 1023)
4281 print_test_error("\"%s\": Bad URI value \"%s\" - bad length %d "
4282 "(RFC 2911 section 4.1.5).", attr
->name
,
4283 attr
->values
[i
].string
.text
,
4284 (int)strlen(attr
->values
[i
].string
.text
));
4291 case IPP_TAG_URISCHEME
:
4292 for (i
= 0; i
< attr
->num_values
; i
++)
4294 ptr
= attr
->values
[i
].string
.text
;
4295 if (islower(*ptr
& 255))
4297 for (ptr
++; *ptr
; ptr
++)
4298 if (!islower(*ptr
& 255) && !isdigit(*ptr
& 255) &&
4299 *ptr
!= '+' && *ptr
!= '-' && *ptr
!= '.')
4303 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
4308 print_test_error("\"%s\": Bad uriScheme value \"%s\" - bad "
4309 "characters (RFC 2911 section 4.1.6).",
4310 attr
->name
, attr
->values
[i
].string
.text
);
4315 if ((ptr
- attr
->values
[i
].string
.text
) > 63)
4320 print_test_error("\"%s\": Bad uriScheme value \"%s\" - bad "
4321 "length %d (RFC 2911 section 4.1.6).",
4322 attr
->name
, attr
->values
[i
].string
.text
,
4323 (int)strlen(attr
->values
[i
].string
.text
));
4330 case IPP_TAG_CHARSET
:
4331 for (i
= 0; i
< attr
->num_values
; i
++)
4333 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
4334 if (!isprint(*ptr
& 255) || isupper(*ptr
& 255) ||
4335 isspace(*ptr
& 255))
4338 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
4343 print_test_error("\"%s\": Bad charset value \"%s\" - bad "
4344 "characters (RFC 2911 section 4.1.7).",
4345 attr
->name
, attr
->values
[i
].string
.text
);
4350 if ((ptr
- attr
->values
[i
].string
.text
) > 40)
4355 print_test_error("\"%s\": Bad charset value \"%s\" - bad "
4356 "length %d (RFC 2911 section 4.1.7).",
4357 attr
->name
, attr
->values
[i
].string
.text
,
4358 (int)strlen(attr
->values
[i
].string
.text
));
4365 case IPP_TAG_LANGUAGE
:
4367 * The following regular expression is derived from the ABNF for
4368 * language tags in RFC 4646. All I can say is that this is the
4369 * easiest way to check the values...
4372 if ((i
= regcomp(&re
,
4374 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
4376 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
4377 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
4378 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
4379 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
4380 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
4382 "x(-[a-z0-9]{1,8})+" /* privateuse */
4384 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
4386 REG_NOSUB
| REG_EXTENDED
)) != 0)
4388 char temp
[256]; /* Temporary error string */
4390 regerror(i
, &re
, temp
, sizeof(temp
));
4391 print_fatal_error("Unable to compile naturalLanguage regular "
4392 "expression: %s.", temp
);
4395 for (i
= 0; i
< attr
->num_values
; i
++)
4397 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
4402 print_test_error("\"%s\": Bad naturalLanguage value \"%s\" - bad "
4403 "characters (RFC 2911 section 4.1.8).",
4404 attr
->name
, attr
->values
[i
].string
.text
);
4409 if (strlen(attr
->values
[i
].string
.text
) > 63)
4414 print_test_error("\"%s\": Bad naturalLanguage value \"%s\" - bad "
4415 "length %d (RFC 2911 section 4.1.8).",
4416 attr
->name
, attr
->values
[i
].string
.text
,
4417 (int)strlen(attr
->values
[i
].string
.text
));
4426 case IPP_TAG_MIMETYPE
:
4428 * The following regular expression is derived from the ABNF for
4429 * language tags in RFC 2045 and 4288. All I can say is that this is
4430 * the easiest way to check the values...
4433 if ((i
= regcomp(&re
,
4435 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
4437 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
4438 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
4439 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
4442 REG_NOSUB
| REG_EXTENDED
)) != 0)
4444 char temp
[256]; /* Temporary error string */
4446 regerror(i
, &re
, temp
, sizeof(temp
));
4447 print_fatal_error("Unable to compile mimeMediaType regular "
4448 "expression: %s.", temp
);
4451 for (i
= 0; i
< attr
->num_values
; i
++)
4453 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
4458 print_test_error("\"%s\": Bad mimeMediaType value \"%s\" - bad "
4459 "characters (RFC 2911 section 4.1.9).",
4460 attr
->name
, attr
->values
[i
].string
.text
);
4465 if (strlen(attr
->values
[i
].string
.text
) > 255)
4470 print_test_error("\"%s\": Bad mimeMediaType value \"%s\" - bad "
4471 "length %d (RFC 2911 section 4.1.9).",
4472 attr
->name
, attr
->values
[i
].string
.text
,
4473 (int)strlen(attr
->values
[i
].string
.text
));
4489 * 'with_value()' - Test a WITH-VALUE predicate.
4492 static int /* O - 1 on match, 0 on non-match */
4493 with_value(char *value
, /* I - Value string */
4494 int regex
, /* I - Value is a regular expression */
4495 ipp_attribute_t
*attr
, /* I - Attribute to compare */
4496 int report
) /* I - 1 = report failures */
4498 int i
; /* Looping var */
4499 char *valptr
; /* Pointer into value */
4503 * NULL matches everything.
4506 if (!value
|| !*value
)
4510 * Compare the value string to the attribute value.
4513 switch (attr
->value_tag
)
4515 case IPP_TAG_INTEGER
:
4517 for (i
= 0; i
< attr
->num_values
; i
++)
4519 char op
, /* Comparison operator */
4520 *nextptr
; /* Next pointer */
4521 int intvalue
; /* Integer value */
4525 if (!strncmp(valptr
, "no-value,", 9))
4528 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
4529 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
4530 *valptr
== '=' || *valptr
== '>')
4533 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
4535 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
4543 intvalue
= strtol(valptr
, &nextptr
, 0);
4544 if (nextptr
== valptr
)
4551 if (attr
->values
[i
].integer
== intvalue
)
4555 if (attr
->values
[i
].integer
< intvalue
)
4559 if (attr
->values
[i
].integer
> intvalue
)
4568 for (i
= 0; i
< attr
->num_values
; i
++)
4569 print_test_error("GOT: %s=%d", attr
->name
, attr
->values
[i
].integer
);
4573 case IPP_TAG_RANGE
:
4574 for (i
= 0; i
< attr
->num_values
; i
++)
4576 char op
, /* Comparison operator */
4577 *nextptr
; /* Next pointer */
4578 int intvalue
; /* Integer value */
4582 if (!strncmp(valptr
, "no-value,", 9))
4585 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
4586 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
4587 *valptr
== '=' || *valptr
== '>')
4590 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
4592 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
4600 intvalue
= strtol(valptr
, &nextptr
, 0);
4601 if (nextptr
== valptr
)
4608 if (attr
->values
[i
].range
.upper
== intvalue
)
4612 if (attr
->values
[i
].range
.upper
< intvalue
)
4616 if (attr
->values
[i
].range
.upper
> intvalue
)
4625 for (i
= 0; i
< attr
->num_values
; i
++)
4626 print_test_error("GOT: %s=%d-%d", attr
->name
,
4627 attr
->values
[i
].range
.lower
,
4628 attr
->values
[i
].range
.upper
);
4632 case IPP_TAG_BOOLEAN
:
4633 for (i
= 0; i
< attr
->num_values
; i
++)
4635 if (!strcmp(value
, "true") == attr
->values
[i
].boolean
)
4641 for (i
= 0; i
< attr
->num_values
; i
++)
4642 print_test_error("GOT: %s=%s", attr
->name
,
4643 attr
->values
[i
].boolean
? "true" : "false");
4647 case IPP_TAG_NOVALUE
:
4648 return (!strcmp(value
, "no-value") || !strncmp(value
, "no-value,", 9));
4650 case IPP_TAG_CHARSET
:
4651 case IPP_TAG_KEYWORD
:
4652 case IPP_TAG_LANGUAGE
:
4653 case IPP_TAG_MIMETYPE
:
4655 case IPP_TAG_NAMELANG
:
4657 case IPP_TAG_TEXTLANG
:
4659 case IPP_TAG_URISCHEME
:
4663 * Value is an extended, case-sensitive POSIX regular expression...
4666 regex_t re
; /* Regular expression */
4668 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
4670 char temp
[256]; /* Temporary string */
4672 regerror(i
, &re
, temp
, sizeof(temp
));
4674 print_fatal_error("Unable to compile WITH-VALUE regular expression "
4675 "\"%s\" - %s", value
, temp
);
4680 * See if ALL of the values match the given regular expression.
4683 for (i
= 0; i
< attr
->num_values
; i
++)
4685 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
4688 print_test_error("GOT: %s=\"%s\"", attr
->name
,
4689 attr
->values
[i
].string
.text
);
4697 return (i
== attr
->num_values
);
4702 * Value is a literal string, see if at least one value matches the
4706 for (i
= 0; i
< attr
->num_values
; i
++)
4708 if (!strcmp(value
, attr
->values
[i
].string
.text
))
4714 for (i
= 0; i
< attr
->num_values
; i
++)
4715 print_test_error("GOT: %s=\"%s\"", attr
->name
,
4716 attr
->values
[i
].string
.text
);