2 * "$Id: ipptest.c 9000 2010-02-24 06:29:38Z mike $"
4 * IPP test command for the Common UNIX Printing System (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 * print_attr() - Print an attribute on the screen.
29 * print_col() - Print a collection attribute on the screen.
30 * print_fatal_error() - Print a fatal error message.
31 * print_test_error() - Print a test error message.
32 * print_xml_header() - Print a standard XML plist header.
33 * print_xml_string() - Print an XML string with escaping.
34 * print_xml_trailer() - Print the XML trailer with success/fail value.
35 * set_variable() - Set a variable value.
36 * usage() - Show program usage.
37 * validate_attr() - Determine whether an attribute is valid.
38 * with_value() - Test a WITH-VALUE predicate.
42 * Include necessary headers...
51 #include <cups/globals.h>
54 #endif /* !O_BINARY */
61 typedef enum /**** 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 struct _cups_expect_s
/**** Expected attribute info ****/
70 int optional
, /* Optional attribute? */
71 not_expect
; /* Don't expect attribute? */
72 char *name
, /* Attribute name */
73 *of_type
, /* Type name */
74 *same_count_as
, /* Parallel attribute name */
75 *if_defined
, /* Only required if variable defined */
76 *if_undefined
, /* Only required if variable is not defined */
77 *with_value
; /* Attribute must include this value */
78 int with_regex
, /* WITH-VALUE is a regular expression */
79 count
; /* Expected count if > 0 */
80 ipp_tag_t in_group
; /* IN-GROUP value */
83 typedef struct _cups_status_s
/**** Status info ****/
85 ipp_status_t status
; /* Expected status code */
86 char *if_defined
, /* Only if variable is defined */
87 *if_undefined
; /* Only if variable is not defined */
90 typedef struct _cups_var_s
/**** Variable ****/
92 char *name
, /* Name of variable */
93 *value
; /* Value of variable */
96 typedef struct _cups_vars_s
/**** Set of variables ****/
98 const char *uri
, /* URI for printer */
99 *filename
; /* Filename */
100 char scheme
[64], /* Scheme from URI */
101 userpass
[256], /* Username/password from URI */
102 hostname
[256], /* Hostname from URI */
103 resource
[1024]; /* Resource path from URI */
104 int port
; /* Port number from URI */
105 http_encryption_t encryption
; /* Encryption for connection? */
106 cups_array_t
*vars
; /* Array of variables */
114 _cups_transfer_t Transfer
= _CUPS_TRANSFER_AUTO
;
115 /* How to transfer requests */
116 int Verbosity
= 0, /* Show all attributes? */
117 Version
= 11, /* Default IPP version */
118 XML
= 0, /* Produce XML output? */
119 XMLHeader
= 0; /* 1 if header is written */
120 const char * const URIStatusStrings
[] = /* URI status strings */
123 "Bad arguments to function",
124 "Bad resource in URI",
125 "Bad port number in URI",
126 "Bad hostname/address in URI",
127 "Bad username in URI",
131 "Missing scheme in URI",
132 "Unknown scheme in URI",
133 "Missing resource in URI"
141 static int compare_vars(_cups_var_t
*a
, _cups_var_t
*b
);
142 static int do_tests(_cups_vars_t
*vars
, const char *testfile
);
143 static void expand_variables(_cups_vars_t
*vars
, char *dst
, const char *src
,
146 __attribute((nonnull(1,2,3)))
147 #endif /* __GNUC__ */
149 static int expect_matches(_cups_expect_t
*expect
, ipp_tag_t value_tag
);
150 static ipp_t
*get_collection(_cups_vars_t
*vars
, FILE *fp
, int *linenum
);
151 static char *get_filename(const char *testfile
, char *dst
, const char *src
,
153 static char *get_token(FILE *fp
, char *buf
, int buflen
,
155 static char *get_variable(_cups_vars_t
*vars
, const char *name
);
156 static char *iso_date(ipp_uchar_t
*date
);
157 static void print_attr(ipp_attribute_t
*attr
);
158 static void print_col(ipp_t
*col
);
159 static void print_fatal_error(const char *s
, ...)
161 __attribute__ ((__format__ (__printf__
, 1, 2)))
162 #endif /* __GNUC__ */
164 static void print_test_error(const char *s
, ...)
166 __attribute__ ((__format__ (__printf__
, 1, 2)))
167 #endif /* __GNUC__ */
169 static void print_xml_header(void);
170 static void print_xml_string(const char *element
, const char *s
);
171 static void print_xml_trailer(int success
, const char *message
);
172 static void set_variable(_cups_vars_t
*vars
, const char *name
,
174 static void usage(void);
175 static int validate_attr(ipp_attribute_t
*attr
, int print
);
176 static int with_value(char *value
, int regex
, ipp_attribute_t
*attr
);
180 * 'main()' - Parse options and do tests.
183 int /* O - Exit status */
184 main(int argc
, /* I - Number of command-line args */
185 char *argv
[]) /* I - Command-line arguments */
187 int i
; /* Looping var */
188 int status
; /* Status of tests... */
189 char *opt
, /* Current option */
190 name
[1024], /* Name/value buffer */
191 *value
; /* Pointer to value */
192 const char *testfile
; /* Test file to use */
193 int interval
; /* Test interval */
194 _cups_vars_t vars
; /* Variables */
195 http_uri_status_t uri_status
; /* URI separation status */
199 * Initialize the locale and variables...
202 _cupsSetLocale(argv
);
204 memset(&vars
, 0, sizeof(vars
));
205 vars
.vars
= cupsArrayNew((cups_array_func_t
)compare_vars
, NULL
);
210 * ipptest URI testfile
217 for (i
= 1; i
< argc
; i
++)
219 if (argv
[i
][0] == '-')
221 for (opt
= argv
[i
] + 1; *opt
; opt
++)
225 case 'E' : /* Encrypt */
227 vars
.encryption
= HTTP_ENCRYPT_REQUIRED
;
229 _cupsLangPrintf(stderr
,
230 _("%s: Sorry, no encryption support compiled in\n"),
232 #endif /* HAVE_SSL */
235 case 'V' : /* Set IPP version */
240 _cupsLangPuts(stderr
,
241 _("ipptest: Missing version for \"-V\".\n"));
245 if (!strcmp(argv
[i
], "1.0"))
247 else if (!strcmp(argv
[i
], "1.1"))
249 else if (!strcmp(argv
[i
], "2.0"))
251 else if (!strcmp(argv
[i
], "2.1"))
253 else if (!strcmp(argv
[i
], "2.2"))
257 _cupsLangPrintf(stderr
,
258 _("ipptest: Bad version %s for \"-V\".\n"),
264 case 'X' : /* Produce XML output */
269 _cupsLangPuts(stderr
, _("ipptest: \"-i\" is incompatible with "
275 case 'c' : /* Enable HTTP chunking */
276 Transfer
= _CUPS_TRANSFER_CHUNKED
;
279 case 'd' : /* Define a variable */
284 _cupsLangPuts(stderr
,
285 _("ipptest: Missing name=value for \"-d\".\n"));
289 strlcpy(name
, argv
[i
], sizeof(name
));
290 if ((value
= strchr(name
, '=')) != NULL
)
293 value
= name
+ strlen(name
);
295 set_variable(&vars
, name
, value
);
298 case 'f' : /* Set the default test filename */
303 _cupsLangPuts(stderr
,
304 _("ipptest: Missing filename for \"-f\".\n"));
308 vars
.filename
= argv
[i
];
311 case 'i' : /* Test every N seconds */
316 _cupsLangPuts(stderr
,
317 _("ipptest: Missing seconds for \"-i\".\n"));
321 interval
= atoi(argv
[i
]);
325 _cupsLangPuts(stderr
, _("ipptest: \"-i\" is incompatible with "
331 case 'l' : /* Disable HTTP chunking */
332 Transfer
= _CUPS_TRANSFER_LENGTH
;
335 case 'v' : /* Be verbose */
340 _cupsLangPrintf(stderr
, _("ipptest: Unknown option \"-%c\".\n"),
347 else if (!strncmp(argv
[i
], "ipp://", 6) ||
348 !strncmp(argv
[i
], "http://", 7) ||
349 !strncmp(argv
[i
], "https://", 8))
357 _cupsLangPuts(stderr
, _("ipptest: May only specify a single URI.\n"));
362 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, vars
.uri
,
363 vars
.scheme
, sizeof(vars
.scheme
),
364 vars
.userpass
, sizeof(vars
.userpass
),
365 vars
.hostname
, sizeof(vars
.hostname
),
367 vars
.resource
, sizeof(vars
.resource
));
369 if (uri_status
!= HTTP_URI_OK
)
371 _cupsLangPrintf(stderr
, _("ipptest: Bad URI - %s.\n"),
372 URIStatusStrings
[uri_status
- HTTP_URI_OVERFLOW
]);
376 if (strcmp(vars
.scheme
, "http") && strcmp(vars
.scheme
, "https") &&
377 strcmp(vars
.scheme
, "ipp"))
379 _cupsLangPuts(stderr
, _("ipptest: Only http, https, and ipp URIs are "
392 _cupsLangPuts(stderr
, _("ipptest: URI required before test file."));
398 if (!do_tests(&vars
, testfile
))
403 if (!vars
.uri
|| !testfile
)
407 * Loop if the interval is set...
411 print_xml_trailer(!status
, NULL
);
417 do_tests(&vars
, testfile
);
430 * 'compare_vars()' - Compare two variables.
433 static int /* O - Result of comparison */
434 compare_vars(_cups_var_t
*a
, /* I - First variable */
435 _cups_var_t
*b
) /* I - Second variable */
437 return (strcasecmp(a
->name
, b
->name
));
442 * 'do_tests()' - Do tests as specified in the test file.
445 static int /* 1 = success, 0 = failure */
446 do_tests(_cups_vars_t
*vars
, /* I - Variables */
447 const char *testfile
) /* I - Test file to use */
449 int i
, /* Looping var */
450 linenum
, /* Current line number */
451 pass
, /* Did we pass the test? */
452 request_id
; /* Current request ID */
453 http_t
*http
= NULL
; /* HTTP connection to server */
454 FILE *fp
= NULL
; /* Test file */
455 char resource
[512], /* Resource for request */
456 token
[1024], /* Token from file */
457 *tokenptr
, /* Pointer into token */
458 temp
[1024]; /* Temporary string */
459 ipp_t
*request
= NULL
; /* IPP request */
460 ipp_t
*response
= NULL
; /* IPP response */
461 char attr
[128]; /* Attribute name */
462 ipp_op_t op
; /* Operation */
463 ipp_tag_t group
; /* Current group */
464 ipp_tag_t value
; /* Current value type */
465 ipp_attribute_t
*attrptr
, /* Attribute pointer */
466 *found
, /* Found attribute */
467 *lastcol
= NULL
; /* Last collection attribute */
468 char name
[1024]; /* Name of test */
469 char filename
[1024]; /* Filename */
470 _cups_transfer_t transfer
; /* To chunk or not to chunk */
471 int version
; /* IPP version number to use */
472 int num_statuses
= 0; /* Number of valid status codes */
473 _cups_status_t statuses
[100], /* Valid status codes */
474 *last_status
; /* Last STATUS (for predicates) */
475 int num_expects
= 0; /* Number of expected attributes */
476 _cups_expect_t expects
[200], /* Expected attributes */
477 *expect
, /* Current expected attribute */
478 *last_expect
; /* Last EXPECT (for predicates) */
479 int num_displayed
= 0; /* Number of displayed attributes */
480 char *displayed
[100]; /* Displayed attributes */
484 * Open the test file...
487 if ((fp
= fopen(testfile
, "r")) == NULL
)
489 print_fatal_error("Unable to open test file %s - %s", testfile
,
495 * Connect to the server...
498 if ((http
= httpConnectEncrypt(vars
->hostname
, vars
->port
,
499 vars
->encryption
)) == NULL
)
501 print_fatal_error("Unable to connect to %s on port %d - %s", vars
->hostname
,
502 vars
->port
, strerror(errno
));
513 printf("\"%s\":\n", testfile
);
515 CUPS_SRAND(time(NULL
));
519 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
521 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
524 * Expect an open brace...
527 if (!strcmp(token
, "DEFINE"))
533 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
534 get_token(fp
, temp
, sizeof(temp
), &linenum
))
536 expand_variables(vars
, token
, temp
, sizeof(token
));
537 set_variable(vars
, attr
, token
);
541 print_fatal_error("Missing DEFINE name and/or value on line %d.",
548 else if (!strcmp(token
, "INCLUDE"))
555 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
558 * Map the filename to and then run the tests...
561 if (!do_tests(vars
, get_filename(testfile
, filename
, temp
,
567 print_fatal_error("Missing INCLUDE filename on line %d.", linenum
);
573 else if (!strcmp(token
, "TRANSFER"))
581 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
583 if (!strcmp(temp
, "auto"))
584 Transfer
= _CUPS_TRANSFER_AUTO
;
585 else if (!strcmp(temp
, "chunked"))
586 Transfer
= _CUPS_TRANSFER_CHUNKED
;
587 else if (!strcmp(temp
, "length"))
588 Transfer
= _CUPS_TRANSFER_LENGTH
;
591 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp
,
598 print_fatal_error("Missing TRANSFER value on line %d.", linenum
);
604 else if (!strcmp(token
, "VERSION"))
606 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
608 if (!strcmp(temp
, "1.0"))
610 else if (!strcmp(temp
, "1.1"))
612 else if (!strcmp(temp
, "2.0"))
614 else if (!strcmp(temp
, "2.1"))
616 else if (!strcmp(temp
, "2.2"))
620 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp
, linenum
);
626 print_fatal_error("Missing VERSION number on line %d.", linenum
);
632 else if (strcmp(token
, "{"))
634 print_fatal_error("Unexpected token %s seen on line %d.", token
, linenum
);
639 * Initialize things...
642 strlcpy(resource
, vars
->resource
, sizeof(resource
));
647 group
= IPP_TAG_ZERO
;
654 strlcpy(name
, testfile
, sizeof(name
));
655 if (strrchr(name
, '.') != NULL
)
656 *strrchr(name
, '.') = '\0';
659 * Parse until we see a close brace...
662 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
664 if (strcasecmp(token
, "COUNT") &&
665 strcasecmp(token
, "IF-DEFINED") &&
666 strcasecmp(token
, "IF-UNDEFINED") &&
667 strcasecmp(token
, "IN-GROUP") &&
668 strcasecmp(token
, "OF-TYPE") &&
669 strcasecmp(token
, "SAME-COUNT-AS") &&
670 strcasecmp(token
, "WITH-VALUE"))
673 if (strcasecmp(token
, "IF-DEFINED") &&
674 strcasecmp(token
, "IF-UNDEFINED"))
677 if (!strcmp(token
, "}"))
679 else if (!strcmp(token
, "{") && lastcol
)
682 * Another collection value
685 ipp_t
*col
= get_collection(vars
, fp
, &linenum
);
686 /* Collection value */
690 ipp_attribute_t
*tempcol
; /* Pointer to new buffer */
694 * Reallocate memory...
697 if ((tempcol
= realloc(lastcol
, sizeof(ipp_attribute_t
) +
698 (lastcol
->num_values
+ 1) *
699 sizeof(ipp_value_t
))) == NULL
)
701 print_fatal_error("Unable to allocate memory on line %d.", linenum
);
705 if (tempcol
!= lastcol
)
708 * Reset pointers in the list...
712 request
->prev
->next
= tempcol
;
714 request
->attrs
= tempcol
;
716 lastcol
= request
->current
= request
->last
= tempcol
;
719 lastcol
->values
[lastcol
->num_values
].collection
= col
;
720 lastcol
->num_values
++;
725 else if (!strcmp(token
, "DEFINE"))
731 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
732 get_token(fp
, temp
, sizeof(temp
), &linenum
))
734 expand_variables(vars
, token
, temp
, sizeof(token
));
735 set_variable(vars
, attr
, token
);
739 print_fatal_error("Missing DEFINE name and/or value on line %d.",
744 else if (!strcasecmp(token
, "NAME"))
750 get_token(fp
, name
, sizeof(name
), &linenum
);
752 else if (!strcmp(token
, "REQUEST-ID"))
759 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
761 if (isdigit(temp
[0] & 255))
762 request_id
= atoi(temp
);
763 else if (!strcasecmp(temp
, "random"))
764 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
767 print_fatal_error("Bad REQUEST-ID value \"%s\" on line %d.", temp
,
774 print_fatal_error("Missing REQUEST-ID value on line %d.", linenum
);
778 else if (!strcmp(token
, "TRANSFER"))
786 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
788 if (!strcmp(temp
, "auto"))
789 transfer
= _CUPS_TRANSFER_AUTO
;
790 else if (!strcmp(temp
, "chunked"))
791 transfer
= _CUPS_TRANSFER_CHUNKED
;
792 else if (!strcmp(temp
, "length"))
793 transfer
= _CUPS_TRANSFER_LENGTH
;
796 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp
,
803 print_fatal_error("Missing TRANSFER value on line %d.", linenum
);
807 else if (!strcasecmp(token
, "VERSION"))
809 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
811 if (!strcmp(temp
, "0.0"))
813 else if (!strcmp(temp
, "1.0"))
815 else if (!strcmp(temp
, "1.1"))
817 else if (!strcmp(temp
, "2.0"))
819 else if (!strcmp(temp
, "2.1"))
821 else if (!strcmp(temp
, "2.2"))
825 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp
, linenum
);
831 print_fatal_error("Missing VERSION number on line %d.", linenum
);
835 else if (!strcasecmp(token
, "RESOURCE"))
841 if (!get_token(fp
, resource
, sizeof(resource
), &linenum
))
843 print_fatal_error("Missing RESOURCE path on line %d.", linenum
);
847 else if (!strcasecmp(token
, "OPERATION"))
853 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
855 print_fatal_error("Missing OPERATION code on line %d.", linenum
);
859 if ((op
= ippOpValue(token
)) < 0)
861 print_fatal_error("Bad OPERATION code \"%s\" on line %d.", token
,
866 else if (!strcasecmp(token
, "GROUP"))
872 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
874 print_fatal_error("Missing GROUP tag on line %d.", linenum
);
878 if ((value
= ippTagValue(token
)) < 0)
880 print_fatal_error("Bad GROUP tag \"%s\" on line %d.", token
, linenum
);
885 ippAddSeparator(request
);
889 else if (!strcasecmp(token
, "DELAY"))
892 * Delay before operation...
897 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
899 print_fatal_error("Missing DELAY value on line %d.", linenum
);
903 if ((delay
= atoi(token
)) <= 0)
905 print_fatal_error("Bad DELAY value \"%s\" on line %d.", token
,
912 else if (!strcasecmp(token
, "ATTR"))
918 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
920 print_fatal_error("Missing ATTR value tag on line %d.", linenum
);
924 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
926 print_fatal_error("Bad ATTR value tag \"%s\" on line %d.", token
,
931 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
933 print_fatal_error("Missing ATTR name on line %d.", linenum
);
937 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
939 print_fatal_error("Missing ATTR value on line %d.", linenum
);
943 expand_variables(vars
, token
, temp
, sizeof(token
));
947 case IPP_TAG_BOOLEAN
:
948 if (!strcasecmp(token
, "true"))
949 ippAddBoolean(request
, group
, attr
, 1);
951 ippAddBoolean(request
, group
, attr
, atoi(token
));
954 case IPP_TAG_INTEGER
:
956 ippAddInteger(request
, group
, value
, attr
, atoi(token
));
959 case IPP_TAG_RESOLUTION
:
961 int xres
, /* X resolution */
962 yres
; /* Y resolution */
963 char units
[6]; /* Units */
965 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
966 (strcasecmp(units
, "dpi") && strcasecmp(units
, "dpc") &&
967 strcasecmp(units
, "other")))
969 print_fatal_error("Bad resolution value \"%s\" on line %d.",
974 if (!strcasecmp(units
, "dpi"))
975 ippAddResolution(request
, group
, attr
, xres
, yres
,
977 else if (!strcasecmp(units
, "dpc"))
978 ippAddResolution(request
, group
, attr
, xres
, yres
,
981 ippAddResolution(request
, group
, attr
, xres
, yres
,
988 int lowers
[4], /* Lower value */
989 uppers
[4], /* Upper values */
990 num_vals
; /* Number of values */
993 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
994 lowers
+ 0, uppers
+ 0,
995 lowers
+ 1, uppers
+ 1,
996 lowers
+ 2, uppers
+ 2,
997 lowers
+ 3, uppers
+ 3);
999 if ((num_vals
& 1) || num_vals
== 0)
1001 print_fatal_error("Bad rangeOfInteger value \"%s\" on line "
1002 "%d.", token
, linenum
);
1006 ippAddRanges(request
, group
, attr
, num_vals
/ 2, lowers
,
1011 case IPP_TAG_BEGIN_COLLECTION
:
1012 if (!strcmp(token
, "{"))
1014 ipp_t
*col
= get_collection(vars
, fp
, &linenum
);
1015 /* Collection value */
1018 lastcol
= ippAddCollection(request
, group
, attr
, col
);
1024 print_fatal_error("Bad ATTR collection value on line %d.",
1031 print_fatal_error("Unsupported ATTR value tag %s on line %d.",
1032 ippTagString(value
), linenum
);
1035 case IPP_TAG_TEXTLANG
:
1036 case IPP_TAG_NAMELANG
:
1039 case IPP_TAG_KEYWORD
:
1041 case IPP_TAG_URISCHEME
:
1042 case IPP_TAG_CHARSET
:
1043 case IPP_TAG_LANGUAGE
:
1044 case IPP_TAG_MIMETYPE
:
1045 if (!strchr(token
, ','))
1046 ippAddString(request
, group
, value
, attr
, NULL
, token
);
1050 * Multiple string values...
1053 int num_values
; /* Number of values */
1054 char *values
[100], /* Values */
1055 *ptr
; /* Pointer to next value */
1061 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
1064 values
[num_values
] = ptr
;
1068 ippAddStrings(request
, group
, value
, attr
, num_values
,
1069 NULL
, (const char **)values
);
1074 else if (!strcasecmp(token
, "FILE"))
1080 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1082 print_fatal_error("Missing FILE filename on line %d.", linenum
);
1086 expand_variables(vars
, token
, temp
, sizeof(token
));
1087 get_filename(testfile
, filename
, token
, sizeof(filename
));
1089 else if (!strcasecmp(token
, "STATUS"))
1095 if (num_statuses
>= (int)(sizeof(statuses
) / sizeof(statuses
[0])))
1097 print_fatal_error("Too many STATUS's on line %d.", linenum
);
1101 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1103 print_fatal_error("Missing STATUS code on line %d.", linenum
);
1107 if ((statuses
[num_statuses
].status
= ippErrorValue(token
)) < 0)
1109 print_fatal_error("Bad STATUS code \"%s\" on line %d.", token
,
1114 last_status
= statuses
+ num_statuses
;
1117 last_status
->if_defined
= NULL
;
1118 last_status
->if_undefined
= NULL
;
1120 else if (!strcasecmp(token
, "EXPECT"))
1123 * Expected attributes...
1126 if (num_expects
>= (int)(sizeof(expects
) / sizeof(expects
[0])))
1128 print_fatal_error("Too many EXPECT's on line %d.", linenum
);
1132 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1134 print_fatal_error("Missing EXPECT name on line %d.", linenum
);
1138 last_expect
= expects
+ num_expects
;
1141 memset(last_expect
, 0, sizeof(_cups_expect_t
));
1143 if (token
[0] == '!')
1145 last_expect
->not_expect
= 1;
1146 last_expect
->name
= strdup(token
+ 1);
1148 else if (token
[0] == '?')
1150 last_expect
->optional
= 1;
1151 last_expect
->name
= strdup(token
+ 1);
1154 last_expect
->name
= strdup(token
);
1156 else if (!strcasecmp(token
, "COUNT"))
1158 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1160 print_fatal_error("Missing COUNT number on line %d.", linenum
);
1164 if ((i
= atoi(token
)) <= 0)
1166 print_fatal_error("Bad COUNT \"%s\" on line %d.", token
, linenum
);
1171 last_expect
->count
= i
;
1174 print_fatal_error("COUNT without a preceding EXPECT on line %d.",
1179 else if (!strcasecmp(token
, "OF-TYPE"))
1181 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1183 print_fatal_error("Missing OF-TYPE value tag(s) on line %d.",
1189 last_expect
->of_type
= strdup(token
);
1192 print_fatal_error("OF-TYPE without a preceding EXPECT on line %d.",
1197 else if (!strcasecmp(token
, "IN-GROUP"))
1199 ipp_tag_t in_group
; /* IN-GROUP value */
1202 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1204 print_fatal_error("Missing IN-GROUP group tag on line %d.", linenum
);
1208 if ((in_group
= ippTagValue(token
)) == (ipp_tag_t
)-1)
1211 else if (last_expect
)
1212 last_expect
->in_group
= in_group
;
1215 print_fatal_error("IN-GROUP without a preceding EXPECT on line %d.",
1220 else if (!strcasecmp(token
, "SAME-COUNT-AS"))
1222 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1224 print_fatal_error("Missing SAME-COUNT-AS name on line %d.", linenum
);
1229 last_expect
->same_count_as
= strdup(token
);
1232 print_fatal_error("SAME-COUNT-AS without a preceding EXPECT on line "
1237 else if (!strcasecmp(token
, "IF-DEFINED"))
1239 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1241 print_fatal_error("Missing IF-DEFINED name on line %d.", linenum
);
1246 last_expect
->if_defined
= strdup(token
);
1247 else if (last_status
)
1248 last_status
->if_defined
= strdup(token
);
1251 print_fatal_error("IF-DEFINED without a preceding EXPECT or STATUS "
1252 "on line %d.", linenum
);
1256 else if (!strcasecmp(token
, "IF-UNDEFINED"))
1258 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1260 print_fatal_error("Missing IF-UNDEFINED name on line %d.", linenum
);
1265 last_expect
->if_undefined
= strdup(token
);
1266 else if (last_status
)
1267 last_status
->if_undefined
= strdup(token
);
1270 print_fatal_error("IF-UNDEFINED without a preceding EXPECT or STATUS "
1271 "on line %d.", linenum
);
1275 else if (!strcasecmp(token
, "WITH-VALUE"))
1277 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1279 print_fatal_error("Missing WITH-VALUE value on line %d.", linenum
);
1285 tokenptr
= token
+ strlen(token
) - 1;
1286 if (token
[0] == '/' && tokenptr
> token
&& *tokenptr
== '/')
1289 * WITH-VALUE is a POSIX extended regular expression.
1292 last_expect
->with_value
= calloc(1, tokenptr
- token
);
1293 last_expect
->with_regex
= 1;
1295 if (last_expect
->with_value
)
1296 memcpy(last_expect
->with_value
, token
+ 1, tokenptr
- token
- 1);
1301 * WITH-VALUE is a literal value...
1304 last_expect
->with_value
= strdup(token
);
1309 print_fatal_error("WITH-VALUE without a preceding EXPECT on line %d.",
1314 else if (!strcasecmp(token
, "DISPLAY"))
1317 * Display attributes...
1320 if (num_displayed
>= (int)(sizeof(displayed
) / sizeof(displayed
[0])))
1322 print_fatal_error("Too many DISPLAY's on line %d", linenum
);
1326 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1328 print_fatal_error("Missing DISPLAY name on line %d.", linenum
);
1332 displayed
[num_displayed
] = strdup(token
);
1337 print_fatal_error("Unexpected token %s seen on line %d.", token
,
1344 * Submit the IPP request...
1347 request
->request
.op
.version
[0] = version
/ 10;
1348 request
->request
.op
.version
[1] = version
% 10;
1349 request
->request
.op
.operation_id
= op
;
1350 request
->request
.op
.request_id
= request_id
;
1355 puts("<key>Name</key>");
1356 print_xml_string("string", name
);
1357 puts("<key>Operation</key>");
1358 print_xml_string("string", ippOpString(op
));
1359 puts("<key>RequestAttributes</key>");
1361 for (attrptr
= request
->attrs
; attrptr
; attrptr
= attrptr
->next
)
1362 print_attr(attrptr
);
1369 printf(" %s:\n", ippOpString(op
));
1371 for (attrptr
= request
->attrs
; attrptr
; attrptr
= attrptr
->next
)
1372 print_attr(attrptr
);
1375 printf(" %-69.69s [", name
);
1379 if (transfer
== _CUPS_TRANSFER_CHUNKED
||
1380 (transfer
== _CUPS_TRANSFER_AUTO
&& filename
[0]))
1383 * Send request using chunking...
1386 http_status_t status
= cupsSendRequest(http
, request
, resource
, 0);
1388 if (status
== HTTP_CONTINUE
&& filename
[0])
1390 int fd
; /* File to send */
1391 char buffer
[8192]; /* Copy buffer */
1392 ssize_t bytes
; /* Bytes read/written */
1394 if ((fd
= open(filename
, O_RDONLY
| O_BINARY
)) >= 0)
1396 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
1397 if ((status
= cupsWriteRequestData(http
, buffer
,
1398 bytes
)) != HTTP_CONTINUE
)
1403 snprintf(buffer
, sizeof(buffer
), "%s: %s", filename
, strerror(errno
));
1404 _cupsSetError(IPP_INTERNAL_ERROR
, buffer
, 0);
1406 status
= HTTP_ERROR
;
1412 if (status
== HTTP_CONTINUE
)
1413 response
= cupsGetResponse(http
, resource
);
1417 else if (filename
[0])
1418 response
= cupsDoFileRequest(http
, request
, resource
, filename
);
1420 response
= cupsDoRequest(http
, request
, resource
);
1428 if (http
->version
!= HTTP_1_1
)
1431 if (response
->request
.status
.version
[0] != (version
/ 10) ||
1432 response
->request
.status
.version
[1] != (version
% 10) ||
1433 response
->request
.status
.request_id
!= request_id
)
1436 if ((attrptr
= ippFindAttribute(response
, "job-id",
1437 IPP_TAG_INTEGER
)) != NULL
)
1439 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
1440 set_variable(vars
, "job-id", temp
);
1443 if ((attrptr
= ippFindAttribute(response
, "job-uri",
1444 IPP_TAG_URI
)) != NULL
)
1445 set_variable(vars
, "job-uri", attrptr
->values
[0].string
.text
);
1447 if ((attrptr
= ippFindAttribute(response
, "notify-subscription-id",
1448 IPP_TAG_INTEGER
)) != NULL
)
1450 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
1451 set_variable(vars
, "notify-subscription-id", temp
);
1454 attrptr
= response
->attrs
;
1455 if (!attrptr
|| !attrptr
->name
||
1456 attrptr
->value_tag
!= IPP_TAG_CHARSET
||
1457 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
1458 attrptr
->num_values
!= 1 ||
1459 strcmp(attrptr
->name
, "attributes-charset"))
1464 attrptr
= attrptr
->next
;
1465 if (!attrptr
|| !attrptr
->name
||
1466 attrptr
->value_tag
!= IPP_TAG_LANGUAGE
||
1467 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
1468 attrptr
->num_values
!= 1 ||
1469 strcmp(attrptr
->name
, "attributes-natural-language"))
1473 if ((attrptr
= ippFindAttribute(response
, "status-message",
1474 IPP_TAG_ZERO
)) != NULL
&&
1475 (attrptr
->value_tag
!= IPP_TAG_TEXT
||
1476 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
1477 attrptr
->num_values
!= 1 ||
1478 (attrptr
->value_tag
== IPP_TAG_TEXT
&&
1479 strlen(attrptr
->values
[0].string
.text
) > 255)))
1482 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
1483 IPP_TAG_ZERO
)) != NULL
&&
1484 (attrptr
->value_tag
!= IPP_TAG_TEXT
||
1485 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
1486 attrptr
->num_values
!= 1 ||
1487 (attrptr
->value_tag
== IPP_TAG_TEXT
&&
1488 strlen(attrptr
->values
[0].string
.text
) > 1023)))
1491 for (attrptr
= response
->attrs
, group
= attrptr
->group_tag
;
1493 attrptr
= attrptr
->next
)
1495 if (attrptr
->group_tag
< group
&& attrptr
->group_tag
!= IPP_TAG_ZERO
)
1501 if (!validate_attr(attrptr
, 0))
1508 for (i
= 0; i
< num_statuses
; i
++)
1510 if (statuses
[i
].if_defined
&&
1511 !get_variable(vars
, statuses
[i
].if_defined
))
1514 if (statuses
[i
].if_undefined
&&
1515 get_variable(vars
, statuses
[i
].if_undefined
))
1518 if (response
->request
.status
.status_code
== statuses
[i
].status
)
1522 if (i
== num_statuses
&& num_statuses
> 0)
1526 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
1528 if (expect
->if_defined
&& !get_variable(vars
, expect
->if_defined
))
1531 if (expect
->if_undefined
&& get_variable(vars
, expect
->if_undefined
))
1534 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
1536 if ((found
&& expect
->not_expect
) ||
1537 (!found
&& !(expect
->not_expect
|| expect
->optional
)) ||
1538 (found
&& !expect_matches(expect
, found
->value_tag
)) ||
1539 (found
&& expect
->in_group
&&
1540 found
->group_tag
!= expect
->in_group
))
1547 !with_value(expect
->with_value
, expect
->with_regex
, found
))
1553 if (found
&& expect
->count
> 0 && found
->num_values
!= expect
->count
)
1559 if (found
&& expect
->same_count_as
)
1561 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
1564 if (!attrptr
|| attrptr
->num_values
!= found
->num_values
)
1576 puts("<key>Successful</key>");
1577 puts(pass
? "<true />" : "<false />");
1578 puts("<key>StatusCode</key>");
1579 print_xml_string("string", ippErrorString(cupsLastError()));
1580 puts("<key>ResponseAttributes</key>");
1582 for (attrptr
= response
? response
->attrs
: NULL
;
1584 attrptr
= attrptr
->next
)
1585 print_attr(attrptr
);
1590 puts(pass
? "PASS]" : "FAIL]");
1592 if (Verbosity
&& response
)
1594 printf(" RECEIVED: %lu bytes in response\n",
1595 (unsigned long)ippLength(response
));
1596 printf(" status-code = %x (%s)\n", cupsLastError(),
1597 ippErrorString(cupsLastError()));
1599 for (attrptr
= response
->attrs
;
1601 attrptr
= attrptr
->next
)
1603 print_attr(attrptr
);
1608 if (pass
&& !XML
&& !Verbosity
&& num_displayed
> 0)
1610 for (attrptr
= response
->attrs
;
1612 attrptr
= attrptr
->next
)
1614 for (i
= 0; i
< num_displayed
; i
++)
1615 if (!strcmp(displayed
[i
], attrptr
->name
))
1617 print_attr(attrptr
);
1625 puts("<key>Errors</key>");
1629 if (http
->version
!= HTTP_1_1
)
1630 print_test_error("Bad HTTP version (%d.%d)", http
->version
/ 100,
1631 http
->version
% 100);
1634 print_test_error("IPP request failed with status %s (%s)",
1635 ippErrorString(cupsLastError()),
1636 cupsLastErrorString());
1639 if (response
->request
.status
.version
[0] != (version
/ 10) ||
1640 response
->request
.status
.version
[1] != (version
% 10))
1641 print_test_error("Bad version %d.%d in response - expected %d.%d "
1642 "(RFC 2911 section 3.1.8).",
1643 response
->request
.status
.version
[0],
1644 response
->request
.status
.version
[1],
1645 version
/ 10, version
% 10);
1647 if (response
->request
.status
.request_id
!= request_id
)
1648 print_test_error("Bad request ID %d in response - expected %d "
1649 "(RFC 2911 section 3.1.1)",
1650 response
->request
.status
.request_id
, request_id
);
1652 attrptr
= response
->attrs
;
1654 print_test_error("Missing first attribute \"attributes-charset "
1655 "(charset)\" in group operation-attributes-tag "
1656 "(RFC 2911 section 3.1.4).");
1659 if (!attrptr
->name
||
1660 attrptr
->value_tag
!= IPP_TAG_CHARSET
||
1661 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
1662 attrptr
->num_values
!= 1 ||
1663 strcmp(attrptr
->name
, "attributes-charset"))
1664 print_test_error("Bad first attribute \"%s (%s%s)\" in group %s, "
1665 "expected \"attributes-charset (charset)\" in "
1666 "group operation-attributes-tag (RFC 2911 section "
1668 attrptr
->name
? attrptr
->name
: "(null)",
1669 attrptr
->num_values
> 1 ? "1setOf " : "",
1670 ippTagString(attrptr
->value_tag
),
1671 ippTagString(attrptr
->group_tag
));
1673 attrptr
= attrptr
->next
;
1675 print_test_error("Missing second attribute \"attributes-natural-"
1676 "language (naturalLanguage)\" in group "
1677 "operation-attributes-tag (RFC 2911 section "
1679 else if (!attrptr
->name
||
1680 attrptr
->value_tag
!= IPP_TAG_LANGUAGE
||
1681 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
1682 attrptr
->num_values
!= 1 ||
1683 strcmp(attrptr
->name
, "attributes-natural-language"))
1684 print_test_error("Bad first attribute \"%s (%s%s)\" in group %s, "
1685 "expected \"attributes-natural-language "
1686 "(naturalLanguage)\" in group "
1687 "operation-attributes-tag (RFC 2911 section "
1689 attrptr
->name
? attrptr
->name
: "(null)",
1690 attrptr
->num_values
> 1 ? "1setOf " : "",
1691 ippTagString(attrptr
->value_tag
),
1692 ippTagString(attrptr
->group_tag
));
1695 if ((attrptr
= ippFindAttribute(response
, "status-message",
1696 IPP_TAG_ZERO
)) != NULL
)
1698 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
1699 print_test_error("status-message (text(255)) has wrong value tag "
1700 "%s (RFC 2911 section 3.1.6.2).",
1701 ippTagString(attrptr
->value_tag
));
1702 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
1703 print_test_error("status-message (text(255)) has wrong group tag "
1704 "%s (RFC 2911 section 3.1.6.2).",
1705 ippTagString(attrptr
->group_tag
));
1706 if (attrptr
->num_values
!= 1)
1707 print_test_error("status-message (text(255)) has %d values "
1708 "(RFC 2911 section 3.1.6.2).",
1709 attrptr
->num_values
);
1710 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
1711 strlen(attrptr
->values
[0].string
.text
) > 255)
1712 print_test_error("status-message (text(255)) has bad length %d"
1713 " (RFC 2911 section 3.1.6.2).",
1714 (int)strlen(attrptr
->values
[0].string
.text
));
1717 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
1718 IPP_TAG_ZERO
)) != NULL
)
1720 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
1721 print_test_error("detailed-status-message (text(MAX)) has wrong "
1722 "value tag %s (RFC 2911 section 3.1.6.3).",
1723 ippTagString(attrptr
->value_tag
));
1724 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
1725 print_test_error("detailed-status-message (text(MAX)) has wrong "
1726 "group tag %s (RFC 2911 section 3.1.6.3).",
1727 ippTagString(attrptr
->group_tag
));
1728 if (attrptr
->num_values
!= 1)
1729 print_test_error("detailed-status-message (text(MAX)) has %d values"
1730 " (RFC 2911 section 3.1.6.3).",
1731 attrptr
->num_values
);
1732 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
1733 strlen(attrptr
->values
[0].string
.text
) > 1023)
1734 print_test_error("detailed-status-message (text(MAX)) has bad "
1735 "length %d (RFC 2911 section 3.1.6.3).",
1736 (int)strlen(attrptr
->values
[0].string
.text
));
1739 for (attrptr
= response
->attrs
, group
= attrptr
->group_tag
;
1741 attrptr
= attrptr
->next
)
1743 if (attrptr
->group_tag
< group
&& attrptr
->group_tag
!= IPP_TAG_ZERO
)
1744 print_test_error("Attribute groups out of order (%s < %s)",
1745 ippTagString(attrptr
->group_tag
),
1746 ippTagString(group
));
1748 validate_attr(attrptr
, 1);
1751 for (i
= 0; i
< num_statuses
; i
++)
1753 if (statuses
[i
].if_defined
&&
1754 !get_variable(vars
, statuses
[i
].if_defined
))
1757 if (statuses
[i
].if_undefined
&&
1758 get_variable(vars
, statuses
[i
].if_undefined
))
1761 if (response
->request
.status
.status_code
== statuses
[i
].status
)
1765 if (i
== num_statuses
&& num_statuses
> 0)
1767 print_test_error("Bad status-code (%s)",
1768 ippErrorString(cupsLastError()));
1769 print_test_error("status-message=\"%s\"", cupsLastErrorString());
1772 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
1774 if (expect
->if_defined
&& !get_variable(vars
, expect
->if_defined
))
1777 if (expect
->if_undefined
&& get_variable(vars
, expect
->if_undefined
))
1780 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
1782 if (found
&& expect
->not_expect
)
1783 print_test_error("NOT EXPECTED: %s", expect
->name
);
1784 else if (!found
&& !(expect
->not_expect
|| expect
->optional
))
1785 print_test_error("EXPECTED: %s", expect
->name
);
1788 if (!expect_matches(expect
, found
->value_tag
))
1789 print_test_error("EXPECTED: %s OF-TYPE %s (got %s)",
1790 expect
->name
, expect
->of_type
,
1791 ippTagString(found
->value_tag
));
1793 if (expect
->in_group
&& found
->group_tag
!= expect
->in_group
)
1794 print_test_error("EXPECTED: %s IN-GROUP %s (got %s).",
1795 expect
->name
, ippTagString(expect
->in_group
),
1796 ippTagString(found
->group_tag
));
1798 if (!with_value(expect
->with_value
, expect
->with_regex
, found
))
1800 if (expect
->with_regex
)
1801 print_test_error("EXPECTED: %s WITH-VALUE /%s/",
1802 expect
->name
, expect
->with_value
);
1804 print_test_error("EXPECTED: %s WITH-VALUE \"%s\"",
1805 expect
->name
, expect
->with_value
);
1808 if (expect
->count
> 0 && found
->num_values
!= expect
->count
)
1810 print_test_error("EXPECTED: %s COUNT %d (got %d)", expect
->name
,
1811 expect
->count
, found
->num_values
);
1814 if (expect
->same_count_as
)
1816 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
1820 print_test_error("EXPECTED: %s (%d values) SAME-COUNT-AS %s "
1821 "(not returned)", expect
->name
,
1822 found
->num_values
, expect
->same_count_as
);
1823 else if (attrptr
->num_values
!= found
->num_values
)
1824 print_test_error("EXPECTED: %s (%d values) SAME-COUNT-AS %s "
1825 "(%d values)", expect
->name
, found
->num_values
,
1826 expect
->same_count_as
, attrptr
->num_values
);
1839 ippDelete(response
);
1842 for (i
= 0; i
< num_statuses
; i
++)
1844 if (statuses
[i
].if_defined
)
1845 free(statuses
[i
].if_defined
);
1846 if (statuses
[i
].if_undefined
)
1847 free(statuses
[i
].if_undefined
);
1851 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
1854 if (expect
->of_type
)
1855 free(expect
->of_type
);
1856 if (expect
->same_count_as
)
1857 free(expect
->same_count_as
);
1858 if (expect
->if_defined
)
1859 free(expect
->if_defined
);
1860 if (expect
->if_undefined
)
1861 free(expect
->if_undefined
);
1862 if (expect
->with_value
)
1863 free(expect
->with_value
);
1867 for (i
= 0; i
< num_displayed
; i
++)
1881 * If we get here there was a fatal test error...
1891 ippDelete(response
);
1893 for (i
= 0; i
< num_statuses
; i
++)
1895 if (statuses
[i
].if_defined
)
1896 free(statuses
[i
].if_defined
);
1897 if (statuses
[i
].if_undefined
)
1898 free(statuses
[i
].if_undefined
);
1901 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
1904 if (expect
->of_type
)
1905 free(expect
->of_type
);
1906 if (expect
->same_count_as
)
1907 free(expect
->same_count_as
);
1908 if (expect
->if_defined
)
1909 free(expect
->if_defined
);
1910 if (expect
->if_undefined
)
1911 free(expect
->if_undefined
);
1912 if (expect
->with_value
)
1913 free(expect
->with_value
);
1916 for (i
= 0; i
< num_displayed
; i
++)
1924 * 'expand_variables()' - Expand variables in a string.
1928 expand_variables(_cups_vars_t
*vars
, /* I - Variables */
1929 char *dst
, /* I - Destination string buffer */
1930 const char *src
, /* I - Source string */
1931 size_t dstsize
) /* I - Size of destination buffer */
1933 char *dstptr
, /* Pointer into destination */
1934 *dstend
, /* End of destination */
1935 temp
[256], /* Temporary string */
1936 *tempptr
; /* Pointer into temporary string */
1937 const char *value
; /* Value to substitute */
1941 dstend
= dst
+ dstsize
- 1;
1943 while (*src
&& dstptr
< dstend
)
1948 * Substitute a string/number...
1951 if (!strncmp(src
, "$$", 2))
1956 else if (!strncmp(src
, "$ENV[", 5))
1958 strlcpy(temp
, src
+ 5, sizeof(temp
));
1960 for (tempptr
= temp
; *tempptr
; tempptr
++)
1961 if (*tempptr
== ']')
1967 value
= getenv(temp
);
1968 src
+= tempptr
- temp
+ 5;
1972 strlcpy(temp
, src
+ 1, sizeof(temp
));
1974 for (tempptr
= temp
; *tempptr
; tempptr
++)
1975 if (!isalnum(*tempptr
& 255) && *tempptr
!= '-' && *tempptr
!= '_')
1981 if (!strcmp(temp
, "uri"))
1983 else if (!strcmp(temp
, "filename"))
1984 value
= vars
->filename
;
1985 else if (!strcmp(temp
, "scheme") || !strcmp(temp
, "method"))
1986 value
= vars
->scheme
;
1987 else if (!strcmp(temp
, "username"))
1988 value
= vars
->userpass
;
1989 else if (!strcmp(temp
, "hostname"))
1990 value
= vars
->hostname
;
1991 else if (!strcmp(temp
, "port"))
1993 snprintf(temp
, sizeof(temp
), "%d", vars
->port
);
1996 else if (!strcmp(temp
, "resource"))
1997 value
= vars
->resource
;
1998 else if (!strcmp(temp
, "user"))
2001 value
= get_variable(vars
, temp
);
2003 src
+= tempptr
- temp
+ 1;
2013 strlcpy(dstptr
, value
, dstend
- dstptr
+ 1);
2014 dstptr
+= strlen(dstptr
);
2026 * 'expect_matches()' - Return true if the tag matches the specification.
2029 static int /* O - 1 if matches, 0 otherwise */
2031 _cups_expect_t
*expect
, /* I - Expected attribute */
2032 ipp_tag_t value_tag
) /* I - Value tag for attribute */
2034 int match
; /* Match? */
2035 char *of_type
, /* Type name to match */
2036 *next
, /* Next name to match */
2037 sep
; /* Separator character */
2041 * If we don't expect a particular type, return immediately...
2044 if (!expect
->of_type
)
2048 * Parse the "of_type" value since the string can contain multiple attribute
2049 * types separated by "," or "|"...
2052 for (of_type
= expect
->of_type
, match
= 0; !match
&& *of_type
; of_type
= next
)
2055 * Find the next separator, and set it (temporarily) to nul if present.
2058 for (next
= of_type
; *next
&& *next
!= '|' && *next
!= ','; next
++);
2060 if ((sep
= *next
) != '\0')
2064 * Support some meta-types to make it easier to write the test file.
2067 if (!strcmp(of_type
, "text"))
2068 match
= value_tag
== IPP_TAG_TEXTLANG
|| value_tag
== IPP_TAG_TEXT
;
2069 else if (!strcmp(of_type
, "name"))
2070 match
= value_tag
== IPP_TAG_NAMELANG
|| value_tag
== IPP_TAG_NAME
;
2071 else if (!strcmp(of_type
, "collection"))
2072 match
= value_tag
== IPP_TAG_BEGIN_COLLECTION
;
2074 match
= value_tag
== ippTagValue(of_type
);
2077 * Restore the separator if we have one...
2089 * 'get_collection()' - Get a collection value from the current test file.
2092 static ipp_t
* /* O - Collection value */
2093 get_collection(_cups_vars_t
*vars
, /* I - Variables */
2094 FILE *fp
, /* I - File to read from */
2095 int *linenum
) /* IO - Line number */
2097 char token
[1024], /* Token from file */
2098 temp
[1024], /* Temporary string */
2099 attr
[128]; /* Attribute name */
2100 ipp_tag_t value
; /* Current value type */
2101 ipp_t
*col
= ippNew(); /* Collection value */
2102 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
2105 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
2107 if (!strcmp(token
, "}"))
2109 else if (!strcmp(token
, "{") && lastcol
)
2112 * Another collection value
2115 ipp_t
*subcol
= get_collection(vars
, fp
, linenum
);
2116 /* Collection value */
2120 ipp_attribute_t
*tempcol
; /* Pointer to new buffer */
2124 * Reallocate memory...
2127 if ((tempcol
= realloc(lastcol
, sizeof(ipp_attribute_t
) +
2128 (lastcol
->num_values
+ 1) *
2129 sizeof(ipp_value_t
))) == NULL
)
2131 print_fatal_error("Unable to allocate memory on line %d.", *linenum
);
2135 if (tempcol
!= lastcol
)
2138 * Reset pointers in the list...
2142 col
->prev
->next
= tempcol
;
2144 col
->attrs
= tempcol
;
2146 lastcol
= col
->current
= col
->last
= tempcol
;
2149 lastcol
->values
[lastcol
->num_values
].collection
= subcol
;
2150 lastcol
->num_values
++;
2155 else if (!strcasecmp(token
, "MEMBER"))
2163 if (!get_token(fp
, token
, sizeof(token
), linenum
))
2165 print_fatal_error("Missing MEMBER value tag on line %d.", *linenum
);
2169 if ((value
= ippTagValue(token
)) < 0)
2171 print_fatal_error("Bad MEMBER value tag \"%s\" on line %d.", token
,
2176 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
2178 print_fatal_error("Missing MEMBER name on line %d.", *linenum
);
2182 if (!get_token(fp
, temp
, sizeof(temp
), linenum
))
2184 print_fatal_error("Missing MEMBER value on line %d.", *linenum
);
2188 expand_variables(vars
, token
, temp
, sizeof(token
));
2192 case IPP_TAG_BOOLEAN
:
2193 if (!strcasecmp(token
, "true"))
2194 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
2196 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, atoi(token
));
2199 case IPP_TAG_INTEGER
:
2201 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
2204 case IPP_TAG_RESOLUTION
:
2206 int xres
, /* X resolution */
2207 yres
; /* Y resolution */
2208 char units
[6]; /* Units */
2210 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
2211 (strcasecmp(units
, "dpi") && strcasecmp(units
, "dpc") &&
2212 strcasecmp(units
, "other")))
2214 print_fatal_error("Bad resolution value \"%s\" on line %d.",
2219 if (!strcasecmp(units
, "dpi"))
2220 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
2222 else if (!strcasecmp(units
, "dpc"))
2223 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
2226 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, xres
, yres
,
2231 case IPP_TAG_RANGE
:
2233 int lowers
[4], /* Lower value */
2234 uppers
[4], /* Upper values */
2235 num_vals
; /* Number of values */
2238 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
2239 lowers
+ 0, uppers
+ 0,
2240 lowers
+ 1, uppers
+ 1,
2241 lowers
+ 2, uppers
+ 2,
2242 lowers
+ 3, uppers
+ 3);
2244 if ((num_vals
& 1) || num_vals
== 0)
2246 print_fatal_error("Bad rangeOfInteger value \"%s\" on line %d.",
2251 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
2256 case IPP_TAG_BEGIN_COLLECTION
:
2257 if (!strcmp(token
, "{"))
2259 ipp_t
*subcol
= get_collection(vars
, fp
, linenum
);
2260 /* Collection value */
2263 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
2269 print_fatal_error("Bad collection value on line %d.", *linenum
);
2275 if (!strchr(token
, ','))
2276 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
2280 * Multiple string values...
2283 int num_values
; /* Number of values */
2284 char *values
[100], /* Values */
2285 *ptr
; /* Pointer to next value */
2291 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
2294 values
[num_values
] = ptr
;
2298 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
2299 NULL
, (const char **)values
);
2309 * If we get here there was a parse error; free memory and return.
2321 * 'get_filename()' - Get a filename based on the current test file.
2324 static char * /* O - Filename */
2325 get_filename(const char *testfile
, /* I - Current test file */
2326 char *dst
, /* I - Destination filename */
2327 const char *src
, /* I - Source filename */
2328 size_t dstsize
) /* I - Size of destination buffer */
2330 char *dstptr
; /* Pointer into destination */
2331 _cups_globals_t
*cg
= _cupsGlobals();
2335 if (*src
== '<' && src
[strlen(src
) - 1] == '>')
2338 * Map <filename> to CUPS_DATADIR/ipptest/filename...
2341 snprintf(dst
, dstsize
, "%s/ipptest/%s", cg
->cups_datadir
, src
+ 1);
2342 dstptr
= dst
+ strlen(dst
) - 1;
2346 else if (*src
== '/' || !strchr(testfile
, '/'))
2349 * Use the path as-is...
2352 strlcpy(dst
, src
, dstsize
);
2357 * Make path relative to testfile...
2360 strlcpy(dst
, testfile
, dstsize
);
2361 if ((dstptr
= strrchr(dst
, '/')) != NULL
)
2364 dstptr
= dst
; /* Should never happen */
2366 strlcpy(dstptr
, src
, dstsize
- (dstptr
- dst
));
2374 * 'get_token()' - Get a token from a file.
2377 static char * /* O - Token from file or NULL on EOF */
2378 get_token(FILE *fp
, /* I - File to read from */
2379 char *buf
, /* I - Buffer to read into */
2380 int buflen
, /* I - Length of buffer */
2381 int *linenum
) /* IO - Current line number */
2383 int ch
, /* Character from file */
2384 quote
; /* Quoting character */
2385 char *bufptr
, /* Pointer into buffer */
2386 *bufend
; /* End of buffer */
2392 * Skip whitespace...
2395 while (isspace(ch
= getc(fp
)))
2407 else if (ch
== '\'' || ch
== '\"')
2410 * Quoted text or regular expression...
2415 bufend
= buf
+ buflen
- 1;
2417 while ((ch
= getc(fp
)) != EOF
)
2422 * Escape next character...
2425 if (bufptr
< bufend
)
2428 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
2431 else if (ch
== quote
)
2433 else if (bufptr
< bufend
)
2447 while ((ch
= getc(fp
)) != EOF
)
2456 * Whitespace delimited text...
2462 bufend
= buf
+ buflen
- 1;
2464 while ((ch
= getc(fp
)) != EOF
)
2465 if (isspace(ch
) || ch
== '#')
2467 else if (bufptr
< bufend
)
2472 else if (ch
== '\n')
2484 * 'get_variable()' - Get the value of a variable.
2487 static char * /* O - Value or NULL */
2488 get_variable(_cups_vars_t
*vars
, /* I - Variables */
2489 const char *name
) /* I - Variable name */
2491 _cups_var_t key
, /* Search key */
2492 *match
; /* Matching variable, if any */
2495 key
.name
= (char *)name
;
2496 match
= cupsArrayFind(vars
->vars
, &key
);
2498 return (match
? match
->value
: NULL
);
2503 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
2507 static char * /* O - ISO 8601 date/time string */
2508 iso_date(ipp_uchar_t
*date
) /* I - IPP (RFC 1903) date/time value */
2510 unsigned year
= (date
[0] << 8) + date
[1];
2512 static char buffer
[255]; /* String buffer */
2515 if (date
[9] == 0 && date
[10] == 0)
2516 snprintf(buffer
, sizeof(buffer
), "%04u-%02u-%02uT%02u:%02u:%02uZ",
2517 year
, date
[2], date
[3], date
[4], date
[5], date
[6]);
2519 snprintf(buffer
, sizeof(buffer
), "%04u-%02u-%02uT%02u:%02u:%02u%c%02u%02u",
2520 year
, date
[2], date
[3], date
[4], date
[5], date
[6],
2521 date
[8], date
[9], date
[10]);
2528 * 'print_attr()' - Print an attribute on the screen.
2532 print_attr(ipp_attribute_t
*attr
) /* I - Attribute to print */
2534 int i
; /* Looping var */
2535 ipp_attribute_t
*colattr
; /* Collection attribute */
2542 printf("<key>%s</key>\n<true />\n", ippTagString(attr
->group_tag
));
2546 print_xml_string("key", attr
->name
);
2547 if (attr
->num_values
> 1)
2554 puts(" -- separator --");
2558 printf(" %s (%s%s) = ", attr
->name
,
2559 attr
->num_values
> 1 ? "1setOf " : "",
2560 ippTagString(attr
->value_tag
));
2563 switch (attr
->value_tag
)
2565 case IPP_TAG_INTEGER
:
2567 for (i
= 0; i
< attr
->num_values
; i
++)
2569 printf("<integer>%d</integer>\n", attr
->values
[i
].integer
);
2571 printf("%d ", attr
->values
[i
].integer
);
2574 case IPP_TAG_BOOLEAN
:
2575 for (i
= 0; i
< attr
->num_values
; i
++)
2577 puts(attr
->values
[i
].boolean
? "<true />" : "<false />");
2578 else if (attr
->values
[i
].boolean
)
2579 fputs("true ", stdout
);
2581 fputs("false ", stdout
);
2584 case IPP_TAG_RANGE
:
2585 for (i
= 0; i
< attr
->num_values
; i
++)
2587 printf("<dict><key>lower</key><integer>%d</integer>"
2588 "<key>upper</key><integer>%d</integer></dict>\n",
2589 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
2591 printf("%d-%d ", attr
->values
[i
].range
.lower
,
2592 attr
->values
[i
].range
.upper
);
2595 case IPP_TAG_RESOLUTION
:
2596 for (i
= 0; i
< attr
->num_values
; i
++)
2598 printf("<dict><key>xres</key><integer>%d</integer>"
2599 "<key>yres</key><integer>%d</integer>"
2600 "<key>units</key><string>%s</string></dict>\n",
2601 attr
->values
[i
].resolution
.xres
,
2602 attr
->values
[i
].resolution
.yres
,
2603 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
2606 printf("%dx%d%s ", attr
->values
[i
].resolution
.xres
,
2607 attr
->values
[i
].resolution
.yres
,
2608 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
2613 for (i
= 0; i
< attr
->num_values
; i
++)
2615 printf("<date>%s</date>\n", iso_date(attr
->values
[i
].date
));
2617 printf("%s ", iso_date(attr
->values
[i
].date
));
2620 case IPP_TAG_STRING
:
2623 case IPP_TAG_KEYWORD
:
2624 case IPP_TAG_CHARSET
:
2626 case IPP_TAG_MIMETYPE
:
2627 case IPP_TAG_LANGUAGE
:
2628 for (i
= 0; i
< attr
->num_values
; i
++)
2630 print_xml_string("string", attr
->values
[i
].string
.text
);
2632 printf("\"%s\" ", attr
->values
[i
].string
.text
);
2635 case IPP_TAG_TEXTLANG
:
2636 case IPP_TAG_NAMELANG
:
2637 for (i
= 0; i
< attr
->num_values
; i
++)
2640 fputs("<dict><key>language</key><string>", stdout
);
2641 print_xml_string(NULL
, attr
->values
[i
].string
.charset
);
2642 fputs("</string><key>string</key><string>", stdout
);
2643 print_xml_string(NULL
, attr
->values
[i
].string
.text
);
2644 puts("</string></dict>");
2647 printf("\"%s\",%s ", attr
->values
[i
].string
.text
,
2648 attr
->values
[i
].string
.charset
);
2651 case IPP_TAG_BEGIN_COLLECTION
:
2652 for (i
= 0; i
< attr
->num_values
; i
++)
2657 for (colattr
= attr
->values
[i
].collection
->attrs
;
2659 colattr
= colattr
->next
)
2660 print_attr(colattr
);
2668 print_col(attr
->values
[i
].collection
);
2675 printf("<string><<%s>></string>\n",
2676 ippTagString(attr
->value_tag
));
2678 fputs(ippTagString(attr
->value_tag
), stdout
);
2684 if (attr
->num_values
> 1)
2693 * 'print_col()' - Print a collection attribute on the screen.
2697 print_col(ipp_t
*col
) /* I - Collection attribute to print */
2699 int i
; /* Looping var */
2700 ipp_attribute_t
*attr
; /* Current attribute in collection */
2703 fputs("{ ", stdout
);
2704 for (attr
= col
->attrs
; attr
; attr
= attr
->next
)
2706 printf("%s (%s%s) = ", attr
->name
, attr
->num_values
> 1 ? "1setOf " : "",
2707 ippTagString(attr
->value_tag
));
2709 switch (attr
->value_tag
)
2711 case IPP_TAG_INTEGER
:
2713 for (i
= 0; i
< attr
->num_values
; i
++)
2714 printf("%d ", attr
->values
[i
].integer
);
2717 case IPP_TAG_BOOLEAN
:
2718 for (i
= 0; i
< attr
->num_values
; i
++)
2719 if (attr
->values
[i
].boolean
)
2725 case IPP_TAG_NOVALUE
:
2729 case IPP_TAG_RANGE
:
2730 for (i
= 0; i
< attr
->num_values
; i
++)
2731 printf("%d-%d ", attr
->values
[i
].range
.lower
,
2732 attr
->values
[i
].range
.upper
);
2735 case IPP_TAG_RESOLUTION
:
2736 for (i
= 0; i
< attr
->num_values
; i
++)
2737 printf("%dx%d%s ", attr
->values
[i
].resolution
.xres
,
2738 attr
->values
[i
].resolution
.yres
,
2739 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
2743 case IPP_TAG_STRING
:
2746 case IPP_TAG_KEYWORD
:
2747 case IPP_TAG_CHARSET
:
2749 case IPP_TAG_MIMETYPE
:
2750 case IPP_TAG_LANGUAGE
:
2751 for (i
= 0; i
< attr
->num_values
; i
++)
2752 printf("\"%s\" ", attr
->values
[i
].string
.text
);
2755 case IPP_TAG_TEXTLANG
:
2756 case IPP_TAG_NAMELANG
:
2757 for (i
= 0; i
< attr
->num_values
; i
++)
2758 printf("\"%s\",%s ", attr
->values
[i
].string
.text
,
2759 attr
->values
[i
].string
.charset
);
2762 case IPP_TAG_BEGIN_COLLECTION
:
2763 for (i
= 0; i
< attr
->num_values
; i
++)
2765 print_col(attr
->values
[i
].collection
);
2771 break; /* anti-compiler-warning-code */
2780 * 'print_fatal_error()' - Print a fatal error message.
2784 print_fatal_error(const char *s
, /* I - Printf-style format string */
2785 ...) /* I - Additional arguments as needed */
2787 char buffer
[10240]; /* Format buffer */
2788 va_list ap
; /* Pointer to arguments */
2792 * Format the error message...
2796 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
2806 print_xml_trailer(0, buffer
);
2809 _cupsLangPrintf(stderr
, "ipptest: %s\n", buffer
);
2814 * 'print_test_error()' - Print a test error message.
2818 print_test_error(const char *s
, /* I - Printf-style format string */
2819 ...) /* I - Additional arguments as needed */
2821 char buffer
[10240]; /* Format buffer */
2822 va_list ap
; /* Pointer to arguments */
2826 * Format the error message...
2830 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
2838 print_xml_string("string", buffer
);
2840 printf(" %s\n", buffer
);
2845 * 'print_xml_header()' - Print a standard XML plist header.
2849 print_xml_header(void)
2853 puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
2854 puts("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
2855 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
2856 puts("<plist version=\"1.0\">");
2858 puts("<key>Transfer</key>");
2859 printf("<string>%s</string>\n",
2860 Transfer
== _CUPS_TRANSFER_AUTO
? "auto" :
2861 Transfer
== _CUPS_TRANSFER_CHUNKED
? "chunked" : "length");
2862 puts("<key>Tests</key>");
2871 * 'print_xml_string()' - Print an XML string with escaping.
2875 print_xml_string(const char *element
, /* I - Element name or NULL */
2876 const char *s
) /* I - String to print */
2879 printf("<%s>", element
);
2884 fputs("&", stdout
);
2886 fputs("<", stdout
);
2888 fputs(">", stdout
);
2896 printf("</%s>\n", element
);
2901 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
2905 print_xml_trailer(int success
, /* I - 1 on success, 0 on failure */
2906 const char *message
) /* I - Error message or NULL */
2911 puts("<key>Successful</key>");
2912 puts(success
? "<true />" : "<false />");
2915 puts("<key>ErrorMessage</key>");
2916 print_xml_string("string", message
);
2927 * 'set_variable()' - Set a variable value.
2931 set_variable(_cups_vars_t
*vars
, /* I - Variables */
2932 const char *name
, /* I - Variable name */
2933 const char *value
) /* I - Value string */
2935 _cups_var_t key
, /* Search key */
2936 *var
; /* New variable */
2939 key
.name
= (char *)name
;
2940 if ((var
= cupsArrayFind(vars
->vars
, &key
)) != NULL
)
2943 var
->value
= strdup(value
);
2945 else if ((var
= malloc(sizeof(_cups_var_t
))) == NULL
)
2947 print_fatal_error("Unable to allocate memory for variable \"%s\".", name
);
2952 var
->name
= strdup(name
);
2953 var
->value
= strdup(value
);
2955 cupsArrayAdd(vars
->vars
, var
);
2961 * 'usage()' - Show program usage.
2967 _cupsLangPuts(stderr
,
2968 _("Usage: ipptest [options] URI filename.test [ ... "
2969 "filenameN.test ]\n"
2973 "-E Test with encryption.\n"
2974 "-V version Set default IPP version.\n"
2975 "-X Produce XML instead of plain text.\n"
2976 "-c Send requests using chunking (default)\n"
2977 "-d name=value Define variable.\n"
2978 "-f filename Set default test file.\n"
2979 "-i seconds Repeat the last test file with the given "
2981 "-l Send requests using content-length\n"
2982 "-v Show all attributes sent and received.\n"));
2989 * 'validate_attr()' - Determine whether an attribute is valid.
2992 static int /* O - 1 if valid, 0 otherwise */
2993 validate_attr(ipp_attribute_t
*attr
, /* I - Attribute to validate */
2994 int print
) /* I - 1 = report issues to stdout */
2996 int i
; /* Looping var */
2997 char scheme
[64], /* Scheme from URI */
2998 userpass
[256], /* Username/password from URI */
2999 hostname
[256], /* Hostname from URI */
3000 resource
[1024]; /* Resource from URI */
3001 int port
, /* Port number from URI */
3002 uri_status
, /* URI separation status */
3003 valid
= 1; /* Is the attribute valid? */
3004 const char *ptr
; /* Pointer into string */
3005 ipp_attribute_t
*colattr
; /* Collection attribute */
3006 regex_t re
; /* Regular expression */
3007 ipp_uchar_t
*date
; /* Current date value */
3018 * Validate the attribute name.
3021 for (ptr
= attr
->name
; *ptr
; ptr
++)
3022 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' && *ptr
!= '_')
3025 if (*ptr
|| ptr
== attr
->name
)
3030 print_test_error("\"%s\": Bad attribute name - invalid character (RFC "
3031 "2911 section 4.1.3).", attr
->name
);
3034 if ((ptr
- attr
->name
) > 255)
3039 print_test_error("\"%s\": Bad attribute name - bad length (RFC 2911 "
3040 "section 4.1.3).", attr
->name
);
3043 switch (attr
->value_tag
)
3045 case IPP_TAG_INTEGER
:
3048 case IPP_TAG_BOOLEAN
:
3049 for (i
= 0; i
< attr
->num_values
; i
++)
3051 if (attr
->values
[i
].boolean
!= 0 &&
3052 attr
->values
[i
].boolean
!= 1)
3057 print_test_error("\"%s\": Bad boolen value %d (RFC 2911 section "
3058 "4.1.10).", attr
->name
, attr
->values
[i
].boolean
);
3066 for (i
= 0; i
< attr
->num_values
; i
++)
3068 if (attr
->values
[i
].integer
< 1)
3073 print_test_error("\"%s\": Bad enum value %d - out of range "
3074 "(RFC 2911 section 4.1.4).", attr
->name
,
3075 attr
->values
[i
].integer
);
3082 case IPP_TAG_STRING
:
3083 for (i
= 0; i
< attr
->num_values
; i
++)
3085 if (attr
->values
[i
].unknown
.length
> 1023)
3090 print_test_error("\"%s\": Bad octetString value - bad length %d "
3091 "(RFC 2911 section 4.1.10).", attr
->name
,
3092 attr
->values
[i
].unknown
.length
);
3100 for (i
= 0; i
< attr
->num_values
; i
++)
3102 date
= attr
->values
[i
].date
;
3104 if (date
[2] < 1 || date
[2] > 12)
3109 print_test_error("\"%s\": Bad dateTime month %u (RFC 2911 "
3110 "section 4.1.13).", attr
->name
, date
[2]);
3115 if (date
[3] < 1 || date
[3] > 31)
3120 print_test_error("\"%s\": Bad dateTime day %u (RFC 2911 "
3121 "section 4.1.13).", attr
->name
, date
[3]);
3131 print_test_error("\"%s\": Bad dateTime hours %u (RFC 2911 "
3132 "section 4.1.13).", attr
->name
, date
[4]);
3142 print_test_error("\"%s\": Bad dateTime minutes %u (RFC 2911 "
3143 "section 4.1.13).", attr
->name
, date
[5]);
3153 print_test_error("\"%s\": Bad dateTime seconds %u (RFC 2911 "
3154 "section 4.1.13).", attr
->name
, date
[6]);
3164 print_test_error("\"%s\": Bad dateTime deciseconds %u (RFC 2911 "
3165 "section 4.1.13).", attr
->name
, date
[7]);
3170 if (date
[8] != '-' && date
[8] != '+')
3175 print_test_error("\"%s\": Bad dateTime UTC sign '%c' (RFC 2911 "
3176 "section 4.1.13).", attr
->name
, date
[8]);
3186 print_test_error("\"%s\": Bad dateTime UTC hours %u (RFC 2911 "
3187 "section 4.1.13).", attr
->name
, date
[9]);
3197 print_test_error("\"%s\": Bad dateTime UTC minutes %u (RFC 2911 "
3198 "section 4.1.13).", attr
->name
, date
[10]);
3205 case IPP_TAG_RESOLUTION
:
3206 for (i
= 0; i
< attr
->num_values
; i
++)
3208 if (attr
->values
[i
].resolution
.xres
<= 0)
3213 print_test_error("\"%s\": Bad resolution value %dx%d%s - cross "
3214 "feed resolution must be positive (RFC 2911 "
3215 "section 4.1.13).", attr
->name
,
3216 attr
->values
[i
].resolution
.xres
,
3217 attr
->values
[i
].resolution
.yres
,
3218 attr
->values
[i
].resolution
.units
==
3219 IPP_RES_PER_INCH
? "dpi" :
3220 attr
->values
[i
].resolution
.units
==
3221 IPP_RES_PER_CM
? "dpc" : "unknown");
3226 if (attr
->values
[i
].resolution
.yres
<= 0)
3231 print_test_error("\"%s\": Bad resolution value %dx%d%s - feed "
3232 "resolution must be positive (RFC 2911 section "
3233 "4.1.13).", attr
->name
,
3234 attr
->values
[i
].resolution
.xres
,
3235 attr
->values
[i
].resolution
.yres
,
3236 attr
->values
[i
].resolution
.units
==
3237 IPP_RES_PER_INCH
? "dpi" :
3238 attr
->values
[i
].resolution
.units
==
3239 IPP_RES_PER_CM
? "dpc" : "unknown");
3244 if (attr
->values
[i
].resolution
.units
!= IPP_RES_PER_INCH
&&
3245 attr
->values
[i
].resolution
.units
!= IPP_RES_PER_CM
)
3250 print_test_error("\"%s\": Bad resolution value %dx%d%s - bad "
3251 "units value (RFC 2911 section 4.1.13).",
3252 attr
->name
, attr
->values
[i
].resolution
.xres
,
3253 attr
->values
[i
].resolution
.yres
,
3254 attr
->values
[i
].resolution
.units
==
3255 IPP_RES_PER_INCH
? "dpi" :
3256 attr
->values
[i
].resolution
.units
==
3257 IPP_RES_PER_CM
? "dpc" : "unknown");
3264 case IPP_TAG_RANGE
:
3265 for (i
= 0; i
< attr
->num_values
; i
++)
3267 if (attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
3272 print_test_error("\"%s\": Bad rangeOfInteger value %d-%d - lower "
3273 "greater than upper (RFC 2911 section 4.1.13).",
3274 attr
->name
, attr
->values
[i
].range
.lower
,
3275 attr
->values
[i
].range
.upper
);
3282 case IPP_TAG_BEGIN_COLLECTION
:
3283 for (i
= 0; i
< attr
->num_values
; i
++)
3285 for (colattr
= attr
->values
[i
].collection
->attrs
;
3287 colattr
= colattr
->next
)
3289 if (!validate_attr(colattr
, 0))
3296 if (colattr
&& print
)
3298 print_test_error("\"%s\": Bad collection value.", attr
->name
);
3302 validate_attr(colattr
, print
);
3303 colattr
= colattr
->next
;
3310 case IPP_TAG_TEXTLANG
:
3311 for (i
= 0; i
< attr
->num_values
; i
++)
3313 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
3315 if ((*ptr
& 0xe0) == 0xc0)
3318 if ((*ptr
& 0xc0) != 0x80)
3321 else if ((*ptr
& 0xf0) == 0xe0)
3324 if ((*ptr
& 0xc0) != 0x80)
3327 if ((*ptr
& 0xc0) != 0x80)
3330 else if ((*ptr
& 0xf8) == 0xf0)
3333 if ((*ptr
& 0xc0) != 0x80)
3336 if ((*ptr
& 0xc0) != 0x80)
3339 if ((*ptr
& 0xc0) != 0x80)
3342 else if (*ptr
& 0x80)
3351 print_test_error("\"%s\": Bad text value \"%s\" - bad UTF-8 "
3352 "sequence (RFC 2911 section 4.1.1).", attr
->name
,
3353 attr
->values
[i
].string
.text
);
3358 if ((ptr
- attr
->values
[i
].string
.text
) > 1023)
3363 print_test_error("\"%s\": Bad text value \"%s\" - bad length %d "
3364 "(RFC 2911 section 4.1.1).", attr
->name
,
3365 attr
->values
[i
].string
.text
,
3366 (int)strlen(attr
->values
[i
].string
.text
));
3374 case IPP_TAG_NAMELANG
:
3375 for (i
= 0; i
< attr
->num_values
; i
++)
3377 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
3379 if ((*ptr
& 0xe0) == 0xc0)
3382 if ((*ptr
& 0xc0) != 0x80)
3385 else if ((*ptr
& 0xf0) == 0xe0)
3388 if ((*ptr
& 0xc0) != 0x80)
3391 if ((*ptr
& 0xc0) != 0x80)
3394 else if ((*ptr
& 0xf8) == 0xf0)
3397 if ((*ptr
& 0xc0) != 0x80)
3400 if ((*ptr
& 0xc0) != 0x80)
3403 if ((*ptr
& 0xc0) != 0x80)
3406 else if (*ptr
& 0x80)
3415 print_test_error("\"%s\": Bad name value \"%s\" - bad UTF-8 "
3416 "sequence (RFC 2911 section 4.1.2).", attr
->name
,
3417 attr
->values
[i
].string
.text
);
3422 if ((ptr
- attr
->values
[i
].string
.text
) > 1023)
3427 print_test_error("\"%s\": Bad name value \"%s\" - bad length %d "
3428 "(RFC 2911 section 4.1.2).", attr
->name
,
3429 attr
->values
[i
].string
.text
,
3430 (int)strlen(attr
->values
[i
].string
.text
));
3437 case IPP_TAG_KEYWORD
:
3438 for (i
= 0; i
< attr
->num_values
; i
++)
3440 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
3441 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' &&
3445 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
3450 print_test_error("\"%s\": Bad keyword value \"%s\" - invalid "
3451 "character (RFC 2911 section 4.1.3).",
3452 attr
->name
, attr
->values
[i
].string
.text
);
3457 if ((ptr
- attr
->values
[i
].string
.text
) > 255)
3462 print_test_error("\"%s\": Bad keyword value \"%s\" - bad "
3463 "length %d (RFC 2911 section 4.1.3).",
3464 attr
->name
, attr
->values
[i
].string
.text
,
3465 (int)strlen(attr
->values
[i
].string
.text
));
3473 for (i
= 0; i
< attr
->num_values
; i
++)
3475 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
,
3476 attr
->values
[i
].string
.text
,
3477 scheme
, sizeof(scheme
),
3478 userpass
, sizeof(userpass
),
3479 hostname
, sizeof(hostname
),
3480 &port
, resource
, sizeof(resource
));
3482 if (uri_status
< HTTP_URI_OK
)
3487 print_test_error("\"%s\": Bad URI value \"%s\" - %s "
3488 "(RFC 2911 section 4.1.5).", attr
->name
,
3489 attr
->values
[i
].string
.text
,
3490 URIStatusStrings
[uri_status
-
3491 HTTP_URI_OVERFLOW
]);
3496 if (strlen(attr
->values
[i
].string
.text
) > 1023)
3501 print_test_error("\"%s\": Bad URI value \"%s\" - bad length %d "
3502 "(RFC 2911 section 4.1.5).", attr
->name
,
3503 attr
->values
[i
].string
.text
,
3504 (int)strlen(attr
->values
[i
].string
.text
));
3511 case IPP_TAG_URISCHEME
:
3512 for (i
= 0; i
< attr
->num_values
; i
++)
3514 ptr
= attr
->values
[i
].string
.text
;
3515 if (islower(*ptr
& 255))
3517 for (ptr
++; *ptr
; ptr
++)
3518 if (!islower(*ptr
& 255) && !isdigit(*ptr
& 255) &&
3519 *ptr
!= '+' && *ptr
!= '-' && *ptr
!= '.')
3523 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
3528 print_test_error("\"%s\": Bad uriScheme value \"%s\" - bad "
3529 "characters (RFC 2911 section 4.1.6).",
3530 attr
->name
, attr
->values
[i
].string
.text
);
3535 if ((ptr
- attr
->values
[i
].string
.text
) > 63)
3540 print_test_error("\"%s\": Bad uriScheme value \"%s\" - bad "
3541 "length %d (RFC 2911 section 4.1.6).",
3542 attr
->name
, attr
->values
[i
].string
.text
,
3543 (int)strlen(attr
->values
[i
].string
.text
));
3550 case IPP_TAG_CHARSET
:
3551 for (i
= 0; i
< attr
->num_values
; i
++)
3553 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
3554 if (!isprint(*ptr
& 255) || isupper(*ptr
& 255) ||
3555 isspace(*ptr
& 255))
3558 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
3563 print_test_error("\"%s\": Bad charset value \"%s\" - bad "
3564 "characters (RFC 2911 section 4.1.7).",
3565 attr
->name
, attr
->values
[i
].string
.text
);
3570 if ((ptr
- attr
->values
[i
].string
.text
) > 40)
3575 print_test_error("\"%s\": Bad charset value \"%s\" - bad "
3576 "length %d (RFC 2911 section 4.1.7).",
3577 attr
->name
, attr
->values
[i
].string
.text
,
3578 (int)strlen(attr
->values
[i
].string
.text
));
3585 case IPP_TAG_LANGUAGE
:
3587 * The following regular expression is derived from the ABNF for
3588 * language tags in RFC 4646. All I can say is that this is the
3589 * easiest way to check the values...
3592 if ((i
= regcomp(&re
,
3594 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
3596 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
3597 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
3598 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
3599 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
3600 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
3602 "x(-[a-z0-9]{1,8})+" /* privateuse */
3604 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
3606 REG_NOSUB
| REG_EXTENDED
)) != 0)
3608 char temp
[256]; /* Temporary error string */
3610 regerror(i
, &re
, temp
, sizeof(temp
));
3611 print_fatal_error("Unable to compile naturalLanguage regular "
3612 "expression: %s.", temp
);
3615 for (i
= 0; i
< attr
->num_values
; i
++)
3617 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
3622 print_test_error("\"%s\": Bad naturalLanguage value \"%s\" - bad "
3623 "characters (RFC 2911 section 4.1.8).",
3624 attr
->name
, attr
->values
[i
].string
.text
);
3629 if (strlen(attr
->values
[i
].string
.text
) > 63)
3634 print_test_error("\"%s\": Bad naturalLanguage value \"%s\" - bad "
3635 "length %d (RFC 2911 section 4.1.8).",
3636 attr
->name
, attr
->values
[i
].string
.text
,
3637 (int)strlen(attr
->values
[i
].string
.text
));
3646 case IPP_TAG_MIMETYPE
:
3648 * The following regular expression is derived from the ABNF for
3649 * language tags in RFC 2045 and 4288. All I can say is that this is
3650 * the easiest way to check the values...
3653 if ((i
= regcomp(&re
,
3655 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
3657 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
3658 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
3659 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
3662 REG_NOSUB
| REG_EXTENDED
)) != 0)
3664 char temp
[256]; /* Temporary error string */
3666 regerror(i
, &re
, temp
, sizeof(temp
));
3667 print_fatal_error("Unable to compile mimeMediaType regular "
3668 "expression: %s.", temp
);
3671 for (i
= 0; i
< attr
->num_values
; i
++)
3673 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
3678 print_test_error("\"%s\": Bad mimeMediaType value \"%s\" - bad "
3679 "characters (RFC 2911 section 4.1.9).",
3680 attr
->name
, attr
->values
[i
].string
.text
);
3685 if (strlen(attr
->values
[i
].string
.text
) > 255)
3690 print_test_error("\"%s\": Bad mimeMediaType value \"%s\" - bad "
3691 "length %d (RFC 2911 section 4.1.9).",
3692 attr
->name
, attr
->values
[i
].string
.text
,
3693 (int)strlen(attr
->values
[i
].string
.text
));
3709 * 'with_value()' - Test a WITH-VALUE predicate.
3712 static int /* O - 1 on match, 0 on non-match */
3713 with_value(char *value
, /* I - Value string */
3714 int regex
, /* I - Value is a regular expression */
3715 ipp_attribute_t
*attr
) /* I - Attribute to compare */
3717 int i
; /* Looping var */
3718 char *valptr
; /* Pointer into value */
3722 * NULL matches everything.
3725 if (!value
|| !*value
)
3729 * Compare the value string to the attribute value.
3732 switch (attr
->value_tag
)
3734 case IPP_TAG_INTEGER
:
3736 for (i
= 0; i
< attr
->num_values
; i
++)
3738 char op
, /* Comparison operator */
3739 *nextptr
; /* Next pointer */
3740 int intvalue
; /* Integer value */
3744 if (!strncmp(valptr
, "no-value,", 9))
3747 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
3748 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
3749 *valptr
== '=' || *valptr
== '>')
3752 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
3754 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
3762 intvalue
= strtol(valptr
, &nextptr
, 0);
3763 if (nextptr
== valptr
)
3770 if (attr
->values
[i
].integer
== intvalue
)
3774 if (attr
->values
[i
].integer
< intvalue
)
3778 if (attr
->values
[i
].integer
> intvalue
)
3786 case IPP_TAG_BOOLEAN
:
3787 for (i
= 0; i
< attr
->num_values
; i
++)
3789 if (!strcmp(value
, "true") == attr
->values
[i
].boolean
)
3794 case IPP_TAG_NOVALUE
:
3795 return (!strcmp(value
, "no-value") || !strncmp(value
, "no-value,", 9));
3797 case IPP_TAG_CHARSET
:
3798 case IPP_TAG_KEYWORD
:
3799 case IPP_TAG_LANGUAGE
:
3800 case IPP_TAG_MIMETYPE
:
3802 case IPP_TAG_NAMELANG
:
3804 case IPP_TAG_TEXTLANG
:
3806 case IPP_TAG_URISCHEME
:
3810 * Value is an extended, case-sensitive POSIX regular expression...
3813 regex_t re
; /* Regular expression */
3815 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
3817 char temp
[256]; /* Temporary string */
3819 regerror(i
, &re
, temp
, sizeof(temp
));
3821 print_fatal_error("Unable to compile WITH-VALUE regular expression "
3822 "\"%s\" - %s", value
, temp
);
3827 * See if ALL of the values match the given regular expression.
3830 for (i
= 0; i
< attr
->num_values
; i
++)
3832 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
3838 return (i
== attr
->num_values
);
3843 * Value is a literal string, see if at least one value matches the
3847 for (i
= 0; i
< attr
->num_values
; i
++)
3849 if (!strcmp(value
, attr
->values
[i
].string
.text
))
3864 * End of "$Id: ipptest.c 9000 2010-02-24 06:29:38Z mike $".