Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
srohatgi01
GitHub Repository: srohatgi01/cups
Path: blob/master/tools/ipptool.c
1090 views
1
/*
2
* ipptool command for CUPS.
3
*
4
* Copyright © 2021-2022 by OpenPrinting.
5
* Copyright © 2020 by The Printer Working Group.
6
* Copyright © 2007-2021 by Apple Inc.
7
* Copyright © 1997-2007 by Easy Software Products.
8
*
9
* Licensed under Apache License v2.0. See the file "LICENSE" for more
10
* information.
11
*/
12
13
/*
14
* Include necessary headers...
15
*/
16
17
#include <cups/cups-private.h>
18
#include <regex.h>
19
#include <sys/stat.h>
20
#ifdef _WIN32
21
# include <windows.h>
22
# ifndef R_OK
23
# define R_OK 0
24
# endif /* !R_OK */
25
#else
26
# include <signal.h>
27
# include <termios.h>
28
#endif /* _WIN32 */
29
#ifndef O_BINARY
30
# define O_BINARY 0
31
#endif /* !O_BINARY */
32
33
34
/*
35
* Limits...
36
*/
37
38
#define MAX_EXPECT 200 // Maximum number of EXPECT directives
39
#define MAX_DISPLAY 200 // Maximum number of DISPLAY directives
40
#define MAX_MONITOR 10 // Maximum number of MONITOR-PRINTER-STATE EXPECT directives
41
42
43
/*
44
* Types...
45
*/
46
47
typedef enum ipptool_transfer_e /**** How to send request data ****/
48
{
49
IPPTOOL_TRANSFER_AUTO, /* Chunk for files, length for static */
50
IPPTOOL_TRANSFER_CHUNKED, /* Chunk always */
51
IPPTOOL_TRANSFER_LENGTH /* Length always */
52
} ipptool_transfer_t;
53
54
typedef enum ipptool_output_e /**** Output mode ****/
55
{
56
IPPTOOL_OUTPUT_QUIET, /* No output */
57
IPPTOOL_OUTPUT_TEST, /* Traditional CUPS test output */
58
IPPTOOL_OUTPUT_PLIST, /* XML plist test output */
59
IPPTOOL_OUTPUT_IPPSERVER, /* ippserver attribute file output */
60
IPPTOOL_OUTPUT_LIST, /* Tabular list output */
61
IPPTOOL_OUTPUT_CSV, /* Comma-separated values output */
62
IPPTOOL_OUTPUT_JSON /* JSON output */
63
} ipptool_output_t;
64
65
typedef enum ipptool_with_e /**** WITH flags ****/
66
{
67
IPPTOOL_WITH_LITERAL = 0, /* Match string is a literal value */
68
IPPTOOL_WITH_ALL = 1, /* Must match all values */
69
IPPTOOL_WITH_REGEX = 2, /* Match string is a regular expression */
70
IPPTOOL_WITH_HOSTNAME = 4, /* Match string is a URI hostname */
71
IPPTOOL_WITH_RESOURCE = 8, /* Match string is a URI resource */
72
IPPTOOL_WITH_SCHEME = 16 /* Match string is a URI scheme */
73
} ipptool_with_t;
74
75
typedef struct ipptool_expect_s /**** Expected attribute info ****/
76
{
77
int optional, /* Optional attribute? */
78
not_expect, /* Don't expect attribute? */
79
expect_all; /* Expect all attributes to match/not match */
80
char *name, /* Attribute name */
81
*of_type, /* Type name */
82
*same_count_as, /* Parallel attribute name */
83
*if_defined, /* Only required if variable defined */
84
*if_not_defined, /* Only required if variable is not defined */
85
*with_value, /* Attribute must include this value */
86
*with_value_from, /* Attribute must have one of the values in this attribute */
87
*define_match, /* Variable to define on match */
88
*define_no_match, /* Variable to define on no-match */
89
*define_value, /* Variable to define with value */
90
*display_match; /* Message to display on a match */
91
int repeat_limit, /* Maximum number of times to repeat */
92
repeat_match, /* Repeat test on match */
93
repeat_no_match, /* Repeat test on no match */
94
with_distinct, /* WITH-DISTINCT-VALUES? */
95
with_flags, /* WITH flags */
96
count; /* Expected count if > 0 */
97
ipp_tag_t in_group; /* IN-GROUP value */
98
} ipptool_expect_t;
99
100
typedef struct ipptool_status_s /**** Status info ****/
101
{
102
ipp_status_t status; /* Expected status code */
103
char *if_defined, /* Only if variable is defined */
104
*if_not_defined, /* Only if variable is not defined */
105
*define_match, /* Variable to define on match */
106
*define_no_match, /* Variable to define on no-match */
107
*define_value; /* Variable to define with value */
108
int repeat_limit, /* Maximum number of times to repeat */
109
repeat_match, /* Repeat the test when it does not match */
110
repeat_no_match; /* Repeat the test when it matches */
111
} ipptool_status_t;
112
113
typedef struct ipptool_test_s /**** Test Data ****/
114
{
115
/* Global Options */
116
_ipp_vars_t *vars; /* Variables */
117
http_encryption_t encryption; /* Encryption for connection */
118
int family; /* Address family */
119
ipptool_output_t output; /* Output mode */
120
int repeat_on_busy; /* Repeat tests on server-error-busy */
121
int stop_after_include_error;
122
/* Stop after include errors? */
123
double timeout; /* Timeout for connection */
124
int validate_headers, /* Validate HTTP headers in response? */
125
verbosity; /* Show all attributes? */
126
127
/* Test Defaults */
128
int def_ignore_errors; /* Default IGNORE-ERRORS value */
129
ipptool_transfer_t def_transfer; /* Default TRANSFER value */
130
int def_version; /* Default IPP version */
131
132
/* Global State */
133
http_t *http; /* HTTP connection to printer/server */
134
cups_file_t *outfile; /* Output file */
135
int show_header, /* Show the test header? */
136
xml_header; /* 1 if XML plist header was written */
137
int pass, /* Have we passed all tests? */
138
test_count, /* Number of tests (total) */
139
pass_count, /* Number of tests that passed */
140
fail_count, /* Number of tests that failed */
141
skip_count; /* Number of tests that were skipped */
142
143
/* Per-Test State */
144
cups_array_t *errors; /* Errors array */
145
int prev_pass, /* Result of previous test */
146
skip_previous; /* Skip on previous test failure? */
147
char compression[16]; /* COMPRESSION value */
148
useconds_t delay; /* Initial delay */
149
int num_displayed; /* Number of displayed attributes */
150
char *displayed[MAX_DISPLAY];/* Displayed attributes */
151
int num_expects; /* Number of expected attributes */
152
ipptool_expect_t expects[MAX_EXPECT], /* Expected attributes */
153
*expect, /* Current expected attribute */
154
*last_expect; /* Last EXPECT (for predicates) */
155
char file[1024], /* Data filename */
156
file_id[1024]; /* File identifier */
157
int ignore_errors; /* Ignore test failures? */
158
char name[1024]; /* Test name */
159
char pause[1024]; /* PAUSE value */
160
useconds_t repeat_interval; /* Repeat interval (delay) */
161
int request_id; /* Current request ID */
162
char resource[512]; /* Resource for request */
163
int pass_test, /* Pass this test? */
164
skip_test, /* Skip this test? */
165
num_statuses; /* Number of valid status codes */
166
ipptool_status_t statuses[100], /* Valid status codes */
167
*last_status; /* Last STATUS (for predicates) */
168
char test_id[1024]; /* Test identifier */
169
ipptool_transfer_t transfer; /* To chunk or not to chunk */
170
int version; /* IPP version number to use */
171
_cups_thread_t monitor_thread; /* Monitoring thread ID */
172
int monitor_done; /* Set to 1 to stop monitor thread */
173
char *monitor_uri; /* MONITOR-PRINTER-STATE URI */
174
useconds_t monitor_delay, /* MONITOR-PRINTER-STATE DELAY value, if any */
175
monitor_interval; /* MONITOR-PRINTER-STATE DELAY interval */
176
int num_monitor_expects; /* Number MONITOR-PRINTER-STATE EXPECTs */
177
ipptool_expect_t monitor_expects[MAX_MONITOR];
178
/* MONITOR-PRINTER-STATE EXPECTs */
179
} ipptool_test_t;
180
181
182
/*
183
* Globals...
184
*/
185
186
static int Cancel = 0; /* Cancel test? */
187
188
189
/*
190
* Local functions...
191
*/
192
193
static void add_stringf(cups_array_t *a, const char *s, ...) _CUPS_FORMAT(2, 3);
194
static int compare_uris(const char *a, const char *b);
195
static void copy_hex_string(char *buffer, unsigned char *data, int datalen, size_t bufsize);
196
static void *do_monitor_printer_state(ipptool_test_t *data);
197
static int do_test(_ipp_file_t *f, ipptool_test_t *data);
198
static int do_tests(const char *testfile, ipptool_test_t *data);
199
static int error_cb(_ipp_file_t *f, ipptool_test_t *data, const char *error);
200
static int expect_matches(ipptool_expect_t *expect, ipp_attribute_t *attr);
201
static char *get_filename(const char *testfile, char *dst, const char *src, size_t dstsize);
202
static const char *get_string(ipp_attribute_t *attr, int element, int flags, char *buffer, size_t bufsize);
203
static void init_data(ipptool_test_t *data);
204
static char *iso_date(const ipp_uchar_t *date);
205
static int parse_monitor_printer_state(_ipp_file_t *f, ipptool_test_t *data);
206
static void pause_message(const char *message);
207
static void print_attr(cups_file_t *outfile, ipptool_output_t output, ipp_attribute_t *attr, ipp_tag_t *group);
208
static ipp_attribute_t *print_csv(ipptool_test_t *data, ipp_t *ipp, ipp_attribute_t *attr, int num_displayed, char **displayed, size_t *widths);
209
static void print_fatal_error(ipptool_test_t *data, const char *s, ...) _CUPS_FORMAT(2, 3);
210
static void print_ippserver_attr(ipptool_test_t *data, ipp_attribute_t *attr, int indent);
211
static void print_ippserver_string(ipptool_test_t *data, const char *s, size_t len);
212
static void print_json_attr(ipptool_test_t *data, ipp_attribute_t *attr, int indent);
213
static void print_json_string(ipptool_test_t *data, const char *s, size_t len);
214
static ipp_attribute_t *print_line(ipptool_test_t *data, ipp_t *ipp, ipp_attribute_t *attr, int num_displayed, char **displayed, size_t *widths);
215
static void print_xml_header(ipptool_test_t *data);
216
static void print_xml_string(cups_file_t *outfile, const char *element, const char *s);
217
static void print_xml_trailer(ipptool_test_t *data, int success, const char *message);
218
#ifndef _WIN32
219
static void sigterm_handler(int sig);
220
#endif /* _WIN32 */
221
static int timeout_cb(http_t *http, void *user_data);
222
static int token_cb(_ipp_file_t *f, _ipp_vars_t *vars, ipptool_test_t *data, const char *token);
223
static void usage(void) _CUPS_NORETURN;
224
static int with_distinct_values(cups_array_t *errors, ipp_attribute_t *attr);
225
static const char *with_flags_string(int flags);
226
static int with_value(ipptool_test_t *data, cups_array_t *errors, char *value, int flags, ipp_attribute_t *attr, char *matchbuf, size_t matchlen);
227
static int with_value_from(cups_array_t *errors, ipp_attribute_t *fromattr, ipp_attribute_t *attr, char *matchbuf, size_t matchlen);
228
229
230
/*
231
* 'main()' - Parse options and do tests.
232
*/
233
234
int /* O - Exit status */
235
main(int argc, /* I - Number of command-line args */
236
char *argv[]) /* I - Command-line arguments */
237
{
238
int i; /* Looping var */
239
int status; /* Status of tests... */
240
char *opt, /* Current option */
241
name[1024], /* Name/value buffer */
242
*value, /* Pointer to value */
243
filename[1024], /* Real filename */
244
testname[1024]; /* Real test filename */
245
const char *ext, /* Extension on filename */
246
*testfile; /* Test file to use */
247
int interval, /* Test interval in microseconds */
248
repeat; /* Repeat count */
249
_ipp_vars_t vars; /* Variables */
250
ipptool_test_t data; /* Test data */
251
_cups_globals_t *cg = _cupsGlobals();
252
/* Global data */
253
254
255
#ifndef _WIN32
256
/*
257
* Catch SIGINT and SIGTERM...
258
*/
259
260
signal(SIGINT, sigterm_handler);
261
signal(SIGTERM, sigterm_handler);
262
#endif /* !_WIN32 */
263
264
/*
265
* Initialize the locale and variables...
266
*/
267
268
_cupsSetLocale(argv);
269
270
init_data(&data);
271
272
_ippVarsInit(&vars, NULL, (_ipp_ferror_cb_t)error_cb, (_ipp_ftoken_cb_t)token_cb);
273
data.vars = &vars;
274
275
_ippVarsSet(data.vars, "date-start", iso_date(ippTimeToDate(time(NULL))));
276
277
/*
278
* We need at least:
279
*
280
* ipptool URI testfile
281
*/
282
283
interval = 0;
284
repeat = 0;
285
status = 0;
286
testfile = NULL;
287
288
for (i = 1; i < argc; i ++)
289
{
290
if (!strcmp(argv[i], "--help"))
291
{
292
usage();
293
}
294
else if (!strcmp(argv[i], "--ippserver"))
295
{
296
i ++;
297
298
if (i >= argc)
299
{
300
_cupsLangPuts(stderr, _("ipptool: Missing filename for \"--ippserver\"."));
301
usage();
302
}
303
304
if (data.outfile != cupsFileStdout())
305
usage();
306
307
if ((data.outfile = cupsFileOpen(argv[i], "w")) == NULL)
308
{
309
_cupsLangPrintf(stderr, _("%s: Unable to open \"%s\": %s"), "ipptool", argv[i], strerror(errno));
310
exit(1);
311
}
312
313
data.output = IPPTOOL_OUTPUT_IPPSERVER;
314
}
315
else if (!strcmp(argv[i], "--stop-after-include-error"))
316
{
317
data.stop_after_include_error = 1;
318
}
319
else if (!strcmp(argv[i], "--version"))
320
{
321
puts(CUPS_SVERSION);
322
return (0);
323
}
324
else if (argv[i][0] == '-')
325
{
326
for (opt = argv[i] + 1; *opt; opt ++)
327
{
328
switch (*opt)
329
{
330
case '4' : /* Connect using IPv4 only */
331
data.family = AF_INET;
332
break;
333
334
#ifdef AF_INET6
335
case '6' : /* Connect using IPv6 only */
336
data.family = AF_INET6;
337
break;
338
#endif /* AF_INET6 */
339
340
case 'C' : /* Enable HTTP chunking */
341
data.def_transfer = IPPTOOL_TRANSFER_CHUNKED;
342
break;
343
344
case 'E' : /* Encrypt with TLS */
345
#ifdef HAVE_TLS
346
data.encryption = HTTP_ENCRYPT_REQUIRED;
347
#else
348
_cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."),
349
argv[0]);
350
#endif /* HAVE_TLS */
351
break;
352
353
case 'I' : /* Ignore errors */
354
data.def_ignore_errors = 1;
355
break;
356
357
case 'L' : /* Disable HTTP chunking */
358
data.def_transfer = IPPTOOL_TRANSFER_LENGTH;
359
break;
360
361
case 'P' : /* Output to plist file */
362
i ++;
363
364
if (i >= argc)
365
{
366
_cupsLangPrintf(stderr, _("%s: Missing filename for \"-P\"."), "ipptool");
367
usage();
368
}
369
370
if (data.outfile != cupsFileStdout())
371
usage();
372
373
if ((data.outfile = cupsFileOpen(argv[i], "w")) == NULL)
374
{
375
_cupsLangPrintf(stderr, _("%s: Unable to open \"%s\": %s"), "ipptool", argv[i], strerror(errno));
376
exit(1);
377
}
378
379
data.output = IPPTOOL_OUTPUT_PLIST;
380
381
if (interval || repeat)
382
{
383
_cupsLangPuts(stderr, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
384
usage();
385
}
386
break;
387
388
case 'R' : /* Repeat on server-error-busy */
389
data.repeat_on_busy = 1;
390
break;
391
392
case 'S' : /* Encrypt with SSL */
393
#ifdef HAVE_TLS
394
data.encryption = HTTP_ENCRYPT_ALWAYS;
395
#else
396
_cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."), "ipptool");
397
#endif /* HAVE_TLS */
398
break;
399
400
case 'T' : /* Set timeout */
401
i ++;
402
403
if (i >= argc)
404
{
405
_cupsLangPrintf(stderr, _("%s: Missing timeout for \"-T\"."), "ipptool");
406
usage();
407
}
408
409
data.timeout = _cupsStrScand(argv[i], NULL, localeconv());
410
break;
411
412
case 'V' : /* Set IPP version */
413
i ++;
414
415
if (i >= argc)
416
{
417
_cupsLangPrintf(stderr, _("%s: Missing version for \"-V\"."), "ipptool");
418
usage();
419
}
420
421
if (!strcmp(argv[i], "1.0"))
422
{
423
data.def_version = 10;
424
}
425
else if (!strcmp(argv[i], "1.1"))
426
{
427
data.def_version = 11;
428
}
429
else if (!strcmp(argv[i], "2.0"))
430
{
431
data.def_version = 20;
432
}
433
else if (!strcmp(argv[i], "2.1"))
434
{
435
data.def_version = 21;
436
}
437
else if (!strcmp(argv[i], "2.2"))
438
{
439
data.def_version = 22;
440
}
441
else
442
{
443
_cupsLangPrintf(stderr, _("%s: Bad version %s for \"-V\"."), "ipptool", argv[i]);
444
usage();
445
}
446
break;
447
448
case 'X' : /* Produce XML output */
449
data.output = IPPTOOL_OUTPUT_PLIST;
450
451
if (interval || repeat)
452
{
453
_cupsLangPuts(stderr, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
454
usage();
455
}
456
break;
457
458
case 'c' : /* CSV output */
459
data.output = IPPTOOL_OUTPUT_CSV;
460
break;
461
462
case 'd' : /* Define a variable */
463
i ++;
464
465
if (i >= argc)
466
{
467
_cupsLangPuts(stderr, _("ipptool: Missing name=value for \"-d\"."));
468
usage();
469
}
470
471
strlcpy(name, argv[i], sizeof(name));
472
if ((value = strchr(name, '=')) != NULL)
473
*value++ = '\0';
474
else
475
value = name + strlen(name);
476
477
_ippVarsSet(data.vars, name, value);
478
break;
479
480
case 'f' : /* Set the default test filename */
481
i ++;
482
483
if (i >= argc)
484
{
485
_cupsLangPuts(stderr, _("ipptool: Missing filename for \"-f\"."));
486
usage();
487
}
488
489
if (access(argv[i], 0))
490
{
491
/*
492
* Try filename.gz...
493
*/
494
495
snprintf(filename, sizeof(filename), "%s.gz", argv[i]);
496
if (access(filename, 0) && filename[0] != '/'
497
#ifdef _WIN32
498
&& (!isalpha(filename[0] & 255) || filename[1] != ':')
499
#endif /* _WIN32 */
500
)
501
{
502
snprintf(filename, sizeof(filename), "%s/ipptool/%s", cg->cups_datadir, argv[i]);
503
if (access(filename, 0))
504
{
505
snprintf(filename, sizeof(filename), "%s/ipptool/%s.gz", cg->cups_datadir, argv[i]);
506
if (access(filename, 0))
507
strlcpy(filename, argv[i], sizeof(filename));
508
}
509
}
510
}
511
else
512
strlcpy(filename, argv[i], sizeof(filename));
513
514
_ippVarsSet(data.vars, "filename", filename);
515
516
if ((ext = strrchr(filename, '.')) != NULL)
517
{
518
/*
519
* Guess the MIME media type based on the extension...
520
*/
521
522
if (!_cups_strcasecmp(ext, ".gif"))
523
_ippVarsSet(data.vars, "filetype", "image/gif");
524
else if (!_cups_strcasecmp(ext, ".htm") ||
525
!_cups_strcasecmp(ext, ".htm.gz") ||
526
!_cups_strcasecmp(ext, ".html") ||
527
!_cups_strcasecmp(ext, ".html.gz"))
528
_ippVarsSet(data.vars, "filetype", "text/html");
529
else if (!_cups_strcasecmp(ext, ".jpg") ||
530
!_cups_strcasecmp(ext, ".jpeg"))
531
_ippVarsSet(data.vars, "filetype", "image/jpeg");
532
else if (!_cups_strcasecmp(ext, ".pcl") ||
533
!_cups_strcasecmp(ext, ".pcl.gz"))
534
_ippVarsSet(data.vars, "filetype", "application/vnd.hp-PCL");
535
else if (!_cups_strcasecmp(ext, ".pdf"))
536
_ippVarsSet(data.vars, "filetype", "application/pdf");
537
else if (!_cups_strcasecmp(ext, ".png"))
538
_ippVarsSet(data.vars, "filetype", "image/png");
539
else if (!_cups_strcasecmp(ext, ".ps") ||
540
!_cups_strcasecmp(ext, ".ps.gz"))
541
_ippVarsSet(data.vars, "filetype", "application/postscript");
542
else if (!_cups_strcasecmp(ext, ".pwg") ||
543
!_cups_strcasecmp(ext, ".pwg.gz") ||
544
!_cups_strcasecmp(ext, ".ras") ||
545
!_cups_strcasecmp(ext, ".ras.gz"))
546
_ippVarsSet(data.vars, "filetype", "image/pwg-raster");
547
else if (!_cups_strcasecmp(ext, ".tif") ||
548
!_cups_strcasecmp(ext, ".tiff"))
549
_ippVarsSet(data.vars, "filetype", "image/tiff");
550
else if (!_cups_strcasecmp(ext, ".txt") ||
551
!_cups_strcasecmp(ext, ".txt.gz"))
552
_ippVarsSet(data.vars, "filetype", "text/plain");
553
else if (!_cups_strcasecmp(ext, ".urf") ||
554
!_cups_strcasecmp(ext, ".urf.gz"))
555
_ippVarsSet(data.vars, "filetype", "image/urf");
556
else if (!_cups_strcasecmp(ext, ".xps"))
557
_ippVarsSet(data.vars, "filetype", "application/openxps");
558
else
559
_ippVarsSet(data.vars, "filetype", "application/octet-stream");
560
}
561
else
562
{
563
/*
564
* Use the "auto-type" MIME media type...
565
*/
566
567
_ippVarsSet(data.vars, "filetype", "application/octet-stream");
568
}
569
break;
570
571
case 'h' : /* Validate response headers */
572
data.validate_headers = 1;
573
break;
574
575
case 'i' : /* Test every N seconds */
576
i ++;
577
578
if (i >= argc)
579
{
580
_cupsLangPuts(stderr, _("ipptool: Missing seconds for \"-i\"."));
581
usage();
582
}
583
else
584
{
585
interval = (int)(_cupsStrScand(argv[i], NULL, localeconv()) * 1000000.0);
586
if (interval <= 0)
587
{
588
_cupsLangPuts(stderr, _("ipptool: Invalid seconds for \"-i\"."));
589
usage();
590
}
591
}
592
593
if ((data.output == IPPTOOL_OUTPUT_PLIST || data.output == IPPTOOL_OUTPUT_IPPSERVER) && interval)
594
{
595
_cupsLangPuts(stderr, _("ipptool: \"-i\" and \"-n\" are incompatible with \"--ippserver\", \"-P\", and \"-X\"."));
596
usage();
597
}
598
break;
599
600
case 'j' : /* JSON output */
601
data.output = IPPTOOL_OUTPUT_JSON;
602
break;
603
604
case 'l' : /* List as a table */
605
data.output = IPPTOOL_OUTPUT_LIST;
606
break;
607
608
case 'n' : /* Repeat count */
609
i ++;
610
611
if (i >= argc)
612
{
613
_cupsLangPuts(stderr, _("ipptool: Missing count for \"-n\"."));
614
usage();
615
}
616
else
617
repeat = atoi(argv[i]);
618
619
if ((data.output == IPPTOOL_OUTPUT_PLIST || data.output == IPPTOOL_OUTPUT_IPPSERVER) && repeat)
620
{
621
_cupsLangPuts(stderr, _("ipptool: \"-i\" and \"-n\" are incompatible with \"--ippserver\", \"-P\", and \"-X\"."));
622
usage();
623
}
624
break;
625
626
case 'q' : /* Be quiet */
627
data.output = IPPTOOL_OUTPUT_QUIET;
628
break;
629
630
case 't' : /* CUPS test output */
631
data.output = IPPTOOL_OUTPUT_TEST;
632
break;
633
634
case 'v' : /* Be verbose */
635
data.verbosity ++;
636
break;
637
638
default :
639
_cupsLangPrintf(stderr, _("%s: Unknown option \"-%c\"."), "ipptool", *opt);
640
usage();
641
}
642
}
643
}
644
else if (!strncmp(argv[i], "ipp://", 6) || !strncmp(argv[i], "http://", 7)
645
#ifdef HAVE_TLS
646
|| !strncmp(argv[i], "ipps://", 7) || !strncmp(argv[i], "https://", 8)
647
#endif /* HAVE_TLS */
648
)
649
{
650
/*
651
* Set URI...
652
*/
653
654
if (data.vars->uri)
655
{
656
_cupsLangPuts(stderr, _("ipptool: May only specify a single URI."));
657
usage();
658
}
659
660
#ifdef HAVE_TLS
661
if (!strncmp(argv[i], "ipps://", 7) || !strncmp(argv[i], "https://", 8))
662
data.encryption = HTTP_ENCRYPT_ALWAYS;
663
#endif /* HAVE_TLS */
664
665
if (!_ippVarsSet(data.vars, "uri", argv[i]))
666
{
667
_cupsLangPrintf(stderr, _("ipptool: Bad URI \"%s\"."), argv[i]);
668
return (1);
669
}
670
671
if (data.vars->username[0] && data.vars->password)
672
cupsSetPasswordCB2(_ippVarsPasswordCB, data.vars);
673
}
674
else
675
{
676
/*
677
* Run test...
678
*/
679
680
if (!data.vars->uri)
681
{
682
_cupsLangPuts(stderr, _("ipptool: URI required before test file."));
683
_cupsLangPuts(stderr, argv[i]);
684
usage();
685
}
686
687
if (access(argv[i], 0) && argv[i][0] != '/'
688
#ifdef _WIN32
689
&& (!isalpha(argv[i][0] & 255) || argv[i][1] != ':')
690
#endif /* _WIN32 */
691
)
692
{
693
snprintf(testname, sizeof(testname), "%s/ipptool/%s", cg->cups_datadir, argv[i]);
694
if (access(testname, 0))
695
testfile = argv[i];
696
else
697
testfile = testname;
698
}
699
else
700
testfile = argv[i];
701
702
if (access(testfile, 0))
703
{
704
_cupsLangPrintf(stderr, _("%s: Unable to open \"%s\": %s"), "ipptool", testfile, strerror(errno));
705
status = 1;
706
}
707
else if (!do_tests(testfile, &data))
708
status = 1;
709
}
710
}
711
712
if (!data.vars->uri || !testfile)
713
usage();
714
715
/*
716
* Loop if the interval is set...
717
*/
718
719
if (data.output == IPPTOOL_OUTPUT_PLIST)
720
print_xml_trailer(&data, !status, NULL);
721
else if (interval > 0 && repeat > 0)
722
{
723
while (repeat > 1)
724
{
725
usleep((useconds_t)interval);
726
do_tests(testfile, &data);
727
repeat --;
728
}
729
}
730
else if (interval > 0)
731
{
732
for (;;)
733
{
734
usleep((useconds_t)interval);
735
do_tests(testfile, &data);
736
}
737
}
738
739
if ((data.output == IPPTOOL_OUTPUT_TEST || (data.output == IPPTOOL_OUTPUT_PLIST && data.outfile)) && data.test_count > 1)
740
{
741
/*
742
* Show a summary report if there were multiple tests...
743
*/
744
745
cupsFilePrintf(cupsFileStdout(), "\nSummary: %d tests, %d passed, %d failed, %d skipped\nScore: %d%%\n", data.test_count, data.pass_count, data.fail_count, data.skip_count, 100 * (data.pass_count + data.skip_count) / data.test_count);
746
}
747
748
cupsFileClose(data.outfile);
749
750
/*
751
* Exit...
752
*/
753
754
return (status);
755
}
756
757
758
/*
759
* 'add_stringf()' - Add a formatted string to an array.
760
*/
761
762
static void
763
add_stringf(cups_array_t *a, /* I - Array */
764
const char *s, /* I - Printf-style format string */
765
...) /* I - Additional args as needed */
766
{
767
char buffer[10240]; /* Format buffer */
768
va_list ap; /* Argument pointer */
769
770
771
/*
772
* Don't bother is the array is NULL...
773
*/
774
775
if (!a)
776
return;
777
778
/*
779
* Format the message...
780
*/
781
782
va_start(ap, s);
783
vsnprintf(buffer, sizeof(buffer), s, ap);
784
va_end(ap);
785
786
/*
787
* Add it to the array...
788
*/
789
790
cupsArrayAdd(a, buffer);
791
}
792
793
794
/*
795
* 'compare_uris()' - Compare two URIs...
796
*/
797
798
static int /* O - Result of comparison */
799
compare_uris(const char *a, /* I - First URI */
800
const char *b) /* I - Second URI */
801
{
802
char ascheme[32], /* Components of first URI */
803
auserpass[256],
804
ahost[256],
805
aresource[256];
806
int aport;
807
char bscheme[32], /* Components of second URI */
808
buserpass[256],
809
bhost[256],
810
bresource[256];
811
int bport;
812
char *ptr; /* Pointer into string */
813
int result; /* Result of comparison */
814
815
816
/*
817
* Separate the URIs into their components...
818
*/
819
820
if (httpSeparateURI(HTTP_URI_CODING_ALL, a, ascheme, sizeof(ascheme), auserpass, sizeof(auserpass), ahost, sizeof(ahost), &aport, aresource, sizeof(aresource)) < HTTP_URI_STATUS_OK)
821
return (-1);
822
823
if (httpSeparateURI(HTTP_URI_CODING_ALL, b, bscheme, sizeof(bscheme), buserpass, sizeof(buserpass), bhost, sizeof(bhost), &bport, bresource, sizeof(bresource)) < HTTP_URI_STATUS_OK)
824
return (-1);
825
826
/*
827
* Strip trailing dots from the host components, if present...
828
*/
829
830
if ((ptr = ahost + strlen(ahost) - 1) > ahost && *ptr == '.')
831
*ptr = '\0';
832
833
if ((ptr = bhost + strlen(bhost) - 1) > bhost && *ptr == '.')
834
*ptr = '\0';
835
836
/*
837
* Compare each component...
838
*/
839
840
if ((result = _cups_strcasecmp(ascheme, bscheme)) != 0)
841
return (result);
842
843
if ((result = strcmp(auserpass, buserpass)) != 0)
844
return (result);
845
846
if ((result = _cups_strcasecmp(ahost, bhost)) != 0)
847
return (result);
848
849
if (aport != bport)
850
return (aport - bport);
851
852
if (!_cups_strcasecmp(ascheme, "mailto") || !_cups_strcasecmp(ascheme, "urn"))
853
return (_cups_strcasecmp(aresource, bresource));
854
else
855
return (strcmp(aresource, bresource));
856
}
857
858
859
/*
860
* 'copy_hex_string()' - Copy an octetString to a C string and encode as hex if
861
* needed.
862
*/
863
864
static void
865
copy_hex_string(char *buffer, /* I - String buffer */
866
unsigned char *data, /* I - octetString data */
867
int datalen, /* I - octetString length */
868
size_t bufsize) /* I - Size of string buffer */
869
{
870
char *bufptr, /* Pointer into string buffer */
871
*bufend = buffer + bufsize - 2;
872
/* End of string buffer */
873
unsigned char *dataptr, /* Pointer into octetString data */
874
*dataend = data + datalen;
875
/* End of octetString data */
876
static const char *hexdigits = "0123456789ABCDEF";
877
/* Hex digits */
878
879
880
/*
881
* First see if there are any non-ASCII bytes in the octetString...
882
*/
883
884
for (dataptr = data; dataptr < dataend; dataptr ++)
885
if (*dataptr < 0x20 || *dataptr >= 0x7f)
886
break;
887
888
if (dataptr < dataend)
889
{
890
/*
891
* Yes, encode as hex...
892
*/
893
894
*buffer = '<';
895
896
for (bufptr = buffer + 1, dataptr = data; bufptr < bufend && dataptr < dataend; dataptr ++)
897
{
898
*bufptr++ = hexdigits[*dataptr >> 4];
899
*bufptr++ = hexdigits[*dataptr & 15];
900
}
901
902
if (bufptr < bufend)
903
*bufptr++ = '>';
904
905
*bufptr = '\0';
906
}
907
else
908
{
909
/*
910
* No, copy as a string...
911
*/
912
913
if ((size_t)datalen > bufsize)
914
datalen = (int)bufsize - 1;
915
916
memcpy(buffer, data, (size_t)datalen);
917
buffer[datalen] = '\0';
918
}
919
}
920
921
922
/*
923
* 'do_monitor_printer_state()' - Do the MONITOR-PRINTER-STATE tests in the background.
924
*/
925
926
static void * // O - Thread exit status
927
do_monitor_printer_state(
928
ipptool_test_t *data) // I - Test data
929
{
930
int i, j; // Looping vars
931
char scheme[32], // URI scheme
932
userpass[32], // URI username:password
933
host[256], // URI hostname/IP address
934
resource[256]; // URI resource path
935
int port; // URI port number
936
http_encryption_t encryption; // Encryption to use
937
http_t *http; // Connection to printer
938
ipp_t *request, // IPP request
939
*response = NULL; // IPP response
940
http_status_t status; // Request status
941
ipp_attribute_t *found; // Found attribute
942
ipptool_expect_t *expect; // Current EXPECT test
943
char buffer[131072]; // Copy buffer
944
int num_pattrs; // Number of printer attributes
945
const char *pattrs[100]; // Printer attributes we care about
946
947
948
// Connect to the printer...
949
if (httpSeparateURI(HTTP_URI_CODING_ALL, data->monitor_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
950
{
951
print_fatal_error(data, "Bad printer URI \"%s\".", data->monitor_uri);
952
return (NULL);
953
}
954
955
if (!_cups_strcasecmp(scheme, "https") || !_cups_strcasecmp(scheme, "ipps") || port == 443)
956
encryption = HTTP_ENCRYPTION_ALWAYS;
957
else
958
encryption = data->encryption;
959
960
if ((http = httpConnect2(host, port, NULL, data->family, encryption, 1, 30000, NULL)) == NULL)
961
{
962
print_fatal_error(data, "Unable to connect to \"%s\" on port %d - %s", host, port, cupsLastErrorString());
963
return (0);
964
}
965
966
#ifdef HAVE_LIBZ
967
httpSetDefaultField(http, HTTP_FIELD_ACCEPT_ENCODING, "deflate, gzip, identity");
968
#else
969
httpSetDefaultField(http, HTTP_FIELD_ACCEPT_ENCODING, "identity");
970
#endif /* HAVE_LIBZ */
971
972
if (data->timeout > 0.0)
973
httpSetTimeout(http, data->timeout, timeout_cb, NULL);
974
975
// Wait for the initial delay as needed...
976
if (data->monitor_delay)
977
usleep(data->monitor_delay);
978
979
// Create a query request that we'll reuse...
980
request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
981
ippSetRequestId(request, data->request_id * 100 - 1);
982
ippSetVersion(request, data->version / 10, data->version % 10);
983
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, data->monitor_uri);
984
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
985
986
for (i = data->num_monitor_expects, expect = data->monitor_expects, num_pattrs = 0; i > 0; i --, expect ++)
987
{
988
// Add EXPECT attribute names...
989
for (j = 0; j < num_pattrs; j ++)
990
{
991
if (!strcmp(expect->name, pattrs[j]))
992
break;
993
}
994
995
if (j >= num_pattrs && num_pattrs < (int)(sizeof(pattrs) / sizeof(pattrs[0])))
996
pattrs[num_pattrs ++] = expect->name;
997
}
998
999
if (num_pattrs > 0)
1000
ippAddStrings(request, IPP_TAG_OPERATION, IPP_CONST_TAG(IPP_TAG_KEYWORD), "requested-attributes", num_pattrs, NULL, pattrs);
1001
1002
// Loop until we need to stop...
1003
while (!data->monitor_done && !Cancel)
1004
{
1005
// Poll the printer state...
1006
ippSetRequestId(request, ippGetRequestId(request) + 1);
1007
1008
if ((status = cupsSendRequest(http, request, resource, ippLength(request))) != HTTP_STATUS_ERROR)
1009
{
1010
response = cupsGetResponse(http, resource);
1011
status = httpGetStatus(http);
1012
}
1013
1014
if (!data->monitor_done && !Cancel && status == HTTP_STATUS_ERROR && httpError(data->http) != EINVAL &&
1015
#ifdef _WIN32
1016
httpError(data->http) != WSAETIMEDOUT)
1017
#else
1018
httpError(data->http) != ETIMEDOUT)
1019
#endif // _WIN32
1020
{
1021
if (httpReconnect2(http, 30000, NULL))
1022
break;
1023
}
1024
else if (status == HTTP_STATUS_ERROR || status == HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED)
1025
{
1026
break;
1027
}
1028
else if (status != HTTP_STATUS_OK)
1029
{
1030
httpFlush(http);
1031
1032
if (status == HTTP_STATUS_UNAUTHORIZED)
1033
continue;
1034
1035
break;
1036
}
1037
1038
for (i = data->num_monitor_expects, expect = data->monitor_expects; i > 0; i --, expect ++)
1039
{
1040
if (expect->if_defined && !_ippVarsGet(data->vars, expect->if_defined))
1041
continue;
1042
1043
if (expect->if_not_defined && _ippVarsGet(data->vars, expect->if_not_defined))
1044
continue;
1045
1046
found = ippFindAttribute(response, expect->name, IPP_TAG_ZERO);
1047
1048
if ((found && expect->not_expect) ||
1049
(!found && !(expect->not_expect || expect->optional)) ||
1050
(found && !expect_matches(expect, found)) ||
1051
(expect->in_group && ippGetGroupTag(found) != expect->in_group) ||
1052
(expect->with_distinct && !with_distinct_values(NULL, found)))
1053
{
1054
if (expect->define_no_match)
1055
{
1056
_ippVarsSet(data->vars, expect->define_no_match, "1");
1057
data->monitor_done = 1;
1058
}
1059
break;
1060
}
1061
1062
if (found)
1063
ippAttributeString(found, buffer, sizeof(buffer));
1064
1065
if (found && !with_value(data, NULL, expect->with_value, expect->with_flags, found, buffer, sizeof(buffer)))
1066
{
1067
if (expect->define_no_match)
1068
{
1069
_ippVarsSet(data->vars, expect->define_no_match, "1");
1070
data->monitor_done = 1;
1071
}
1072
break;
1073
}
1074
1075
if (found && expect->count > 0 && ippGetCount(found) != expect->count)
1076
{
1077
if (expect->define_no_match)
1078
{
1079
_ippVarsSet(data->vars, expect->define_no_match, "1");
1080
data->monitor_done = 1;
1081
}
1082
break;
1083
}
1084
1085
if (found && expect->display_match && (data->output == IPPTOOL_OUTPUT_TEST || (data->output == IPPTOOL_OUTPUT_PLIST && data->outfile != cupsFileStdout())))
1086
cupsFilePrintf(cupsFileStdout(), "CONT]\n\n%s\n\n %-68.68s [", expect->display_match, data->name);
1087
1088
if (found && expect->define_match)
1089
{
1090
_ippVarsSet(data->vars, expect->define_match, "1");
1091
data->monitor_done = 1;
1092
}
1093
1094
if (found && expect->define_value)
1095
{
1096
if (!expect->with_value)
1097
{
1098
int last = ippGetCount(found) - 1;
1099
// Last element in attribute
1100
1101
switch (ippGetValueTag(found))
1102
{
1103
case IPP_TAG_ENUM :
1104
case IPP_TAG_INTEGER :
1105
snprintf(buffer, sizeof(buffer), "%d", ippGetInteger(found, last));
1106
break;
1107
1108
case IPP_TAG_BOOLEAN :
1109
if (ippGetBoolean(found, last))
1110
strlcpy(buffer, "true", sizeof(buffer));
1111
else
1112
strlcpy(buffer, "false", sizeof(buffer));
1113
break;
1114
1115
case IPP_TAG_CHARSET :
1116
case IPP_TAG_KEYWORD :
1117
case IPP_TAG_LANGUAGE :
1118
case IPP_TAG_MIMETYPE :
1119
case IPP_TAG_NAME :
1120
case IPP_TAG_NAMELANG :
1121
case IPP_TAG_TEXT :
1122
case IPP_TAG_TEXTLANG :
1123
case IPP_TAG_URI :
1124
case IPP_TAG_URISCHEME :
1125
strlcpy(buffer, ippGetString(found, last, NULL), sizeof(buffer));
1126
break;
1127
1128
default :
1129
ippAttributeString(found, buffer, sizeof(buffer));
1130
break;
1131
}
1132
}
1133
1134
_ippVarsSet(data->vars, expect->define_value, buffer);
1135
data->monitor_done = 1;
1136
}
1137
}
1138
1139
if (i == 0)
1140
data->monitor_done = 1; // All tests passed
1141
1142
ippDelete(response);
1143
response = NULL;
1144
1145
// Sleep between requests...
1146
if (data->monitor_done || Cancel)
1147
break;
1148
1149
usleep(data->monitor_interval);
1150
}
1151
1152
// Close the connection to the printer and return...
1153
httpClose(http);
1154
ippDelete(request);
1155
ippDelete(response);
1156
1157
return (NULL);
1158
}
1159
1160
1161
/*
1162
* 'do_test()' - Do a single test from the test file.
1163
*/
1164
1165
static int /* O - 1 on success, 0 on failure */
1166
do_test(_ipp_file_t *f, /* I - IPP data file */
1167
ipptool_test_t *data) /* I - Test data */
1168
1169
{
1170
int i, /* Looping var */
1171
status_ok, /* Did we get a matching status? */
1172
repeat_count = 0, /* Repeat count */
1173
repeat_test; /* Repeat the test? */
1174
ipptool_expect_t *expect; /* Current expected attribute */
1175
ipp_t *request, /* IPP request */
1176
*response; /* IPP response */
1177
size_t length; /* Length of IPP request */
1178
http_status_t status; /* HTTP status */
1179
cups_array_t *a; /* Duplicate attribute array */
1180
ipp_tag_t group; /* Current group */
1181
ipp_attribute_t *attrptr, /* Attribute pointer */
1182
*found; /* Found attribute */
1183
char temp[1024]; /* Temporary string */
1184
cups_file_t *reqfile; /* File to send */
1185
ssize_t bytes; /* Bytes read/written */
1186
char buffer[1024 * 1024]; /* Copy buffer */
1187
size_t widths[200]; /* Width of columns */
1188
const char *error; /* Current error */
1189
1190
1191
if (Cancel)
1192
return (0);
1193
1194
/*
1195
* Show any PAUSE message, as needed...
1196
*/
1197
1198
if (data->pause[0])
1199
{
1200
if (!data->skip_test && !data->pass_test)
1201
pause_message(data->pause);
1202
1203
data->pause[0] = '\0';
1204
}
1205
1206
/*
1207
* Start the background thread as needed...
1208
*/
1209
1210
if (data->monitor_uri)
1211
{
1212
data->monitor_done = 0;
1213
data->monitor_thread = _cupsThreadCreate((_cups_thread_func_t)do_monitor_printer_state, data);
1214
}
1215
1216
/*
1217
* Take over control of the attributes in the request...
1218
*/
1219
1220
request = f->attrs;
1221
f->attrs = NULL;
1222
1223
/*
1224
* Submit the IPP request...
1225
*/
1226
1227
data->test_count ++;
1228
1229
ippSetVersion(request, data->version / 10, data->version % 10);
1230
ippSetRequestId(request, data->request_id);
1231
1232
if (data->output == IPPTOOL_OUTPUT_PLIST)
1233
{
1234
cupsFilePuts(data->outfile, "<dict>\n");
1235
cupsFilePuts(data->outfile, "<key>Name</key>\n");
1236
print_xml_string(data->outfile, "string", data->name);
1237
if (data->file_id[0])
1238
{
1239
cupsFilePuts(data->outfile, "<key>FileId</key>\n");
1240
print_xml_string(data->outfile, "string", data->file_id);
1241
}
1242
if (data->test_id[0])
1243
{
1244
cupsFilePuts(data->outfile, "<key>TestId</key>\n");
1245
print_xml_string(data->outfile, "string", data->test_id);
1246
}
1247
cupsFilePuts(data->outfile, "<key>Version</key>\n");
1248
cupsFilePrintf(data->outfile, "<string>%d.%d</string>\n", data->version / 10, data->version % 10);
1249
cupsFilePuts(data->outfile, "<key>Operation</key>\n");
1250
print_xml_string(data->outfile, "string", ippOpString(ippGetOperation(request)));
1251
cupsFilePuts(data->outfile, "<key>RequestId</key>\n");
1252
cupsFilePrintf(data->outfile, "<integer>%d</integer>\n", data->request_id);
1253
cupsFilePuts(data->outfile, "<key>RequestAttributes</key>\n");
1254
cupsFilePuts(data->outfile, "<array>\n");
1255
if (ippFirstAttribute(request))
1256
{
1257
cupsFilePuts(data->outfile, "<dict>\n");
1258
for (attrptr = ippFirstAttribute(request), group = ippGetGroupTag(attrptr); attrptr; attrptr = ippNextAttribute(request))
1259
print_attr(data->outfile, data->output, attrptr, &group);
1260
cupsFilePuts(data->outfile, "</dict>\n");
1261
}
1262
cupsFilePuts(data->outfile, "</array>\n");
1263
}
1264
1265
if (data->output == IPPTOOL_OUTPUT_TEST || (data->output == IPPTOOL_OUTPUT_PLIST && data->outfile != cupsFileStdout()))
1266
{
1267
if (data->verbosity)
1268
{
1269
cupsFilePrintf(cupsFileStdout(), " %s:\n", ippOpString(ippGetOperation(request)));
1270
1271
for (attrptr = ippFirstAttribute(request); attrptr; attrptr = ippNextAttribute(request))
1272
print_attr(cupsFileStdout(), IPPTOOL_OUTPUT_TEST, attrptr, NULL);
1273
}
1274
1275
cupsFilePrintf(cupsFileStdout(), " %-68.68s [", data->name);
1276
}
1277
1278
if ((data->skip_previous && !data->prev_pass) || data->skip_test || data->pass_test)
1279
{
1280
if (!data->pass_test)
1281
data->skip_count ++;
1282
1283
ippDelete(request);
1284
request = NULL;
1285
response = NULL;
1286
1287
if (data->output == IPPTOOL_OUTPUT_PLIST)
1288
{
1289
cupsFilePuts(data->outfile, "<key>Successful</key>\n");
1290
cupsFilePuts(data->outfile, "<true />\n");
1291
cupsFilePuts(data->outfile, "<key>Skipped</key>\n");
1292
if (data->pass_test)
1293
cupsFilePuts(data->outfile, "<false />\n");
1294
else
1295
cupsFilePuts(data->outfile, "<true />\n");
1296
cupsFilePuts(data->outfile, "<key>StatusCode</key>\n");
1297
if (data->pass_test)
1298
print_xml_string(data->outfile, "string", "pass");
1299
else
1300
print_xml_string(data->outfile, "string", "skip");
1301
cupsFilePuts(data->outfile, "<key>ResponseAttributes</key>\n");
1302
cupsFilePuts(data->outfile, "<dict />\n");
1303
}
1304
1305
if (data->output == IPPTOOL_OUTPUT_TEST || (data->output == IPPTOOL_OUTPUT_PLIST && data->outfile != cupsFileStdout()))
1306
{
1307
if (data->pass_test)
1308
cupsFilePuts(cupsFileStdout(), "PASS]\n");
1309
else
1310
cupsFilePuts(cupsFileStdout(), "SKIP]\n");
1311
}
1312
1313
goto skip_error;
1314
}
1315
1316
data->vars->password_tries = 0;
1317
1318
do
1319
{
1320
if (data->delay > 0)
1321
usleep(data->delay);
1322
1323
data->delay = data->repeat_interval;
1324
repeat_count ++;
1325
1326
status = HTTP_STATUS_OK;
1327
1328
if (data->transfer == IPPTOOL_TRANSFER_CHUNKED || (data->transfer == IPPTOOL_TRANSFER_AUTO && data->file[0]))
1329
{
1330
/*
1331
* Send request using chunking - a 0 length means "chunk".
1332
*/
1333
1334
length = 0;
1335
}
1336
else
1337
{
1338
/*
1339
* Send request using content length...
1340
*/
1341
1342
length = ippLength(request);
1343
1344
if (data->file[0] && (reqfile = cupsFileOpen(data->file, "r")) != NULL)
1345
{
1346
/*
1347
* Read the file to get the uncompressed file size...
1348
*/
1349
1350
while ((bytes = cupsFileRead(reqfile, buffer, sizeof(buffer))) > 0)
1351
length += (size_t)bytes;
1352
1353
cupsFileClose(reqfile);
1354
}
1355
}
1356
1357
/*
1358
* Send the request...
1359
*/
1360
1361
data->prev_pass = 1;
1362
repeat_test = 0;
1363
response = NULL;
1364
1365
if (status != HTTP_STATUS_ERROR)
1366
{
1367
while (!response && !Cancel && data->prev_pass)
1368
{
1369
ippSetRequestId(request, ++ data->request_id);
1370
1371
status = cupsSendRequest(data->http, request, data->resource, length);
1372
1373
#ifdef HAVE_LIBZ
1374
if (data->compression[0])
1375
httpSetField(data->http, HTTP_FIELD_CONTENT_ENCODING, data->compression);
1376
#endif /* HAVE_LIBZ */
1377
1378
if (!Cancel && status == HTTP_STATUS_CONTINUE && ippGetState(request) == IPP_DATA && data->file[0])
1379
{
1380
if ((reqfile = cupsFileOpen(data->file, "r")) != NULL)
1381
{
1382
while (!Cancel && (bytes = cupsFileRead(reqfile, buffer, sizeof(buffer))) > 0)
1383
{
1384
if ((status = cupsWriteRequestData(data->http, buffer, (size_t)bytes)) != HTTP_STATUS_CONTINUE)
1385
break;
1386
}
1387
1388
cupsFileClose(reqfile);
1389
}
1390
else
1391
{
1392
snprintf(buffer, sizeof(buffer), "%s: %s", data->file, strerror(errno));
1393
_cupsSetError(IPP_INTERNAL_ERROR, buffer, 0);
1394
1395
status = HTTP_STATUS_ERROR;
1396
}
1397
}
1398
1399
/*
1400
* Get the server's response...
1401
*/
1402
1403
if (!Cancel && status != HTTP_STATUS_ERROR)
1404
{
1405
response = cupsGetResponse(data->http, data->resource);
1406
status = httpGetStatus(data->http);
1407
}
1408
1409
if (!Cancel && status == HTTP_STATUS_ERROR && httpError(data->http) != EINVAL &&
1410
#ifdef _WIN32
1411
httpError(data->http) != WSAETIMEDOUT)
1412
#else
1413
httpError(data->http) != ETIMEDOUT)
1414
#endif /* _WIN32 */
1415
{
1416
if (httpReconnect2(data->http, 30000, NULL))
1417
data->prev_pass = 0;
1418
}
1419
else if (status == HTTP_STATUS_ERROR || status == HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED)
1420
{
1421
data->prev_pass = 0;
1422
break;
1423
}
1424
else if (status != HTTP_STATUS_OK)
1425
{
1426
httpFlush(data->http);
1427
1428
if (status == HTTP_STATUS_UNAUTHORIZED)
1429
continue;
1430
1431
break;
1432
}
1433
}
1434
}
1435
1436
if (!Cancel && status == HTTP_STATUS_ERROR && httpError(data->http) != EINVAL &&
1437
#ifdef _WIN32
1438
httpError(data->http) != WSAETIMEDOUT)
1439
#else
1440
httpError(data->http) != ETIMEDOUT)
1441
#endif /* _WIN32 */
1442
{
1443
if (httpReconnect2(data->http, 30000, NULL))
1444
data->prev_pass = 0;
1445
}
1446
else if (status == HTTP_STATUS_ERROR)
1447
{
1448
if (!Cancel)
1449
httpReconnect2(data->http, 30000, NULL);
1450
1451
data->prev_pass = 0;
1452
}
1453
else if (status != HTTP_STATUS_OK)
1454
{
1455
httpFlush(data->http);
1456
data->prev_pass = 0;
1457
}
1458
1459
/*
1460
* Check results of request...
1461
*/
1462
1463
cupsArrayClear(data->errors);
1464
1465
if (httpGetVersion(data->http) != HTTP_1_1)
1466
{
1467
int version = (int)httpGetVersion(data->http);
1468
1469
add_stringf(data->errors, "Bad HTTP version (%d.%d)", version / 100, version % 100);
1470
}
1471
1472
if (data->validate_headers)
1473
{
1474
const char *header; /* HTTP header value */
1475
1476
if ((header = httpGetField(data->http, HTTP_FIELD_CONTENT_TYPE)) == NULL || _cups_strcasecmp(header, "application/ipp"))
1477
add_stringf(data->errors, "Bad HTTP Content-Type in response (%s)", header && *header ? header : "<missing>");
1478
1479
if ((header = httpGetField(data->http, HTTP_FIELD_DATE)) != NULL && *header && httpGetDateTime(header) == 0)
1480
add_stringf(data->errors, "Bad HTTP Date in response (%s)", header);
1481
}
1482
1483
if (!response)
1484
{
1485
/*
1486
* No response, log error...
1487
*/
1488
1489
add_stringf(data->errors, "IPP request failed with status %s (%s)", ippErrorString(cupsLastError()), cupsLastErrorString());
1490
}
1491
else
1492
{
1493
/*
1494
* Collect common attribute values...
1495
*/
1496
1497
if ((attrptr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) != NULL)
1498
{
1499
snprintf(temp, sizeof(temp), "%d", ippGetInteger(attrptr, 0));
1500
_ippVarsSet(data->vars, "job-id", temp);
1501
}
1502
1503
if ((attrptr = ippFindAttribute(response, "job-uri", IPP_TAG_URI)) != NULL)
1504
_ippVarsSet(data->vars, "job-uri", ippGetString(attrptr, 0, NULL));
1505
1506
if ((attrptr = ippFindAttribute(response, "notify-subscription-id", IPP_TAG_INTEGER)) != NULL)
1507
{
1508
snprintf(temp, sizeof(temp), "%d", ippGetInteger(attrptr, 0));
1509
_ippVarsSet(data->vars, "notify-subscription-id", temp);
1510
}
1511
1512
/*
1513
* Check response, validating groups and attributes and logging errors
1514
* as needed...
1515
*/
1516
1517
if (ippGetState(response) != IPP_DATA)
1518
add_stringf(data->errors, "Missing end-of-attributes-tag in response (RFC 2910 section 3.5.1)");
1519
1520
if (data->version)
1521
{
1522
int major, minor; /* IPP version */
1523
1524
major = ippGetVersion(response, &minor);
1525
1526
if (major != (data->version / 10) || minor != (data->version % 10))
1527
add_stringf(data->errors, "Bad version %d.%d in response - expected %d.%d (RFC 8011 section 4.1.8).", major, minor, data->version / 10, data->version % 10);
1528
}
1529
1530
if (ippGetRequestId(response) != data->request_id)
1531
add_stringf(data->errors, "Bad request ID %d in response - expected %d (RFC 8011 section 4.1.1)", ippGetRequestId(response), data->request_id);
1532
1533
attrptr = ippFirstAttribute(response);
1534
if (!attrptr)
1535
{
1536
add_stringf(data->errors, "Missing first attribute \"attributes-charset (charset)\" in group operation-attributes-tag (RFC 8011 section 4.1.4).");
1537
}
1538
else
1539
{
1540
if (!ippGetName(attrptr) || ippGetValueTag(attrptr) != IPP_TAG_CHARSET || ippGetGroupTag(attrptr) != IPP_TAG_OPERATION || ippGetCount(attrptr) != 1 ||strcmp(ippGetName(attrptr), "attributes-charset"))
1541
add_stringf(data->errors, "Bad first attribute \"%s (%s%s)\" in group %s, expected \"attributes-charset (charset)\" in group operation-attributes-tag (RFC 8011 section 4.1.4).", ippGetName(attrptr) ? ippGetName(attrptr) : "(null)", ippGetCount(attrptr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attrptr)), ippTagString(ippGetGroupTag(attrptr)));
1542
1543
attrptr = ippNextAttribute(response);
1544
if (!attrptr)
1545
add_stringf(data->errors, "Missing second attribute \"attributes-natural-language (naturalLanguage)\" in group operation-attributes-tag (RFC 8011 section 4.1.4).");
1546
else if (!ippGetName(attrptr) || ippGetValueTag(attrptr) != IPP_TAG_LANGUAGE || ippGetGroupTag(attrptr) != IPP_TAG_OPERATION || ippGetCount(attrptr) != 1 || strcmp(ippGetName(attrptr), "attributes-natural-language"))
1547
add_stringf(data->errors, "Bad first attribute \"%s (%s%s)\" in group %s, expected \"attributes-natural-language (naturalLanguage)\" in group operation-attributes-tag (RFC 8011 section 4.1.4).", ippGetName(attrptr) ? ippGetName(attrptr) : "(null)", ippGetCount(attrptr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attrptr)), ippTagString(ippGetGroupTag(attrptr)));
1548
}
1549
1550
if ((attrptr = ippFindAttribute(response, "status-message", IPP_TAG_ZERO)) != NULL)
1551
{
1552
const char *status_message = ippGetString(attrptr, 0, NULL);
1553
/* String value */
1554
1555
if (ippGetValueTag(attrptr) != IPP_TAG_TEXT)
1556
add_stringf(data->errors, "status-message (text(255)) has wrong value tag %s (RFC 8011 section 4.1.6.2).", ippTagString(ippGetValueTag(attrptr)));
1557
if (ippGetGroupTag(attrptr) != IPP_TAG_OPERATION)
1558
add_stringf(data->errors, "status-message (text(255)) has wrong group tag %s (RFC 8011 section 4.1.6.2).", ippTagString(ippGetGroupTag(attrptr)));
1559
if (ippGetCount(attrptr) != 1)
1560
add_stringf(data->errors, "status-message (text(255)) has %d values (RFC 8011 section 4.1.6.2).", ippGetCount(attrptr));
1561
if (status_message && strlen(status_message) > 255)
1562
add_stringf(data->errors, "status-message (text(255)) has bad length %d (RFC 8011 section 4.1.6.2).", (int)strlen(status_message));
1563
}
1564
1565
if ((attrptr = ippFindAttribute(response, "detailed-status-message",
1566
IPP_TAG_ZERO)) != NULL)
1567
{
1568
const char *detailed_status_message = ippGetString(attrptr, 0, NULL);
1569
/* String value */
1570
1571
if (ippGetValueTag(attrptr) != IPP_TAG_TEXT)
1572
add_stringf(data->errors, "detailed-status-message (text(MAX)) has wrong value tag %s (RFC 8011 section 4.1.6.3).", ippTagString(ippGetValueTag(attrptr)));
1573
if (ippGetGroupTag(attrptr) != IPP_TAG_OPERATION)
1574
add_stringf(data->errors, "detailed-status-message (text(MAX)) has wrong group tag %s (RFC 8011 section 4.1.6.3).", ippTagString(ippGetGroupTag(attrptr)));
1575
if (ippGetCount(attrptr) != 1)
1576
add_stringf(data->errors, "detailed-status-message (text(MAX)) has %d values (RFC 8011 section 4.1.6.3).", ippGetCount(attrptr));
1577
if (detailed_status_message && strlen(detailed_status_message) > 1023)
1578
add_stringf(data->errors, "detailed-status-message (text(MAX)) has bad length %d (RFC 8011 section 4.1.6.3).", (int)strlen(detailed_status_message));
1579
}
1580
1581
a = cupsArrayNew((cups_array_func_t)strcmp, NULL);
1582
1583
for (attrptr = ippFirstAttribute(response), group = ippGetGroupTag(attrptr);
1584
attrptr;
1585
attrptr = ippNextAttribute(response))
1586
{
1587
if (ippGetGroupTag(attrptr) != group)
1588
{
1589
int out_of_order = 0; /* Are attribute groups out-of-order? */
1590
cupsArrayClear(a);
1591
1592
switch (ippGetGroupTag(attrptr))
1593
{
1594
case IPP_TAG_ZERO :
1595
break;
1596
1597
case IPP_TAG_OPERATION :
1598
out_of_order = 1;
1599
break;
1600
1601
case IPP_TAG_UNSUPPORTED_GROUP :
1602
if (group != IPP_TAG_OPERATION)
1603
out_of_order = 1;
1604
break;
1605
1606
case IPP_TAG_JOB :
1607
case IPP_TAG_PRINTER :
1608
if (group != IPP_TAG_OPERATION && group != IPP_TAG_UNSUPPORTED_GROUP)
1609
out_of_order = 1;
1610
break;
1611
1612
case IPP_TAG_SUBSCRIPTION :
1613
if (group > ippGetGroupTag(attrptr) && group != IPP_TAG_DOCUMENT)
1614
out_of_order = 1;
1615
break;
1616
1617
default :
1618
if (group > ippGetGroupTag(attrptr))
1619
out_of_order = 1;
1620
break;
1621
}
1622
1623
if (out_of_order)
1624
add_stringf(data->errors, "Attribute groups out of order (%s < %s)", ippTagString(ippGetGroupTag(attrptr)), ippTagString(group));
1625
1626
if (ippGetGroupTag(attrptr) != IPP_TAG_ZERO)
1627
group = ippGetGroupTag(attrptr);
1628
}
1629
1630
if (!ippValidateAttribute(attrptr))
1631
cupsArrayAdd(data->errors, (void *)cupsLastErrorString());
1632
1633
if (ippGetName(attrptr))
1634
{
1635
if (cupsArrayFind(a, (void *)ippGetName(attrptr)) && data->output < IPPTOOL_OUTPUT_LIST)
1636
add_stringf(data->errors, "Duplicate \"%s\" attribute in %s group", ippGetName(attrptr), ippTagString(group));
1637
1638
cupsArrayAdd(a, (void *)ippGetName(attrptr));
1639
}
1640
}
1641
1642
cupsArrayDelete(a);
1643
1644
/*
1645
* Now check the test-defined expected status-code and attribute
1646
* values...
1647
*/
1648
1649
if (ippGetStatusCode(response) == IPP_STATUS_ERROR_BUSY && data->repeat_on_busy)
1650
{
1651
// Repeat on a server-error-busy status code...
1652
status_ok = 1;
1653
repeat_test = 1;
1654
}
1655
else
1656
{
1657
for (i = 0, status_ok = 0; i < data->num_statuses; i ++)
1658
{
1659
if (data->statuses[i].if_defined &&
1660
!_ippVarsGet(data->vars, data->statuses[i].if_defined))
1661
continue;
1662
1663
if (data->statuses[i].if_not_defined &&
1664
_ippVarsGet(data->vars, data->statuses[i].if_not_defined))
1665
continue;
1666
1667
if (ippGetStatusCode(response) == data->statuses[i].status)
1668
{
1669
status_ok = 1;
1670
1671
if (data->statuses[i].repeat_match && repeat_count < data->statuses[i].repeat_limit)
1672
repeat_test = 1;
1673
1674
if (data->statuses[i].define_match)
1675
_ippVarsSet(data->vars, data->statuses[i].define_match, "1");
1676
}
1677
else
1678
{
1679
if (data->statuses[i].repeat_no_match && repeat_count < data->statuses[i].repeat_limit)
1680
repeat_test = 1;
1681
1682
if (data->statuses[i].define_no_match)
1683
{
1684
_ippVarsSet(data->vars, data->statuses[i].define_no_match, "1");
1685
status_ok = 1;
1686
}
1687
}
1688
}
1689
}
1690
1691
if (!status_ok && data->num_statuses > 0)
1692
{
1693
for (i = 0; i < data->num_statuses; i ++)
1694
{
1695
if (data->statuses[i].if_defined &&
1696
!_ippVarsGet(data->vars, data->statuses[i].if_defined))
1697
continue;
1698
1699
if (data->statuses[i].if_not_defined &&
1700
_ippVarsGet(data->vars, data->statuses[i].if_not_defined))
1701
continue;
1702
1703
if (!data->statuses[i].repeat_match || repeat_count >= data->statuses[i].repeat_limit)
1704
add_stringf(data->errors, "EXPECTED: STATUS %s (got %s)", ippErrorString(data->statuses[i].status), ippErrorString(cupsLastError()));
1705
}
1706
1707
if ((attrptr = ippFindAttribute(response, "status-message", IPP_TAG_TEXT)) != NULL)
1708
add_stringf(data->errors, "status-message=\"%s\"", ippGetString(attrptr, 0, NULL));
1709
}
1710
1711
for (i = data->num_expects, expect = data->expects; i > 0; i --, expect ++)
1712
{
1713
ipp_attribute_t *group_found; /* Found parent attribute for group tests */
1714
1715
if (expect->if_defined && !_ippVarsGet(data->vars, expect->if_defined))
1716
continue;
1717
1718
if (expect->if_not_defined &&
1719
_ippVarsGet(data->vars, expect->if_not_defined))
1720
continue;
1721
1722
if ((found = ippFindAttribute(response, expect->name, IPP_TAG_ZERO)) != NULL && expect->in_group && expect->in_group != ippGetGroupTag(found))
1723
{
1724
while ((found = ippFindNextAttribute(response, expect->name, IPP_TAG_ZERO)) != NULL)
1725
if (expect->in_group == ippGetGroupTag(found))
1726
break;
1727
}
1728
1729
do
1730
{
1731
group_found = found;
1732
1733
if (expect->in_group && strchr(expect->name, '/'))
1734
{
1735
char group_name[256],/* Parent attribute name */
1736
*group_ptr; /* Pointer into parent attribute name */
1737
1738
strlcpy(group_name, expect->name, sizeof(group_name));
1739
if ((group_ptr = strchr(group_name, '/')) != NULL)
1740
*group_ptr = '\0';
1741
1742
group_found = ippFindAttribute(response, group_name, IPP_TAG_ZERO);
1743
}
1744
1745
if ((found && expect->not_expect) ||
1746
(!found && !(expect->not_expect || expect->optional)) ||
1747
(found && !expect_matches(expect, found)) ||
1748
(group_found && expect->in_group && ippGetGroupTag(group_found) != expect->in_group) ||
1749
(expect->with_distinct && !with_distinct_values(NULL, found)))
1750
{
1751
if (expect->define_no_match)
1752
_ippVarsSet(data->vars, expect->define_no_match, "1");
1753
else if (!expect->define_match && !expect->define_value)
1754
{
1755
if (found && expect->not_expect && !expect->with_value && !expect->with_value_from)
1756
add_stringf(data->errors, "NOT EXPECTED: %s", expect->name);
1757
else if (!found && !(expect->not_expect || expect->optional))
1758
add_stringf(data->errors, "EXPECTED: %s", expect->name);
1759
else if (found)
1760
{
1761
if (!expect_matches(expect, found))
1762
add_stringf(data->errors, "EXPECTED: %s OF-TYPE %s (got %s)",
1763
expect->name, expect->of_type,
1764
ippTagString(ippGetValueTag(found)));
1765
1766
if (expect->in_group && ippGetGroupTag(group_found) != expect->in_group)
1767
add_stringf(data->errors, "EXPECTED: %s IN-GROUP %s (got %s).",
1768
expect->name, ippTagString(expect->in_group),
1769
ippTagString(ippGetGroupTag(group_found)));
1770
1771
if (expect->with_distinct)
1772
with_distinct_values(data->errors, found);
1773
}
1774
}
1775
1776
if (expect->repeat_no_match && repeat_count < expect->repeat_limit)
1777
repeat_test = 1;
1778
break;
1779
}
1780
1781
if (found)
1782
ippAttributeString(found, buffer, sizeof(buffer));
1783
1784
if (found && expect->with_value_from && !with_value_from(NULL, ippFindAttribute(response, expect->with_value_from, IPP_TAG_ZERO), found, buffer, sizeof(buffer)))
1785
{
1786
if (expect->define_no_match)
1787
_ippVarsSet(data->vars, expect->define_no_match, "1");
1788
else if (!expect->define_match && !expect->define_value && ((!expect->repeat_match && !expect->repeat_no_match) || repeat_count >= expect->repeat_limit))
1789
{
1790
add_stringf(data->errors, "EXPECTED: %s WITH-VALUES-FROM %s", expect->name, expect->with_value_from);
1791
1792
with_value_from(data->errors, ippFindAttribute(response, expect->with_value_from, IPP_TAG_ZERO), found, buffer, sizeof(buffer));
1793
}
1794
1795
if (expect->repeat_no_match && repeat_count < expect->repeat_limit)
1796
repeat_test = 1;
1797
1798
break;
1799
}
1800
else if (found && !with_value(data, NULL, expect->with_value, expect->with_flags, found, buffer, sizeof(buffer)))
1801
{
1802
if (expect->define_no_match)
1803
_ippVarsSet(data->vars, expect->define_no_match, "1");
1804
else if (!expect->define_match && !expect->define_value &&
1805
!expect->repeat_match && (!expect->repeat_no_match || repeat_count >= expect->repeat_limit))
1806
{
1807
if (expect->with_flags & IPPTOOL_WITH_REGEX)
1808
add_stringf(data->errors, "EXPECTED: %s %s /%s/", expect->name, with_flags_string(expect->with_flags), expect->with_value);
1809
else
1810
add_stringf(data->errors, "EXPECTED: %s %s \"%s\"", expect->name, with_flags_string(expect->with_flags), expect->with_value);
1811
1812
with_value(data, data->errors, expect->with_value, expect->with_flags, found, buffer, sizeof(buffer));
1813
}
1814
1815
if (expect->repeat_no_match &&
1816
repeat_count < expect->repeat_limit)
1817
repeat_test = 1;
1818
1819
break;
1820
}
1821
1822
if (found && expect->count > 0 && ippGetCount(found) != expect->count)
1823
{
1824
if (expect->define_no_match)
1825
_ippVarsSet(data->vars, expect->define_no_match, "1");
1826
else if (!expect->define_match && !expect->define_value)
1827
{
1828
add_stringf(data->errors, "EXPECTED: %s COUNT %d (got %d)", expect->name, expect->count, ippGetCount(found));
1829
}
1830
1831
if (expect->repeat_no_match &&
1832
repeat_count < expect->repeat_limit)
1833
repeat_test = 1;
1834
1835
break;
1836
}
1837
1838
if (found && expect->same_count_as)
1839
{
1840
attrptr = ippFindAttribute(response, expect->same_count_as,
1841
IPP_TAG_ZERO);
1842
1843
if (!attrptr || ippGetCount(attrptr) != ippGetCount(found))
1844
{
1845
if (expect->define_no_match)
1846
_ippVarsSet(data->vars, expect->define_no_match, "1");
1847
else if (!expect->define_match && !expect->define_value)
1848
{
1849
if (!attrptr)
1850
add_stringf(data->errors, "EXPECTED: %s (%d values) SAME-COUNT-AS %s (not returned)", expect->name, ippGetCount(found), expect->same_count_as);
1851
else if (ippGetCount(attrptr) != ippGetCount(found))
1852
add_stringf(data->errors, "EXPECTED: %s (%d values) SAME-COUNT-AS %s (%d values)", expect->name, ippGetCount(found), expect->same_count_as, ippGetCount(attrptr));
1853
}
1854
1855
if (expect->repeat_no_match &&
1856
repeat_count < expect->repeat_limit)
1857
repeat_test = 1;
1858
1859
break;
1860
}
1861
}
1862
1863
if (found && expect->display_match && (data->output == IPPTOOL_OUTPUT_TEST || (data->output == IPPTOOL_OUTPUT_PLIST && data->outfile != cupsFileStdout())))
1864
cupsFilePrintf(cupsFileStdout(), "\n%s\n\n", expect->display_match);
1865
1866
if (found && expect->define_match)
1867
_ippVarsSet(data->vars, expect->define_match, "1");
1868
1869
if (found && expect->define_value)
1870
{
1871
if (!expect->with_value)
1872
{
1873
int last = ippGetCount(found) - 1;
1874
/* Last element in attribute */
1875
1876
switch (ippGetValueTag(found))
1877
{
1878
case IPP_TAG_ENUM :
1879
case IPP_TAG_INTEGER :
1880
snprintf(buffer, sizeof(buffer), "%d", ippGetInteger(found, last));
1881
break;
1882
1883
case IPP_TAG_BOOLEAN :
1884
if (ippGetBoolean(found, last))
1885
strlcpy(buffer, "true", sizeof(buffer));
1886
else
1887
strlcpy(buffer, "false", sizeof(buffer));
1888
break;
1889
1890
case IPP_TAG_RESOLUTION :
1891
{
1892
int xres, /* Horizontal resolution */
1893
yres; /* Vertical resolution */
1894
ipp_res_t units; /* Resolution units */
1895
1896
xres = ippGetResolution(found, last, &yres, &units);
1897
1898
if (xres == yres)
1899
snprintf(buffer, sizeof(buffer), "%d%s", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
1900
else
1901
snprintf(buffer, sizeof(buffer), "%dx%d%s", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
1902
}
1903
break;
1904
1905
case IPP_TAG_CHARSET :
1906
case IPP_TAG_KEYWORD :
1907
case IPP_TAG_LANGUAGE :
1908
case IPP_TAG_MIMETYPE :
1909
case IPP_TAG_NAME :
1910
case IPP_TAG_NAMELANG :
1911
case IPP_TAG_TEXT :
1912
case IPP_TAG_TEXTLANG :
1913
case IPP_TAG_URI :
1914
case IPP_TAG_URISCHEME :
1915
strlcpy(buffer, ippGetString(found, last, NULL), sizeof(buffer));
1916
break;
1917
1918
default :
1919
ippAttributeString(found, buffer, sizeof(buffer));
1920
break;
1921
}
1922
}
1923
1924
_ippVarsSet(data->vars, expect->define_value, buffer);
1925
}
1926
1927
if (found && expect->repeat_match &&
1928
repeat_count < expect->repeat_limit)
1929
repeat_test = 1;
1930
}
1931
while (expect->expect_all && (found = ippFindNextAttribute(response, expect->name, IPP_TAG_ZERO)) != NULL);
1932
}
1933
}
1934
1935
/*
1936
* If we are going to repeat this test, display intermediate results...
1937
*/
1938
1939
if (repeat_test)
1940
{
1941
if (data->output == IPPTOOL_OUTPUT_TEST || (data->output == IPPTOOL_OUTPUT_PLIST && data->outfile != cupsFileStdout()))
1942
{
1943
cupsFilePrintf(cupsFileStdout(), "%04d]\n", repeat_count);
1944
\
1945
if (data->num_displayed > 0)
1946
{
1947
for (attrptr = ippFirstAttribute(response); attrptr; attrptr = ippNextAttribute(response))
1948
{
1949
const char *attrname = ippGetName(attrptr);
1950
if (attrname)
1951
{
1952
for (i = 0; i < data->num_displayed; i ++)
1953
{
1954
if (!strcmp(data->displayed[i], attrname))
1955
{
1956
print_attr(cupsFileStdout(), IPPTOOL_OUTPUT_TEST, attrptr, NULL);
1957
break;
1958
}
1959
}
1960
}
1961
}
1962
}
1963
}
1964
1965
if (data->output == IPPTOOL_OUTPUT_TEST || (data->output == IPPTOOL_OUTPUT_PLIST && data->outfile != cupsFileStdout()))
1966
{
1967
cupsFilePrintf(cupsFileStdout(), " %-68.68s [", data->name);
1968
}
1969
1970
ippDelete(response);
1971
response = NULL;
1972
}
1973
}
1974
while (repeat_test);
1975
1976
ippDelete(request);
1977
1978
request = NULL;
1979
1980
if (cupsArrayCount(data->errors) > 0)
1981
data->prev_pass = data->pass = 0;
1982
1983
if (data->prev_pass)
1984
data->pass_count ++;
1985
else
1986
data->fail_count ++;
1987
1988
if (data->output == IPPTOOL_OUTPUT_PLIST)
1989
{
1990
cupsFilePuts(data->outfile, "<key>Successful</key>\n");
1991
cupsFilePuts(data->outfile, data->prev_pass ? "<true />\n" : "<false />\n");
1992
cupsFilePuts(data->outfile, "<key>StatusCode</key>\n");
1993
print_xml_string(data->outfile, "string", ippErrorString(cupsLastError()));
1994
cupsFilePuts(data->outfile, "<key>ResponseAttributes</key>\n");
1995
cupsFilePuts(data->outfile, "<array>\n");
1996
cupsFilePuts(data->outfile, "<dict>\n");
1997
for (attrptr = ippFirstAttribute(response), group = ippGetGroupTag(attrptr);
1998
attrptr;
1999
attrptr = ippNextAttribute(response))
2000
print_attr(data->outfile, data->output, attrptr, &group);
2001
cupsFilePuts(data->outfile, "</dict>\n");
2002
cupsFilePuts(data->outfile, "</array>\n");
2003
}
2004
else if (data->output == IPPTOOL_OUTPUT_IPPSERVER && response)
2005
{
2006
for (attrptr = ippFirstAttribute(response); attrptr; attrptr = ippNextAttribute(response))
2007
{
2008
if (!ippGetName(attrptr) || ippGetGroupTag(attrptr) != IPP_TAG_PRINTER)
2009
continue;
2010
2011
print_ippserver_attr(data, attrptr, 0);
2012
}
2013
}
2014
else if (data->output == IPPTOOL_OUTPUT_JSON && response)
2015
{
2016
ipp_tag_t cur_tag = IPP_TAG_ZERO, /* Current group tag */
2017
group_tag; /* Attribute's group tag */
2018
2019
cupsFilePuts(data->outfile, "[\n");
2020
attrptr = ippFirstAttribute(response);
2021
while (attrptr)
2022
{
2023
group_tag = ippGetGroupTag(attrptr);
2024
2025
if (group_tag && ippGetName(attrptr))
2026
{
2027
if (group_tag != cur_tag)
2028
{
2029
if (cur_tag)
2030
cupsFilePuts(data->outfile, " },\n");
2031
2032
cupsFilePrintf(data->outfile, " {\n \"group-tag\": \"%s\",\n", ippTagString(group_tag));
2033
cur_tag = group_tag;
2034
}
2035
2036
print_json_attr(data, attrptr, 8);
2037
attrptr = ippNextAttribute(response);
2038
cupsFilePuts(data->outfile, ippGetName(attrptr) && ippGetGroupTag(attrptr) == cur_tag ? ",\n" : "\n");
2039
}
2040
else
2041
{
2042
attrptr = ippNextAttribute(response);
2043
}
2044
}
2045
2046
if (cur_tag)
2047
cupsFilePuts(data->outfile, " }\n");
2048
cupsFilePuts(data->outfile, "]\n");
2049
}
2050
2051
if (data->output == IPPTOOL_OUTPUT_TEST || (data->output == IPPTOOL_OUTPUT_PLIST && data->outfile != cupsFileStdout()))
2052
{
2053
cupsFilePuts(cupsFileStdout(), data->prev_pass ? "PASS]\n" : "FAIL]\n");
2054
2055
if (!data->prev_pass || (data->verbosity && response))
2056
{
2057
cupsFilePrintf(cupsFileStdout(), " RECEIVED: %lu bytes in response\n", (unsigned long)ippLength(response));
2058
cupsFilePrintf(cupsFileStdout(), " status-code = %s (%s)\n", ippErrorString(cupsLastError()), cupsLastErrorString());
2059
2060
if (data->verbosity && response)
2061
{
2062
for (attrptr = ippFirstAttribute(response); attrptr; attrptr = ippNextAttribute(response))
2063
print_attr(cupsFileStdout(), IPPTOOL_OUTPUT_TEST, attrptr, NULL);
2064
}
2065
}
2066
}
2067
else if (!data->prev_pass && data->output != IPPTOOL_OUTPUT_QUIET)
2068
fprintf(stderr, "%s\n", cupsLastErrorString());
2069
2070
if (data->prev_pass && data->output >= IPPTOOL_OUTPUT_LIST && !data->verbosity && data->num_displayed > 0)
2071
{
2072
size_t width; /* Length of value */
2073
2074
for (i = 0; i < data->num_displayed; i ++)
2075
{
2076
widths[i] = strlen(data->displayed[i]);
2077
2078
for (attrptr = ippFindAttribute(response, data->displayed[i], IPP_TAG_ZERO);
2079
attrptr;
2080
attrptr = ippFindNextAttribute(response, data->displayed[i], IPP_TAG_ZERO))
2081
{
2082
width = ippAttributeString(attrptr, NULL, 0);
2083
if (width > widths[i])
2084
widths[i] = width;
2085
}
2086
}
2087
2088
if (data->output == IPPTOOL_OUTPUT_CSV)
2089
print_csv(data, NULL, NULL, data->num_displayed, data->displayed, widths);
2090
else
2091
print_line(data, NULL, NULL, data->num_displayed, data->displayed, widths);
2092
2093
attrptr = ippFirstAttribute(response);
2094
2095
while (attrptr)
2096
{
2097
while (attrptr && ippGetGroupTag(attrptr) <= IPP_TAG_OPERATION)
2098
attrptr = ippNextAttribute(response);
2099
2100
if (attrptr)
2101
{
2102
if (data->output == IPPTOOL_OUTPUT_CSV)
2103
attrptr = print_csv(data, response, attrptr, data->num_displayed, data->displayed, widths);
2104
else
2105
attrptr = print_line(data, response, attrptr, data->num_displayed, data->displayed, widths);
2106
2107
while (attrptr && ippGetGroupTag(attrptr) > IPP_TAG_OPERATION)
2108
attrptr = ippNextAttribute(response);
2109
}
2110
}
2111
}
2112
else if (!data->prev_pass)
2113
{
2114
if (data->output == IPPTOOL_OUTPUT_PLIST)
2115
{
2116
cupsFilePuts(data->outfile, "<key>Errors</key>\n");
2117
cupsFilePuts(data->outfile, "<array>\n");
2118
2119
for (error = (char *)cupsArrayFirst(data->errors);
2120
error;
2121
error = (char *)cupsArrayNext(data->errors))
2122
print_xml_string(data->outfile, "string", error);
2123
2124
cupsFilePuts(data->outfile, "</array>\n");
2125
}
2126
2127
if (data->output == IPPTOOL_OUTPUT_TEST || (data->output == IPPTOOL_OUTPUT_PLIST && data->outfile != cupsFileStdout()))
2128
{
2129
for (error = (char *)cupsArrayFirst(data->errors);
2130
error;
2131
error = (char *)cupsArrayNext(data->errors))
2132
cupsFilePrintf(cupsFileStdout(), " %s\n", error);
2133
}
2134
}
2135
2136
if (data->num_displayed > 0 && !data->verbosity && response && (data->output == IPPTOOL_OUTPUT_TEST || (data->output == IPPTOOL_OUTPUT_PLIST && data->outfile != cupsFileStdout())))
2137
{
2138
for (attrptr = ippFirstAttribute(response); attrptr; attrptr = ippNextAttribute(response))
2139
{
2140
if (ippGetName(attrptr))
2141
{
2142
for (i = 0; i < data->num_displayed; i ++)
2143
{
2144
if (!strcmp(data->displayed[i], ippGetName(attrptr)))
2145
{
2146
print_attr(data->outfile, data->output, attrptr, NULL);
2147
break;
2148
}
2149
}
2150
}
2151
}
2152
}
2153
2154
skip_error:
2155
2156
if (data->monitor_thread)
2157
{
2158
data->monitor_done = 1;
2159
_cupsThreadWait(data->monitor_thread);
2160
}
2161
2162
if (data->output == IPPTOOL_OUTPUT_PLIST)
2163
cupsFilePuts(data->outfile, "</dict>\n");
2164
2165
ippDelete(response);
2166
response = NULL;
2167
2168
for (i = 0; i < data->num_statuses; i ++)
2169
{
2170
free(data->statuses[i].if_defined);
2171
free(data->statuses[i].if_not_defined);
2172
free(data->statuses[i].define_match);
2173
free(data->statuses[i].define_no_match);
2174
}
2175
data->num_statuses = 0;
2176
2177
for (i = data->num_expects, expect = data->expects; i > 0; i --, expect ++)
2178
{
2179
free(expect->name);
2180
free(expect->of_type);
2181
free(expect->same_count_as);
2182
free(expect->if_defined);
2183
free(expect->if_not_defined);
2184
free(expect->with_value);
2185
free(expect->define_match);
2186
free(expect->define_no_match);
2187
free(expect->define_value);
2188
free(expect->display_match);
2189
}
2190
data->num_expects = 0;
2191
2192
for (i = 0; i < data->num_displayed; i ++)
2193
free(data->displayed[i]);
2194
data->num_displayed = 0;
2195
2196
free(data->monitor_uri);
2197
data->monitor_uri = NULL;
2198
2199
for (i = data->num_monitor_expects, expect = data->monitor_expects; i > 0; i --, expect ++)
2200
{
2201
free(expect->name);
2202
free(expect->of_type);
2203
free(expect->same_count_as);
2204
free(expect->if_defined);
2205
free(expect->if_not_defined);
2206
free(expect->with_value);
2207
free(expect->define_match);
2208
free(expect->define_no_match);
2209
free(expect->define_value);
2210
free(expect->display_match);
2211
}
2212
data->num_monitor_expects = 0;
2213
2214
return (data->ignore_errors || data->prev_pass);
2215
}
2216
2217
2218
/*
2219
* 'do_tests()' - Do tests as specified in the test file.
2220
*/
2221
2222
static int /* O - 1 on success, 0 on failure */
2223
do_tests(const char *testfile, /* I - Test file to use */
2224
ipptool_test_t *data) /* I - Test data */
2225
{
2226
http_encryption_t encryption; /* Encryption mode */
2227
2228
2229
/*
2230
* Connect to the printer/server...
2231
*/
2232
2233
if (!_cups_strcasecmp(data->vars->scheme, "https") || !_cups_strcasecmp(data->vars->scheme, "ipps") || data->vars->port == 443)
2234
encryption = HTTP_ENCRYPTION_ALWAYS;
2235
else
2236
encryption = data->encryption;
2237
2238
if ((data->http = httpConnect2(data->vars->host, data->vars->port, NULL, data->family, encryption, 1, 30000, NULL)) == NULL)
2239
{
2240
print_fatal_error(data, "Unable to connect to \"%s\" on port %d - %s", data->vars->host, data->vars->port, cupsLastErrorString());
2241
return (0);
2242
}
2243
2244
#ifdef HAVE_LIBZ
2245
httpSetDefaultField(data->http, HTTP_FIELD_ACCEPT_ENCODING, "deflate, gzip, identity");
2246
#else
2247
httpSetDefaultField(data->http, HTTP_FIELD_ACCEPT_ENCODING, "identity");
2248
#endif /* HAVE_LIBZ */
2249
2250
if (data->timeout > 0.0)
2251
httpSetTimeout(data->http, data->timeout, timeout_cb, NULL);
2252
2253
/*
2254
* Run tests...
2255
*/
2256
2257
_ippFileParse(data->vars, testfile, (void *)data);
2258
2259
/*
2260
* Close connection and return...
2261
*/
2262
2263
httpClose(data->http);
2264
data->http = NULL;
2265
2266
return (data->pass);
2267
}
2268
2269
2270
/*
2271
* 'error_cb()' - Print/add an error message.
2272
*/
2273
2274
static int /* O - 1 to continue, 0 to stop */
2275
error_cb(_ipp_file_t *f, /* I - IPP file data */
2276
ipptool_test_t *data, /* I - Test data */
2277
const char *error) /* I - Error message */
2278
{
2279
(void)f;
2280
2281
print_fatal_error(data, "%s", error);
2282
2283
return (1);
2284
}
2285
2286
2287
/*
2288
* 'expect_matches()' - Return true if the tag matches the specification.
2289
*/
2290
2291
static int /* O - 1 if matches, 0 otherwise */
2292
expect_matches(
2293
ipptool_expect_t *expect, /* I - Expected attribute */
2294
ipp_attribute_t *attr) /* I - Attribute */
2295
{
2296
int i, /* Looping var */
2297
count, /* Number of values */
2298
match; /* Match? */
2299
char *of_type, /* Type name to match */
2300
*paren, /* Pointer to opening parenthesis */
2301
*next, /* Next name to match */
2302
sep; /* Separator character */
2303
ipp_tag_t value_tag; /* Syntax/value tag */
2304
int lower, upper; /* Lower and upper bounds for syntax */
2305
2306
2307
/*
2308
* If we don't expect a particular type, return immediately...
2309
*/
2310
2311
if (!expect->of_type)
2312
return (1);
2313
2314
/*
2315
* Parse the "of_type" value since the string can contain multiple attribute
2316
* types separated by "," or "|"...
2317
*/
2318
2319
value_tag = ippGetValueTag(attr);
2320
count = ippGetCount(attr);
2321
2322
for (of_type = expect->of_type, match = 0; !match && *of_type; of_type = next)
2323
{
2324
/*
2325
* Find the next separator, and set it (temporarily) to nul if present.
2326
*/
2327
2328
for (next = of_type; *next && *next != '|' && *next != ','; next ++);
2329
2330
if ((sep = *next) != '\0')
2331
*next = '\0';
2332
2333
/*
2334
* Support some meta-types to make it easier to write the test file.
2335
*/
2336
2337
if ((paren = strchr(of_type, '(')) != NULL)
2338
{
2339
char *ptr; // Pointer into syntax string
2340
2341
*paren = '\0';
2342
2343
if (!strncmp(paren + 1, "MIN:", 4))
2344
{
2345
lower = INT_MIN;
2346
ptr = paren + 5;
2347
}
2348
else if ((ptr = strchr(paren + 1, ':')) != NULL)
2349
{
2350
lower = atoi(paren + 1);
2351
}
2352
else
2353
{
2354
lower = 0;
2355
ptr = paren + 1;
2356
}
2357
2358
if (!strcmp(ptr, "MAX)"))
2359
upper = INT_MAX;
2360
else
2361
upper = atoi(ptr);
2362
}
2363
else
2364
{
2365
lower = INT_MIN;
2366
upper = INT_MAX;
2367
}
2368
2369
if (!strcmp(of_type, "text"))
2370
{
2371
if (upper == INT_MAX)
2372
upper = 1023;
2373
2374
if (value_tag == IPP_TAG_TEXTLANG || value_tag == IPP_TAG_TEXT)
2375
{
2376
for (i = 0; i < count; i ++)
2377
{
2378
if (strlen(ippGetString(attr, i, NULL)) > (size_t)upper)
2379
break;
2380
}
2381
2382
match = (i == count);
2383
}
2384
}
2385
else if (!strcmp(of_type, "name"))
2386
{
2387
if (upper == INT_MAX)
2388
upper = 255;
2389
2390
if (value_tag == IPP_TAG_NAMELANG || value_tag == IPP_TAG_NAME)
2391
{
2392
for (i = 0; i < count; i ++)
2393
{
2394
if (strlen(ippGetString(attr, i, NULL)) > (size_t)upper)
2395
break;
2396
}
2397
2398
match = (i == count);
2399
}
2400
}
2401
else if (!strcmp(of_type, "collection"))
2402
{
2403
match = value_tag == IPP_TAG_BEGIN_COLLECTION;
2404
}
2405
else if (value_tag == ippTagValue(of_type))
2406
{
2407
switch (value_tag)
2408
{
2409
case IPP_TAG_KEYWORD :
2410
case IPP_TAG_URI :
2411
if (upper == INT_MAX)
2412
{
2413
if (value_tag == IPP_TAG_KEYWORD)
2414
upper = 255;
2415
else
2416
upper = 1023;
2417
}
2418
2419
for (i = 0; i < count; i ++)
2420
{
2421
if (strlen(ippGetString(attr, i, NULL)) > (size_t)upper)
2422
break;
2423
}
2424
2425
match = (i == count);
2426
break;
2427
2428
case IPP_TAG_STRING :
2429
if (upper == INT_MAX)
2430
upper = 1023;
2431
2432
for (i = 0; i < count; i ++)
2433
{
2434
int datalen; // Length of octetString value
2435
2436
ippGetOctetString(attr, i, &datalen);
2437
2438
if (datalen > upper)
2439
break;
2440
}
2441
2442
match = (i == count);
2443
break;
2444
2445
case IPP_TAG_INTEGER :
2446
for (i = 0; i < count; i ++)
2447
{
2448
int value = ippGetInteger(attr, i);
2449
// Integer value
2450
2451
if (value < lower || value > upper)
2452
break;
2453
}
2454
2455
match = (i == count);
2456
break;
2457
2458
case IPP_TAG_RANGE :
2459
for (i = 0; i < count; i ++)
2460
{
2461
int vupper, vlower = ippGetRange(attr, i, &vupper);
2462
// Range value
2463
2464
if (vlower < lower || vlower > upper || vupper < lower || vupper > upper)
2465
break;
2466
}
2467
2468
match = (i == count);
2469
break;
2470
2471
default :
2472
// No other constraints, so this is a match
2473
match = 1;
2474
break;
2475
}
2476
}
2477
2478
/*
2479
* Restore the separators if we have them...
2480
*/
2481
2482
if (paren)
2483
*paren = '(';
2484
2485
if (sep)
2486
*next++ = sep;
2487
}
2488
2489
return (match);
2490
}
2491
2492
2493
/*
2494
* 'get_filename()' - Get a filename based on the current test file.
2495
*/
2496
2497
static char * /* O - Filename */
2498
get_filename(const char *testfile, /* I - Current test file */
2499
char *dst, /* I - Destination filename */
2500
const char *src, /* I - Source filename */
2501
size_t dstsize) /* I - Size of destination buffer */
2502
{
2503
char *dstptr; /* Pointer into destination */
2504
_cups_globals_t *cg = _cupsGlobals();
2505
/* Global data */
2506
2507
2508
if (*src == '<' && src[strlen(src) - 1] == '>')
2509
{
2510
/*
2511
* Map <filename> to CUPS_DATADIR/ipptool/filename...
2512
*/
2513
2514
snprintf(dst, dstsize, "%s/ipptool/%s", cg->cups_datadir, src + 1);
2515
dstptr = dst + strlen(dst) - 1;
2516
if (*dstptr == '>')
2517
*dstptr = '\0';
2518
}
2519
else if (!access(src, R_OK) || *src == '/'
2520
#ifdef _WIN32
2521
|| (isalpha(*src & 255) && src[1] == ':')
2522
#endif /* _WIN32 */
2523
)
2524
{
2525
/*
2526
* Use the path as-is...
2527
*/
2528
2529
strlcpy(dst, src, dstsize);
2530
}
2531
else
2532
{
2533
/*
2534
* Make path relative to testfile...
2535
*/
2536
2537
strlcpy(dst, testfile, dstsize);
2538
if ((dstptr = strrchr(dst, '/')) != NULL)
2539
dstptr ++;
2540
else
2541
dstptr = dst; /* Should never happen */
2542
2543
strlcpy(dstptr, src, dstsize - (size_t)(dstptr - dst));
2544
2545
#if _WIN32
2546
if (_access(dst, 0))
2547
{
2548
/*
2549
* Not available relative to the testfile, see if it can be found on the
2550
* desktop...
2551
*/
2552
const char *userprofile = getenv("USERPROFILE");
2553
/* User home directory */
2554
2555
if (userprofile)
2556
snprintf(dst, dstsize, "%s/Desktop/%s", userprofile, src);
2557
}
2558
#endif /* _WIN32 */
2559
}
2560
2561
return (dst);
2562
}
2563
2564
2565
/*
2566
* 'get_string()' - Get a pointer to a string value or the portion of interest.
2567
*/
2568
2569
static const char * /* O - Pointer to string */
2570
get_string(ipp_attribute_t *attr, /* I - IPP attribute */
2571
int element, /* I - Element to fetch */
2572
int flags, /* I - Value ("with") flags */
2573
char *buffer, /* I - Temporary buffer */
2574
size_t bufsize) /* I - Size of temporary buffer */
2575
{
2576
const char *value; /* Value */
2577
char *ptr, /* Pointer into value */
2578
scheme[256], /* URI scheme */
2579
userpass[256], /* Username/password */
2580
hostname[256], /* Hostname */
2581
resource[1024]; /* Resource */
2582
int port; /* Port number */
2583
2584
2585
value = ippGetString(attr, element, NULL);
2586
2587
if (flags & IPPTOOL_WITH_HOSTNAME)
2588
{
2589
if (httpSeparateURI(HTTP_URI_CODING_ALL, value, scheme, sizeof(scheme), userpass, sizeof(userpass), buffer, (int)bufsize, &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
2590
buffer[0] = '\0';
2591
2592
ptr = buffer + strlen(buffer) - 1;
2593
if (ptr >= buffer && *ptr == '.')
2594
*ptr = '\0'; /* Drop trailing "." */
2595
2596
return (buffer);
2597
}
2598
else if (flags & IPPTOOL_WITH_RESOURCE)
2599
{
2600
if (httpSeparateURI(HTTP_URI_CODING_ALL, value, scheme, sizeof(scheme), userpass, sizeof(userpass), hostname, sizeof(hostname), &port, buffer, (int)bufsize) < HTTP_URI_STATUS_OK)
2601
buffer[0] = '\0';
2602
2603
return (buffer);
2604
}
2605
else if (flags & IPPTOOL_WITH_SCHEME)
2606
{
2607
if (httpSeparateURI(HTTP_URI_CODING_ALL, value, buffer, (int)bufsize, userpass, sizeof(userpass), hostname, sizeof(hostname), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
2608
buffer[0] = '\0';
2609
2610
return (buffer);
2611
}
2612
else if (ippGetValueTag(attr) == IPP_TAG_URI && (!strncmp(value, "ipp://", 6) || !strncmp(value, "http://", 7) || !strncmp(value, "ipps://", 7) || !strncmp(value, "https://", 8)))
2613
{
2614
http_uri_status_t status = httpSeparateURI(HTTP_URI_CODING_ALL, value, scheme, sizeof(scheme), userpass, sizeof(userpass), hostname, sizeof(hostname), &port, resource, sizeof(resource));
2615
2616
if (status < HTTP_URI_STATUS_OK)
2617
{
2618
/*
2619
* Bad URI...
2620
*/
2621
2622
buffer[0] = '\0';
2623
}
2624
else
2625
{
2626
/*
2627
* Normalize URI with no trailing dot...
2628
*/
2629
2630
if ((ptr = hostname + strlen(hostname) - 1) >= hostname && *ptr == '.')
2631
*ptr = '\0';
2632
2633
httpAssembleURI(HTTP_URI_CODING_ALL, buffer, (int)bufsize, scheme, userpass, hostname, port, resource);
2634
}
2635
2636
return (buffer);
2637
}
2638
else
2639
return (value);
2640
}
2641
2642
2643
/*
2644
* 'init_data()' - Initialize test data.
2645
*/
2646
2647
static void
2648
init_data(ipptool_test_t *data) /* I - Data */
2649
{
2650
memset(data, 0, sizeof(ipptool_test_t));
2651
2652
data->output = IPPTOOL_OUTPUT_LIST;
2653
data->outfile = cupsFileStdout();
2654
data->family = AF_UNSPEC;
2655
data->def_transfer = IPPTOOL_TRANSFER_AUTO;
2656
data->def_version = 11;
2657
data->errors = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free);
2658
data->pass = 1;
2659
data->prev_pass = 1;
2660
data->request_id = (CUPS_RAND() % 1000) * 137;
2661
data->show_header = 1;
2662
}
2663
2664
2665
/*
2666
* 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
2667
* value.
2668
*/
2669
2670
static char * /* O - ISO 8601 date/time string */
2671
iso_date(const ipp_uchar_t *date) /* I - IPP (RFC 1903) date/time value */
2672
{
2673
time_t utctime; /* UTC time since 1970 */
2674
struct tm utcdate; /* UTC date/time */
2675
static char buffer[255]; /* String buffer */
2676
2677
2678
utctime = ippDateToTime(date);
2679
gmtime_r(&utctime, &utcdate);
2680
2681
snprintf(buffer, sizeof(buffer), "%04d-%02d-%02dT%02d:%02d:%02dZ",
2682
utcdate.tm_year + 1900, utcdate.tm_mon + 1, utcdate.tm_mday,
2683
utcdate.tm_hour, utcdate.tm_min, utcdate.tm_sec);
2684
2685
return (buffer);
2686
}
2687
2688
2689
/*
2690
* 'parse_monitor_printer_state()' - Parse the MONITOR-PRINTER-STATE directive.
2691
*
2692
* MONITOR-PRINTER-STATE [printer-uri] {
2693
* DELAY nnn
2694
* EXPECT attribute-name ...
2695
* }
2696
*/
2697
2698
static int /* O - 1 to continue, 0 to stop */
2699
parse_monitor_printer_state(
2700
_ipp_file_t *f, /* I - IPP file data */
2701
ipptool_test_t *data) /* I - Test data */
2702
{
2703
char token[256], /* Token string */
2704
name[1024], /* Name string */
2705
temp[1024], /* Temporary string */
2706
value[1024], /* Value string */
2707
*ptr; /* Pointer into value */
2708
2709
2710
if (!_ippFileReadToken(f, temp, sizeof(temp)))
2711
{
2712
print_fatal_error(data, "Missing printer URI on line %d of \"%s\".", f->linenum, f->filename);
2713
return (0);
2714
}
2715
2716
if (strcmp(temp, "{"))
2717
{
2718
// Got a printer URI so copy it...
2719
_ippVarsExpand(data->vars, value, temp, sizeof(value));
2720
data->monitor_uri = strdup(value);
2721
2722
// Then see if we have an opening brace...
2723
if (!_ippFileReadToken(f, temp, sizeof(temp)) || strcmp(temp, "{"))
2724
{
2725
print_fatal_error(data, "Missing opening brace on line %d of \"%s\".", f->linenum, f->filename);
2726
return (0);
2727
}
2728
}
2729
else
2730
{
2731
// Use the default printer URI...
2732
data->monitor_uri = strdup(data->vars->uri);
2733
}
2734
2735
// Loop until we get a closing brace...
2736
while (_ippFileReadToken(f, token, sizeof(token)))
2737
{
2738
if (_cups_strcasecmp(token, "COUNT") &&
2739
_cups_strcasecmp(token, "DEFINE-MATCH") &&
2740
_cups_strcasecmp(token, "DEFINE-NO-MATCH") &&
2741
_cups_strcasecmp(token, "DEFINE-VALUE") &&
2742
_cups_strcasecmp(token, "DISPLAY-MATCH") &&
2743
_cups_strcasecmp(token, "IF-DEFINED") &&
2744
_cups_strcasecmp(token, "IF-NOT-DEFINED") &&
2745
_cups_strcasecmp(token, "IN-GROUP") &&
2746
_cups_strcasecmp(token, "OF-TYPE") &&
2747
_cups_strcasecmp(token, "WITH-DISTINCT-VALUES") &&
2748
_cups_strcasecmp(token, "WITH-VALUE"))
2749
data->last_expect = NULL;
2750
2751
if (!strcmp(token, "}"))
2752
return (1);
2753
else if (!_cups_strcasecmp(token, "EXPECT"))
2754
{
2755
/*
2756
* Expected attributes...
2757
*/
2758
2759
if (data->num_monitor_expects >= (int)(sizeof(data->monitor_expects) / sizeof(data->monitor_expects[0])))
2760
{
2761
print_fatal_error(data, "Too many EXPECT's on line %d of \"%s\".", f->linenum, f->filename);
2762
return (0);
2763
}
2764
2765
if (!_ippFileReadToken(f, name, sizeof(name)))
2766
{
2767
print_fatal_error(data, "Missing EXPECT name on line %d of \"%s\".", f->linenum, f->filename);
2768
return (0);
2769
}
2770
2771
data->last_expect = data->monitor_expects + data->num_monitor_expects;
2772
data->num_monitor_expects ++;
2773
2774
memset(data->last_expect, 0, sizeof(ipptool_expect_t));
2775
data->last_expect->repeat_limit = 1000;
2776
2777
if (name[0] == '!')
2778
{
2779
data->last_expect->not_expect = 1;
2780
data->last_expect->name = strdup(name + 1);
2781
}
2782
else if (name[0] == '?')
2783
{
2784
data->last_expect->optional = 1;
2785
data->last_expect->name = strdup(name + 1);
2786
}
2787
else
2788
data->last_expect->name = strdup(name);
2789
}
2790
else if (!_cups_strcasecmp(token, "COUNT"))
2791
{
2792
int count; /* Count value */
2793
2794
if (!_ippFileReadToken(f, temp, sizeof(temp)))
2795
{
2796
print_fatal_error(data, "Missing COUNT number on line %d of \"%s\".", f->linenum, f->filename);
2797
return (0);
2798
}
2799
2800
if ((count = atoi(temp)) <= 0)
2801
{
2802
print_fatal_error(data, "Bad COUNT \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
2803
return (0);
2804
}
2805
2806
if (data->last_expect)
2807
{
2808
data->last_expect->count = count;
2809
}
2810
else
2811
{
2812
print_fatal_error(data, "COUNT without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
2813
return (0);
2814
}
2815
}
2816
else if (!_cups_strcasecmp(token, "DEFINE-MATCH"))
2817
{
2818
if (!_ippFileReadToken(f, temp, sizeof(temp)))
2819
{
2820
print_fatal_error(data, "Missing DEFINE-MATCH variable on line %d of \"%s\".", f->linenum, f->filename);
2821
return (0);
2822
}
2823
2824
if (data->last_expect)
2825
{
2826
data->last_expect->define_match = strdup(temp);
2827
}
2828
else
2829
{
2830
print_fatal_error(data, "DEFINE-MATCH without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
2831
return (0);
2832
}
2833
}
2834
else if (!_cups_strcasecmp(token, "DEFINE-NO-MATCH"))
2835
{
2836
if (!_ippFileReadToken(f, temp, sizeof(temp)))
2837
{
2838
print_fatal_error(data, "Missing DEFINE-NO-MATCH variable on line %d of \"%s\".", f->linenum, f->filename);
2839
return (0);
2840
}
2841
2842
if (data->last_expect)
2843
{
2844
data->last_expect->define_no_match = strdup(temp);
2845
}
2846
else
2847
{
2848
print_fatal_error(data, "DEFINE-NO-MATCH without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
2849
return (0);
2850
}
2851
}
2852
else if (!_cups_strcasecmp(token, "DEFINE-VALUE"))
2853
{
2854
if (!_ippFileReadToken(f, temp, sizeof(temp)))
2855
{
2856
print_fatal_error(data, "Missing DEFINE-VALUE variable on line %d of \"%s\".", f->linenum, f->filename);
2857
return (0);
2858
}
2859
2860
if (data->last_expect)
2861
{
2862
data->last_expect->define_value = strdup(temp);
2863
}
2864
else
2865
{
2866
print_fatal_error(data, "DEFINE-VALUE without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
2867
return (0);
2868
}
2869
}
2870
else if (!_cups_strcasecmp(token, "DISPLAY-MATCH"))
2871
{
2872
if (!_ippFileReadToken(f, temp, sizeof(temp)))
2873
{
2874
print_fatal_error(data, "Missing DISPLAY-MATCH message on line %d of \"%s\".", f->linenum, f->filename);
2875
return (0);
2876
}
2877
2878
if (data->last_expect)
2879
{
2880
data->last_expect->display_match = strdup(temp);
2881
}
2882
else
2883
{
2884
print_fatal_error(data, "DISPLAY-MATCH without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
2885
return (0);
2886
}
2887
}
2888
else if (!_cups_strcasecmp(token, "DELAY"))
2889
{
2890
/*
2891
* Delay before operation...
2892
*/
2893
2894
double dval; /* Delay value */
2895
2896
if (!_ippFileReadToken(f, temp, sizeof(temp)))
2897
{
2898
print_fatal_error(data, "Missing DELAY value on line %d of \"%s\".", f->linenum, f->filename);
2899
return (0);
2900
}
2901
2902
_ippVarsExpand(data->vars, value, temp, sizeof(value));
2903
2904
if ((dval = _cupsStrScand(value, &ptr, localeconv())) < 0.0 || (*ptr && *ptr != ','))
2905
{
2906
print_fatal_error(data, "Bad DELAY value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
2907
return (0);
2908
}
2909
2910
data->monitor_delay = (useconds_t)(1000000.0 * dval);
2911
2912
if (*ptr == ',')
2913
{
2914
if ((dval = _cupsStrScand(ptr + 1, &ptr, localeconv())) <= 0.0 || *ptr)
2915
{
2916
print_fatal_error(data, "Bad DELAY value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
2917
return (0);
2918
}
2919
2920
data->monitor_interval = (useconds_t)(1000000.0 * dval);
2921
}
2922
else
2923
data->monitor_interval = data->monitor_delay;
2924
}
2925
else if (!_cups_strcasecmp(token, "OF-TYPE"))
2926
{
2927
if (!_ippFileReadToken(f, temp, sizeof(temp)))
2928
{
2929
print_fatal_error(data, "Missing OF-TYPE value tag(s) on line %d of \"%s\".", f->linenum, f->filename);
2930
return (0);
2931
}
2932
2933
if (data->last_expect)
2934
{
2935
data->last_expect->of_type = strdup(temp);
2936
}
2937
else
2938
{
2939
print_fatal_error(data, "OF-TYPE without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
2940
return (0);
2941
}
2942
}
2943
else if (!_cups_strcasecmp(token, "IN-GROUP"))
2944
{
2945
ipp_tag_t in_group; /* IN-GROUP value */
2946
2947
if (!_ippFileReadToken(f, temp, sizeof(temp)))
2948
{
2949
print_fatal_error(data, "Missing IN-GROUP group tag on line %d of \"%s\".", f->linenum, f->filename);
2950
return (0);
2951
}
2952
2953
if ((in_group = ippTagValue(temp)) == IPP_TAG_ZERO || in_group >= IPP_TAG_UNSUPPORTED_VALUE)
2954
{
2955
print_fatal_error(data, "Bad IN-GROUP group tag \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
2956
return (0);
2957
}
2958
else if (data->last_expect)
2959
{
2960
data->last_expect->in_group = in_group;
2961
}
2962
else
2963
{
2964
print_fatal_error(data, "IN-GROUP without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
2965
return (0);
2966
}
2967
}
2968
else if (!_cups_strcasecmp(token, "IF-DEFINED"))
2969
{
2970
if (!_ippFileReadToken(f, temp, sizeof(temp)))
2971
{
2972
print_fatal_error(data, "Missing IF-DEFINED name on line %d of \"%s\".", f->linenum, f->filename);
2973
return (0);
2974
}
2975
2976
if (data->last_expect)
2977
{
2978
data->last_expect->if_defined = strdup(temp);
2979
}
2980
else
2981
{
2982
print_fatal_error(data, "IF-DEFINED without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
2983
return (0);
2984
}
2985
}
2986
else if (!_cups_strcasecmp(token, "IF-NOT-DEFINED"))
2987
{
2988
if (!_ippFileReadToken(f, temp, sizeof(temp)))
2989
{
2990
print_fatal_error(data, "Missing IF-NOT-DEFINED name on line %d of \"%s\".", f->linenum, f->filename);
2991
return (0);
2992
}
2993
2994
if (data->last_expect)
2995
{
2996
data->last_expect->if_not_defined = strdup(temp);
2997
}
2998
else
2999
{
3000
print_fatal_error(data, "IF-NOT-DEFINED without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
3001
return (0);
3002
}
3003
}
3004
else if (!_cups_strcasecmp(token, "WITH-DISTINCT-VALUES"))
3005
{
3006
if (data->last_expect)
3007
{
3008
data->last_expect->with_distinct = 1;
3009
}
3010
else
3011
{
3012
print_fatal_error(data, "%s without a preceding EXPECT on line %d of \"%s\".", token, f->linenum, f->filename);
3013
return (0);
3014
}
3015
}
3016
else if (!_cups_strcasecmp(token, "WITH-VALUE"))
3017
{
3018
off_t lastpos; /* Last file position */
3019
int lastline; /* Last line number */
3020
3021
if (!_ippFileReadToken(f, temp, sizeof(temp)))
3022
{
3023
print_fatal_error(data, "Missing %s value on line %d of \"%s\".", token, f->linenum, f->filename);
3024
return (0);
3025
}
3026
3027
/*
3028
* Read additional comma-delimited values - needed since legacy test files
3029
* will have unquoted WITH-VALUE values with commas...
3030
*/
3031
3032
ptr = temp + strlen(temp);
3033
3034
for (;;)
3035
{
3036
lastpos = cupsFileTell(f->fp);
3037
lastline = f->linenum;
3038
ptr += strlen(ptr);
3039
3040
if (!_ippFileReadToken(f, ptr, (sizeof(temp) - (size_t)(ptr - temp))))
3041
break;
3042
3043
if (!strcmp(ptr, ","))
3044
{
3045
/*
3046
* Append a value...
3047
*/
3048
3049
ptr += strlen(ptr);
3050
3051
if (!_ippFileReadToken(f, ptr, (sizeof(temp) - (size_t)(ptr - temp))))
3052
break;
3053
}
3054
else
3055
{
3056
/*
3057
* Not another value, stop here...
3058
*/
3059
3060
cupsFileSeek(f->fp, lastpos);
3061
f->linenum = lastline;
3062
*ptr = '\0';
3063
break;
3064
}
3065
}
3066
3067
if (data->last_expect)
3068
{
3069
/*
3070
* Expand any variables in the value and then save it.
3071
*/
3072
3073
_ippVarsExpand(data->vars, value, temp, sizeof(value));
3074
3075
ptr = value + strlen(value) - 1;
3076
3077
if (value[0] == '/' && ptr > value && *ptr == '/')
3078
{
3079
/*
3080
* WITH-VALUE is a POSIX extended regular expression.
3081
*/
3082
3083
data->last_expect->with_value = calloc(1, (size_t)(ptr - value));
3084
data->last_expect->with_flags |= IPPTOOL_WITH_REGEX;
3085
3086
if (data->last_expect->with_value)
3087
memcpy(data->last_expect->with_value, value + 1, (size_t)(ptr - value - 1));
3088
}
3089
else
3090
{
3091
/*
3092
* WITH-VALUE is a literal value...
3093
*/
3094
3095
for (ptr = value; *ptr; ptr ++)
3096
{
3097
if (*ptr == '\\' && ptr[1])
3098
{
3099
/*
3100
* Remove \ from \foo...
3101
*/
3102
3103
_cups_strcpy(ptr, ptr + 1);
3104
}
3105
}
3106
3107
data->last_expect->with_value = strdup(value);
3108
data->last_expect->with_flags |= IPPTOOL_WITH_LITERAL;
3109
}
3110
}
3111
else
3112
{
3113
print_fatal_error(data, "%s without a preceding EXPECT on line %d of \"%s\".", token, f->linenum, f->filename);
3114
return (0);
3115
}
3116
}
3117
}
3118
3119
print_fatal_error(data, "Missing closing brace on line %d of \"%s\".", f->linenum, f->filename);
3120
3121
return (0);
3122
}
3123
3124
3125
/*
3126
* 'pause_message()' - Display the message and pause until the user presses a key.
3127
*/
3128
3129
static void
3130
pause_message(const char *message) /* I - Message */
3131
{
3132
#ifdef _WIN32
3133
HANDLE tty; /* Console handle */
3134
DWORD mode; /* Console mode */
3135
char key; /* Key press */
3136
DWORD bytes; /* Bytes read for key press */
3137
3138
3139
/*
3140
* Disable input echo and set raw input...
3141
*/
3142
3143
if ((tty = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE)
3144
return;
3145
3146
if (!GetConsoleMode(tty, &mode))
3147
return;
3148
3149
if (!SetConsoleMode(tty, 0))
3150
return;
3151
3152
#else
3153
int tty; /* /dev/tty - never read from stdin */
3154
struct termios original, /* Original input mode */
3155
noecho; /* No echo input mode */
3156
char key; /* Current key press */
3157
3158
3159
/*
3160
* Disable input echo and set raw input...
3161
*/
3162
3163
if ((tty = open("/dev/tty", O_RDONLY)) < 0)
3164
return;
3165
3166
if (tcgetattr(tty, &original))
3167
{
3168
close(tty);
3169
return;
3170
}
3171
3172
noecho = original;
3173
noecho.c_lflag &= (tcflag_t)~(ICANON | ECHO | ECHOE | ISIG);
3174
3175
if (tcsetattr(tty, TCSAFLUSH, &noecho))
3176
{
3177
close(tty);
3178
return;
3179
}
3180
#endif /* _WIN32 */
3181
3182
/*
3183
* Display the prompt...
3184
*/
3185
3186
cupsFilePrintf(cupsFileStdout(), "\n%s\n\n---- PRESS ANY KEY ----", message);
3187
3188
#ifdef _WIN32
3189
/*
3190
* Read a key...
3191
*/
3192
3193
ReadFile(tty, &key, 1, &bytes, NULL);
3194
3195
/*
3196
* Cleanup...
3197
*/
3198
3199
SetConsoleMode(tty, mode);
3200
3201
#else
3202
/*
3203
* Read a key...
3204
*/
3205
3206
read(tty, &key, 1);
3207
3208
/*
3209
* Cleanup...
3210
*/
3211
3212
tcsetattr(tty, TCSAFLUSH, &original);
3213
close(tty);
3214
#endif /* _WIN32 */
3215
3216
/*
3217
* Erase the "press any key" prompt...
3218
*/
3219
3220
cupsFilePuts(cupsFileStdout(), "\r \r");
3221
}
3222
3223
3224
/*
3225
* 'print_attr()' - Print an attribute on the screen.
3226
*/
3227
3228
static void
3229
print_attr(cups_file_t *outfile, /* I - Output file */
3230
ipptool_output_t output, /* I - Output format */
3231
ipp_attribute_t *attr, /* I - Attribute to print */
3232
ipp_tag_t *group) /* IO - Current group */
3233
{
3234
int i, /* Looping var */
3235
count; /* Number of values */
3236
ipp_attribute_t *colattr; /* Collection attribute */
3237
3238
3239
if (output == IPPTOOL_OUTPUT_PLIST)
3240
{
3241
if (!ippGetName(attr) || (group && *group != ippGetGroupTag(attr)))
3242
{
3243
if (ippGetGroupTag(attr) != IPP_TAG_ZERO)
3244
{
3245
cupsFilePuts(outfile, "</dict>\n");
3246
cupsFilePuts(outfile, "<dict>\n");
3247
}
3248
3249
if (group)
3250
*group = ippGetGroupTag(attr);
3251
}
3252
3253
if (!ippGetName(attr))
3254
return;
3255
3256
print_xml_string(outfile, "key", ippGetName(attr));
3257
if ((count = ippGetCount(attr)) > 1)
3258
cupsFilePuts(outfile, "<array>\n");
3259
3260
switch (ippGetValueTag(attr))
3261
{
3262
case IPP_TAG_INTEGER :
3263
case IPP_TAG_ENUM :
3264
for (i = 0; i < count; i ++)
3265
cupsFilePrintf(outfile, "<integer>%d</integer>\n", ippGetInteger(attr, i));
3266
break;
3267
3268
case IPP_TAG_BOOLEAN :
3269
for (i = 0; i < count; i ++)
3270
cupsFilePuts(outfile, ippGetBoolean(attr, i) ? "<true />\n" : "<false />\n");
3271
break;
3272
3273
case IPP_TAG_RANGE :
3274
for (i = 0; i < count; i ++)
3275
{
3276
int lower, upper; /* Lower and upper ranges */
3277
3278
lower = ippGetRange(attr, i, &upper);
3279
cupsFilePrintf(outfile, "<dict><key>lower</key><integer>%d</integer><key>upper</key><integer>%d</integer></dict>\n", lower, upper);
3280
}
3281
break;
3282
3283
case IPP_TAG_RESOLUTION :
3284
for (i = 0; i < count; i ++)
3285
{
3286
int xres, yres; /* Resolution values */
3287
ipp_res_t units; /* Resolution units */
3288
3289
xres = ippGetResolution(attr, i, &yres, &units);
3290
cupsFilePrintf(outfile, "<dict><key>xres</key><integer>%d</integer><key>yres</key><integer>%d</integer><key>units</key><string>%s</string></dict>\n", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
3291
}
3292
break;
3293
3294
case IPP_TAG_DATE :
3295
for (i = 0; i < count; i ++)
3296
cupsFilePrintf(outfile, "<date>%s</date>\n", iso_date(ippGetDate(attr, i)));
3297
break;
3298
3299
case IPP_TAG_STRING :
3300
for (i = 0; i < count; i ++)
3301
{
3302
int datalen; /* Length of data */
3303
void *data = ippGetOctetString(attr, i, &datalen);
3304
/* Data */
3305
char buffer[IPP_MAX_LENGTH * 5 / 4 + 1];
3306
/* Base64 output buffer */
3307
3308
cupsFilePrintf(outfile, "<data>%s</data>\n", httpEncode64_2(buffer, sizeof(buffer), data, datalen));
3309
}
3310
break;
3311
3312
case IPP_TAG_TEXT :
3313
case IPP_TAG_NAME :
3314
case IPP_TAG_KEYWORD :
3315
case IPP_TAG_URI :
3316
case IPP_TAG_URISCHEME :
3317
case IPP_TAG_CHARSET :
3318
case IPP_TAG_LANGUAGE :
3319
case IPP_TAG_MIMETYPE :
3320
for (i = 0; i < count; i ++)
3321
print_xml_string(outfile, "string", ippGetString(attr, i, NULL));
3322
break;
3323
3324
case IPP_TAG_TEXTLANG :
3325
case IPP_TAG_NAMELANG :
3326
for (i = 0; i < count; i ++)
3327
{
3328
const char *s, /* String */
3329
*lang; /* Language */
3330
3331
s = ippGetString(attr, i, &lang);
3332
cupsFilePuts(outfile, "<dict><key>language</key><string>");
3333
print_xml_string(outfile, NULL, lang);
3334
cupsFilePuts(outfile, "</string><key>string</key><string>");
3335
print_xml_string(outfile, NULL, s);
3336
cupsFilePuts(outfile, "</string></dict>\n");
3337
}
3338
break;
3339
3340
case IPP_TAG_BEGIN_COLLECTION :
3341
for (i = 0; i < count; i ++)
3342
{
3343
ipp_t *col = ippGetCollection(attr, i);
3344
/* Collection value */
3345
3346
cupsFilePuts(outfile, "<dict>\n");
3347
for (colattr = ippFirstAttribute(col); colattr; colattr = ippNextAttribute(col))
3348
print_attr(outfile, output, colattr, NULL);
3349
cupsFilePuts(outfile, "</dict>\n");
3350
}
3351
break;
3352
3353
default :
3354
cupsFilePrintf(outfile, "<string>&lt;&lt;%s&gt;&gt;</string>\n", ippTagString(ippGetValueTag(attr)));
3355
break;
3356
}
3357
3358
if (count > 1)
3359
cupsFilePuts(outfile, "</array>\n");
3360
}
3361
else
3362
{
3363
char buffer[131072]; /* Value buffer */
3364
3365
if (output == IPPTOOL_OUTPUT_TEST)
3366
{
3367
if (!ippGetName(attr))
3368
{
3369
cupsFilePuts(outfile, " -- separator --\n");
3370
return;
3371
}
3372
3373
cupsFilePrintf(outfile, " %s (%s%s) = ", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)));
3374
}
3375
3376
ippAttributeString(attr, buffer, sizeof(buffer));
3377
cupsFilePrintf(outfile, "%s\n", buffer);
3378
}
3379
}
3380
3381
3382
/*
3383
* 'print_csv()' - Print a line of CSV text.
3384
*/
3385
3386
static ipp_attribute_t * /* O - Next attribute */
3387
print_csv(
3388
ipptool_test_t *data, /* I - Test data */
3389
ipp_t *ipp, /* I - Response message */
3390
ipp_attribute_t *attr, /* I - First attribute for line */
3391
int num_displayed, /* I - Number of attributes to display */
3392
char **displayed, /* I - Attributes to display */
3393
size_t *widths) /* I - Column widths */
3394
{
3395
int i; /* Looping var */
3396
size_t maxlength; /* Max length of all columns */
3397
ipp_attribute_t *current = attr; /* Current attribute */
3398
char *values[MAX_DISPLAY], /* Strings to display */
3399
*valptr; /* Pointer into value */
3400
3401
/*
3402
* Get the maximum string length we have to show and allocate...
3403
*/
3404
3405
for (i = 1, maxlength = widths[0]; i < num_displayed; i ++)
3406
if (widths[i] > maxlength)
3407
maxlength = widths[i];
3408
3409
maxlength += 2;
3410
3411
/*
3412
* Loop through the attributes to display...
3413
*/
3414
3415
if (attr)
3416
{
3417
// Collect the values...
3418
memset(values, 0, sizeof(values));
3419
3420
for (; current; current = ippNextAttribute(ipp))
3421
{
3422
if (!ippGetName(current))
3423
break;
3424
3425
for (i = 0; i < num_displayed; i ++)
3426
{
3427
if (!strcmp(ippGetName(current), displayed[i]))
3428
{
3429
if ((values[i] = (char *)calloc(1, maxlength)) != NULL)
3430
ippAttributeString(current, values[i], maxlength);
3431
break;
3432
}
3433
}
3434
}
3435
3436
// Output the line...
3437
for (i = 0; i < num_displayed; i ++)
3438
{
3439
if (i)
3440
cupsFilePutChar(data->outfile, ',');
3441
3442
if (!values[i])
3443
continue;
3444
3445
if (strchr(values[i], ',') != NULL || strchr(values[i], '\"') != NULL || strchr(values[i], '\\') != NULL)
3446
{
3447
// Quoted value...
3448
cupsFilePutChar(data->outfile, '\"');
3449
for (valptr = values[i]; *valptr; valptr ++)
3450
{
3451
if (*valptr == '\\' || *valptr == '\"')
3452
cupsFilePutChar(data->outfile, '\\');
3453
cupsFilePutChar(data->outfile, *valptr);
3454
}
3455
cupsFilePutChar(data->outfile, '\"');
3456
}
3457
else
3458
{
3459
// Unquoted value...
3460
cupsFilePuts(data->outfile, values[i]);
3461
}
3462
3463
free(values[i]);
3464
}
3465
cupsFilePutChar(data->outfile, '\n');
3466
}
3467
else
3468
{
3469
// Show column headings...
3470
for (i = 0; i < num_displayed; i ++)
3471
{
3472
if (i)
3473
cupsFilePutChar(data->outfile, ',');
3474
3475
cupsFilePuts(data->outfile, displayed[i]);
3476
}
3477
cupsFilePutChar(data->outfile, '\n');
3478
}
3479
3480
return (current);
3481
}
3482
3483
3484
/*
3485
* 'print_fatal_error()' - Print a fatal error message.
3486
*/
3487
3488
static void
3489
print_fatal_error(
3490
ipptool_test_t *data, /* I - Test data */
3491
const char *s, /* I - Printf-style format string */
3492
...) /* I - Additional arguments as needed */
3493
{
3494
char buffer[10240]; /* Format buffer */
3495
va_list ap; /* Pointer to arguments */
3496
3497
3498
/*
3499
* Format the error message...
3500
*/
3501
3502
va_start(ap, s);
3503
vsnprintf(buffer, sizeof(buffer), s, ap);
3504
va_end(ap);
3505
3506
/*
3507
* Then output it...
3508
*/
3509
3510
if (data->output == IPPTOOL_OUTPUT_PLIST)
3511
{
3512
print_xml_header(data);
3513
print_xml_trailer(data, 0, buffer);
3514
}
3515
3516
_cupsLangPrintf(stderr, "ipptool: %s", buffer);
3517
}
3518
3519
3520
/*
3521
* 'print_ippserver_attr()' - Print an attribute suitable for use by ippserver.
3522
*/
3523
3524
static void
3525
print_ippserver_attr(
3526
ipptool_test_t *data, /* I - Test data */
3527
ipp_attribute_t *attr, /* I - Attribute to print */
3528
int indent) /* I - Indentation level */
3529
{
3530
int i, /* Looping var */
3531
count = ippGetCount(attr);
3532
/* Number of values */
3533
ipp_attribute_t *colattr; /* Collection attribute */
3534
3535
3536
if (indent == 0)
3537
cupsFilePrintf(data->outfile, "ATTR %s %s", ippTagString(ippGetValueTag(attr)), ippGetName(attr));
3538
else
3539
cupsFilePrintf(data->outfile, "%*sMEMBER %s %s", indent, "", ippTagString(ippGetValueTag(attr)), ippGetName(attr));
3540
3541
switch (ippGetValueTag(attr))
3542
{
3543
case IPP_TAG_INTEGER :
3544
case IPP_TAG_ENUM :
3545
for (i = 0; i < count; i ++)
3546
cupsFilePrintf(data->outfile, "%s%d", i ? "," : " ", ippGetInteger(attr, i));
3547
break;
3548
3549
case IPP_TAG_BOOLEAN :
3550
cupsFilePuts(data->outfile, ippGetBoolean(attr, 0) ? " true" : " false");
3551
3552
for (i = 1; i < count; i ++)
3553
cupsFilePuts(data->outfile, ippGetBoolean(attr, 1) ? ",true" : ",false");
3554
break;
3555
3556
case IPP_TAG_RANGE :
3557
for (i = 0; i < count; i ++)
3558
{
3559
int upper, lower = ippGetRange(attr, i, &upper);
3560
3561
cupsFilePrintf(data->outfile, "%s%d-%d", i ? "," : " ", lower, upper);
3562
}
3563
break;
3564
3565
case IPP_TAG_RESOLUTION :
3566
for (i = 0; i < count; i ++)
3567
{
3568
ipp_res_t units;
3569
int yres, xres = ippGetResolution(attr, i, &yres, &units);
3570
3571
cupsFilePrintf(data->outfile, "%s%dx%d%s", i ? "," : " ", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
3572
}
3573
break;
3574
3575
case IPP_TAG_DATE :
3576
for (i = 0; i < count; i ++)
3577
cupsFilePrintf(data->outfile, "%s%s", i ? "," : " ", iso_date(ippGetDate(attr, i)));
3578
break;
3579
3580
case IPP_TAG_STRING :
3581
for (i = 0; i < count; i ++)
3582
{
3583
int len;
3584
const char *s = (const char *)ippGetOctetString(attr, i, &len);
3585
3586
cupsFilePuts(data->outfile, i ? "," : " ");
3587
print_ippserver_string(data, s, (size_t)len);
3588
}
3589
break;
3590
3591
case IPP_TAG_TEXT :
3592
case IPP_TAG_TEXTLANG :
3593
case IPP_TAG_NAME :
3594
case IPP_TAG_NAMELANG :
3595
case IPP_TAG_KEYWORD :
3596
case IPP_TAG_URI :
3597
case IPP_TAG_URISCHEME :
3598
case IPP_TAG_CHARSET :
3599
case IPP_TAG_LANGUAGE :
3600
case IPP_TAG_MIMETYPE :
3601
for (i = 0; i < count; i ++)
3602
{
3603
const char *s = ippGetString(attr, i, NULL);
3604
3605
cupsFilePuts(data->outfile, i ? "," : " ");
3606
print_ippserver_string(data, s, strlen(s));
3607
}
3608
break;
3609
3610
case IPP_TAG_BEGIN_COLLECTION :
3611
for (i = 0; i < count; i ++)
3612
{
3613
ipp_t *col = ippGetCollection(attr, i);
3614
3615
cupsFilePuts(data->outfile, i ? ",{\n" : " {\n");
3616
for (colattr = ippFirstAttribute(col); colattr; colattr = ippNextAttribute(col))
3617
print_ippserver_attr(data, colattr, indent + 4);
3618
cupsFilePrintf(data->outfile, "%*s}", indent, "");
3619
}
3620
break;
3621
3622
default :
3623
/* Out-of-band value */
3624
break;
3625
}
3626
3627
cupsFilePuts(data->outfile, "\n");
3628
}
3629
3630
3631
/*
3632
* 'print_ippserver_string()' - Print a string suitable for use by ippserver.
3633
*/
3634
3635
static void
3636
print_ippserver_string(
3637
ipptool_test_t *data, /* I - Test data */
3638
const char *s, /* I - String to print */
3639
size_t len) /* I - Length of string */
3640
{
3641
cupsFilePutChar(data->outfile, '\"');
3642
while (len > 0)
3643
{
3644
if (*s == '\"' || *s == '\\')
3645
cupsFilePutChar(data->outfile, '\\');
3646
cupsFilePutChar(data->outfile, *s);
3647
3648
s ++;
3649
len --;
3650
}
3651
cupsFilePutChar(data->outfile, '\"');
3652
}
3653
3654
3655
/*
3656
* 'print_json_attr()' - Print an attribute in JSON format.
3657
*/
3658
3659
static void
3660
print_json_attr(
3661
ipptool_test_t *data, /* I - Test data */
3662
ipp_attribute_t *attr, /* I - IPP attribute */
3663
int indent) /* I - Indentation */
3664
{
3665
const char *name = ippGetName(attr);
3666
/* Name of attribute */
3667
int i, /* Looping var */
3668
count = ippGetCount(attr);
3669
/* Number of values */
3670
ipp_attribute_t *colattr; /* Collection attribute */
3671
3672
3673
cupsFilePrintf(data->outfile, "%*s", indent, "");
3674
print_json_string(data, name, strlen(name));
3675
3676
switch (ippGetValueTag(attr))
3677
{
3678
case IPP_TAG_INTEGER :
3679
case IPP_TAG_ENUM :
3680
if (count == 1)
3681
{
3682
cupsFilePrintf(data->outfile, ": %d", ippGetInteger(attr, 0));
3683
}
3684
else
3685
{
3686
cupsFilePuts(data->outfile, ": [\n");
3687
for (i = 0; i < count; i ++)
3688
cupsFilePrintf(data->outfile, "%*s%d%s", indent + 4, "", ippGetInteger(attr, i), (i + 1) < count ? ",\n" : "\n");
3689
cupsFilePrintf(data->outfile, "%*s]", indent, "");
3690
}
3691
break;
3692
3693
case IPP_TAG_BOOLEAN :
3694
if (count == 1)
3695
{
3696
cupsFilePrintf(data->outfile, ": %s", ippGetBoolean(attr, 0) ? "true" : "false");
3697
}
3698
else
3699
{
3700
cupsFilePuts(data->outfile, ": [\n");
3701
for (i = 0; i < count; i ++)
3702
cupsFilePrintf(data->outfile, "%*s%s%s", indent + 4, "", ippGetBoolean(attr, i) ? "true" : "false", (i + 1) < count ? ",\n" : "\n");
3703
cupsFilePrintf(data->outfile, "%*s]", indent, "");
3704
}
3705
break;
3706
3707
case IPP_TAG_RANGE :
3708
if (count == 1)
3709
{
3710
int upper, lower = ippGetRange(attr, 0, &upper);
3711
3712
cupsFilePrintf(data->outfile, ": {\n%*s\"lower\": %d,\n%*s\"upper\":%d\n%*s}", indent + 4, "", lower, indent + 4, "", upper, indent, "");
3713
}
3714
else
3715
{
3716
cupsFilePuts(data->outfile, ": [\n");
3717
for (i = 0; i < count; i ++)
3718
{
3719
int upper, lower = ippGetRange(attr, i, &upper);
3720
3721
cupsFilePrintf(data->outfile, "%*s{\n%*s\"lower\": %d,\n%*s\"upper\":%d\n%*s},\n", indent + 4, "", indent + 8, "", lower, indent + 8, "", upper, indent + 4, "");
3722
}
3723
cupsFilePrintf(data->outfile, "%*s]", indent, "");
3724
}
3725
break;
3726
3727
case IPP_TAG_RESOLUTION :
3728
if (count == 1)
3729
{
3730
ipp_res_t units;
3731
int yres, xres = ippGetResolution(attr, 0, &yres, &units);
3732
3733
cupsFilePrintf(data->outfile, ": {\n%*s\"units\": \"%s\",\n%*s\"xres\": %d,\n%*s\"yres\":%d\n%*s}", indent + 4, "", units == IPP_RES_PER_INCH ? "dpi" : "dpcm", indent + 4, "", xres, indent + 4, "", yres, indent, "");
3734
}
3735
else
3736
{
3737
cupsFilePuts(data->outfile, ": [\n");
3738
for (i = 0; i < count; i ++)
3739
{
3740
ipp_res_t units;
3741
int yres, xres = ippGetResolution(attr, i, &yres, &units);
3742
3743
cupsFilePrintf(data->outfile, "%*s{\n%*s\"units\": \"%s\",\n%*s\"xres\": %d,\n%*s\"yres\":%d\n%*s},\n", indent + 4, "", indent + 8, "", units == IPP_RES_PER_INCH ? "dpi" : "dpcm", indent + 8, "", xres, indent + 8, "", yres, indent + 4, "");
3744
}
3745
cupsFilePrintf(data->outfile, "%*s]", indent, "");
3746
}
3747
break;
3748
3749
case IPP_TAG_DATE :
3750
if (count == 1)
3751
{
3752
cupsFilePrintf(data->outfile, ": \"%s\"", iso_date(ippGetDate(attr, 0)));
3753
}
3754
else
3755
{
3756
cupsFilePuts(data->outfile, ": [\n");
3757
for (i = 0; i < count; i ++)
3758
cupsFilePrintf(data->outfile, "%*s\"%s\"%s", indent + 4, "", iso_date(ippGetDate(attr, i)), (i + 1) < count ? ",\n" : "\n");
3759
cupsFilePrintf(data->outfile, "%*s]", indent, "");
3760
}
3761
break;
3762
3763
case IPP_TAG_STRING :
3764
if (count == 1)
3765
{
3766
int len;
3767
const char *s = (const char *)ippGetOctetString(attr, 0, &len);
3768
3769
cupsFilePuts(data->outfile, ": \"");
3770
while (len > 0)
3771
{
3772
cupsFilePrintf(data->outfile, "%02X", *s++ & 255);
3773
len --;
3774
}
3775
cupsFilePuts(data->outfile, "\"");
3776
}
3777
else
3778
{
3779
cupsFilePuts(data->outfile, ": [\n");
3780
for (i = 0; i < count; i ++)
3781
{
3782
int len;
3783
const char *s = (const char *)ippGetOctetString(attr, i, &len);
3784
3785
cupsFilePrintf(data->outfile, "%*s\"", indent + 4, "");
3786
while (len > 0)
3787
{
3788
cupsFilePrintf(data->outfile, "%02X", *s++ & 255);
3789
len --;
3790
}
3791
cupsFilePuts(data->outfile, (i + 1) < count ? "\",\n" : "\"\n");
3792
}
3793
cupsFilePrintf(data->outfile, "%*s]", indent, "");
3794
}
3795
break;
3796
3797
case IPP_TAG_TEXT :
3798
case IPP_TAG_TEXTLANG :
3799
case IPP_TAG_NAME :
3800
case IPP_TAG_NAMELANG :
3801
case IPP_TAG_KEYWORD :
3802
case IPP_TAG_URI :
3803
case IPP_TAG_URISCHEME :
3804
case IPP_TAG_CHARSET :
3805
case IPP_TAG_LANGUAGE :
3806
case IPP_TAG_MIMETYPE :
3807
if (count == 1)
3808
{
3809
const char *s = ippGetString(attr, 0, NULL);
3810
3811
cupsFilePuts(data->outfile, ": ");
3812
print_json_string(data, s, strlen(s));
3813
}
3814
else
3815
{
3816
cupsFilePuts(data->outfile, ": [\n");
3817
for (i = 0; i < count; i ++)
3818
{
3819
const char *s = ippGetString(attr, i, NULL);
3820
3821
cupsFilePrintf(data->outfile, "%*s", indent + 4, "");
3822
print_json_string(data, s, strlen(s));
3823
cupsFilePuts(data->outfile, (i + 1) < count ? ",\n" : "\n");
3824
}
3825
cupsFilePrintf(data->outfile, "%*s]", indent, "");
3826
}
3827
break;
3828
3829
case IPP_TAG_BEGIN_COLLECTION :
3830
if (count == 1)
3831
{
3832
ipp_t *col = ippGetCollection(attr, 0);
3833
3834
cupsFilePuts(data->outfile, ": {\n");
3835
colattr = ippFirstAttribute(col);
3836
while (colattr)
3837
{
3838
print_json_attr(data, colattr, indent + 4);
3839
colattr = ippNextAttribute(col);
3840
cupsFilePuts(data->outfile, colattr ? ",\n" : "\n");
3841
}
3842
cupsFilePrintf(data->outfile, "%*s}", indent, "");
3843
}
3844
else
3845
{
3846
cupsFilePuts(data->outfile, ": [\n");
3847
for (i = 0; i < count; i ++)
3848
{
3849
ipp_t *col = ippGetCollection(attr, i);
3850
3851
cupsFilePrintf(data->outfile, "%*s{\n", indent + 4, "");
3852
colattr = ippFirstAttribute(col);
3853
while (colattr)
3854
{
3855
print_json_attr(data, colattr, indent + 8);
3856
colattr = ippNextAttribute(col);
3857
cupsFilePuts(data->outfile, colattr ? ",\n" : "\n");
3858
}
3859
cupsFilePrintf(data->outfile, "%*s}%s", indent + 4, "", (i + 1) < count ? ",\n" : "\n");
3860
}
3861
cupsFilePrintf(data->outfile, "%*s]", indent, "");
3862
}
3863
break;
3864
3865
default :
3866
/* Out-of-band value */
3867
break;
3868
}
3869
}
3870
3871
3872
/*
3873
* 'print_json_string()' - Print a string in JSON format.
3874
*/
3875
3876
static void
3877
print_json_string(
3878
ipptool_test_t *data, /* I - Test data */
3879
const char *s, /* I - String to print */
3880
size_t len) /* I - Length of string */
3881
{
3882
cupsFilePutChar(data->outfile, '\"');
3883
while (len > 0)
3884
{
3885
switch (*s)
3886
{
3887
case '\"' :
3888
case '\\' :
3889
cupsFilePutChar(data->outfile, '\\');
3890
cupsFilePutChar(data->outfile, *s);
3891
break;
3892
3893
case '\n' :
3894
cupsFilePuts(data->outfile, "\\n");
3895
break;
3896
3897
case '\r' :
3898
cupsFilePuts(data->outfile, "\\r");
3899
break;
3900
3901
case '\t' :
3902
cupsFilePuts(data->outfile, "\\t");
3903
break;
3904
3905
default :
3906
if (*s < ' ' && *s >= 0)
3907
cupsFilePrintf(data->outfile, "\\%03o", *s);
3908
else
3909
cupsFilePutChar(data->outfile, *s);
3910
break;
3911
}
3912
3913
s ++;
3914
len --;
3915
}
3916
cupsFilePutChar(data->outfile, '\"');
3917
}
3918
3919
3920
/*
3921
* 'print_line()' - Print a line of formatted or CSV text.
3922
*/
3923
3924
static ipp_attribute_t * /* O - Next attribute */
3925
print_line(
3926
ipptool_test_t *data, /* I - Test data */
3927
ipp_t *ipp, /* I - Response message */
3928
ipp_attribute_t *attr, /* I - First attribute for line */
3929
int num_displayed, /* I - Number of attributes to display */
3930
char **displayed, /* I - Attributes to display */
3931
size_t *widths) /* I - Column widths */
3932
{
3933
int i; /* Looping var */
3934
size_t maxlength; /* Max length of all columns */
3935
ipp_attribute_t *current = attr; /* Current attribute */
3936
char *values[MAX_DISPLAY]; /* Strings to display */
3937
3938
3939
/*
3940
* Get the maximum string length we have to show and allocate...
3941
*/
3942
3943
for (i = 1, maxlength = widths[0]; i < num_displayed; i ++)
3944
if (widths[i] > maxlength)
3945
maxlength = widths[i];
3946
3947
maxlength += 2;
3948
3949
/*
3950
* Loop through the attributes to display...
3951
*/
3952
3953
if (attr)
3954
{
3955
// Collect the values...
3956
memset(values, 0, sizeof(values));
3957
3958
for (; current; current = ippNextAttribute(ipp))
3959
{
3960
if (!ippGetName(current))
3961
break;
3962
3963
for (i = 0; i < num_displayed; i ++)
3964
{
3965
if (!strcmp(ippGetName(current), displayed[i]))
3966
{
3967
if ((values[i] = (char *)calloc(1, maxlength)) != NULL)
3968
ippAttributeString(current, values[i], maxlength);
3969
break;
3970
}
3971
}
3972
}
3973
3974
// Output the line...
3975
for (i = 0; i < num_displayed; i ++)
3976
{
3977
if (i)
3978
cupsFilePutChar(data->outfile, ' ');
3979
3980
cupsFilePrintf(data->outfile, "%*s", (int)-widths[i], values[i] ? values[i] : "");
3981
free(values[i]);
3982
}
3983
cupsFilePutChar(data->outfile, '\n');
3984
}
3985
else
3986
{
3987
// Show column headings...
3988
char *buffer = (char *)malloc(maxlength);
3989
// Buffer for separator lines
3990
3991
if (!buffer)
3992
return (current);
3993
3994
for (i = 0; i < num_displayed; i ++)
3995
{
3996
if (i)
3997
cupsFilePutChar(data->outfile, ' ');
3998
3999
cupsFilePrintf(data->outfile, "%*s", (int)-widths[i], displayed[i]);
4000
}
4001
cupsFilePutChar(data->outfile, '\n');
4002
4003
for (i = 0; i < num_displayed; i ++)
4004
{
4005
if (i)
4006
cupsFilePutChar(data->outfile, ' ');
4007
4008
memset(buffer, '-', widths[i]);
4009
buffer[widths[i]] = '\0';
4010
cupsFilePuts(data->outfile, buffer);
4011
}
4012
cupsFilePutChar(data->outfile, '\n');
4013
free(buffer);
4014
}
4015
4016
return (current);
4017
}
4018
4019
4020
/*
4021
* 'print_xml_header()' - Print a standard XML plist header.
4022
*/
4023
4024
static void
4025
print_xml_header(ipptool_test_t *data)/* I - Test data */
4026
{
4027
if (!data->xml_header)
4028
{
4029
cupsFilePuts(data->outfile, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
4030
cupsFilePuts(data->outfile, "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n");
4031
cupsFilePuts(data->outfile, "<plist version=\"1.0\">\n");
4032
cupsFilePuts(data->outfile, "<dict>\n");
4033
cupsFilePuts(data->outfile, "<key>ipptoolVersion</key>\n");
4034
cupsFilePuts(data->outfile, "<string>" CUPS_SVERSION "</string>\n");
4035
cupsFilePuts(data->outfile, "<key>Transfer</key>\n");
4036
cupsFilePrintf(data->outfile, "<string>%s</string>\n", data->transfer == IPPTOOL_TRANSFER_AUTO ? "auto" : data->transfer == IPPTOOL_TRANSFER_CHUNKED ? "chunked" : "length");
4037
cupsFilePuts(data->outfile, "<key>Tests</key>\n");
4038
cupsFilePuts(data->outfile, "<array>\n");
4039
4040
data->xml_header = 1;
4041
}
4042
}
4043
4044
4045
/*
4046
* 'print_xml_string()' - Print an XML string with escaping.
4047
*/
4048
4049
static void
4050
print_xml_string(cups_file_t *outfile, /* I - Test data */
4051
const char *element, /* I - Element name or NULL */
4052
const char *s) /* I - String to print */
4053
{
4054
if (element)
4055
cupsFilePrintf(outfile, "<%s>", element);
4056
4057
while (*s)
4058
{
4059
if (*s == '&')
4060
cupsFilePuts(outfile, "&amp;");
4061
else if (*s == '<')
4062
cupsFilePuts(outfile, "&lt;");
4063
else if (*s == '>')
4064
cupsFilePuts(outfile, "&gt;");
4065
else if ((*s & 0xe0) == 0xc0)
4066
{
4067
/*
4068
* Validate UTF-8 two-byte sequence...
4069
*/
4070
4071
if ((s[1] & 0xc0) != 0x80)
4072
{
4073
cupsFilePutChar(outfile, '?');
4074
s ++;
4075
}
4076
else
4077
{
4078
cupsFilePutChar(outfile, *s++);
4079
cupsFilePutChar(outfile, *s);
4080
}
4081
}
4082
else if ((*s & 0xf0) == 0xe0)
4083
{
4084
/*
4085
* Validate UTF-8 three-byte sequence...
4086
*/
4087
4088
if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80)
4089
{
4090
cupsFilePutChar(outfile, '?');
4091
s += 2;
4092
}
4093
else
4094
{
4095
cupsFilePutChar(outfile, *s++);
4096
cupsFilePutChar(outfile, *s++);
4097
cupsFilePutChar(outfile, *s);
4098
}
4099
}
4100
else if ((*s & 0xf8) == 0xf0)
4101
{
4102
/*
4103
* Validate UTF-8 four-byte sequence...
4104
*/
4105
4106
if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 ||
4107
(s[3] & 0xc0) != 0x80)
4108
{
4109
cupsFilePutChar(outfile, '?');
4110
s += 3;
4111
}
4112
else
4113
{
4114
cupsFilePutChar(outfile, *s++);
4115
cupsFilePutChar(outfile, *s++);
4116
cupsFilePutChar(outfile, *s++);
4117
cupsFilePutChar(outfile, *s);
4118
}
4119
}
4120
else if ((*s & 0x80) || (*s < ' ' && !isspace(*s & 255)))
4121
{
4122
/*
4123
* Invalid control character...
4124
*/
4125
4126
cupsFilePutChar(outfile, '?');
4127
}
4128
else
4129
cupsFilePutChar(outfile, *s);
4130
4131
s ++;
4132
}
4133
4134
if (element)
4135
cupsFilePrintf(outfile, "</%s>\n", element);
4136
}
4137
4138
4139
/*
4140
* 'print_xml_trailer()' - Print the XML trailer with success/fail value.
4141
*/
4142
4143
static void
4144
print_xml_trailer(
4145
ipptool_test_t *data, /* I - Test data */
4146
int success, /* I - 1 on success, 0 on failure */
4147
const char *message) /* I - Error message or NULL */
4148
{
4149
if (data->xml_header)
4150
{
4151
cupsFilePuts(data->outfile, "</array>\n");
4152
cupsFilePuts(data->outfile, "<key>Successful</key>\n");
4153
cupsFilePuts(data->outfile, success ? "<true />\n" : "<false />\n");
4154
if (message)
4155
{
4156
cupsFilePuts(data->outfile, "<key>ErrorMessage</key>\n");
4157
print_xml_string(data->outfile, "string", message);
4158
}
4159
cupsFilePuts(data->outfile, "</dict>\n");
4160
cupsFilePuts(data->outfile, "</plist>\n");
4161
4162
data->xml_header = 0;
4163
}
4164
}
4165
4166
4167
#ifndef _WIN32
4168
/*
4169
* 'sigterm_handler()' - Handle SIGINT and SIGTERM.
4170
*/
4171
4172
static void
4173
sigterm_handler(int sig) /* I - Signal number (unused) */
4174
{
4175
(void)sig;
4176
4177
Cancel = 1;
4178
4179
signal(SIGINT, SIG_DFL);
4180
signal(SIGTERM, SIG_DFL);
4181
}
4182
#endif /* !_WIN32 */
4183
4184
4185
/*
4186
* 'timeout_cb()' - Handle HTTP timeouts.
4187
*/
4188
4189
static int /* O - 1 to continue, 0 to cancel */
4190
timeout_cb(http_t *http, /* I - Connection to server */
4191
void *user_data) /* I - User data (unused) */
4192
{
4193
int buffered = 0; /* Bytes buffered but not yet sent */
4194
4195
4196
(void)user_data;
4197
4198
/*
4199
* If the socket still have data waiting to be sent to the printer (as can
4200
* happen if the printer runs out of paper), continue to wait until the output
4201
* buffer is empty...
4202
*/
4203
4204
#ifdef SO_NWRITE /* macOS and some versions of Linux */
4205
socklen_t len = sizeof(buffered); /* Size of return value */
4206
4207
if (getsockopt(httpGetFd(http), SOL_SOCKET, SO_NWRITE, &buffered, &len))
4208
buffered = 0;
4209
4210
#elif defined(SIOCOUTQ) /* Others except Windows */
4211
if (ioctl(httpGetFd(http), SIOCOUTQ, &buffered))
4212
buffered = 0;
4213
4214
#else /* Windows (not possible) */
4215
(void)http;
4216
#endif /* SO_NWRITE */
4217
4218
return (buffered > 0);
4219
}
4220
4221
4222
/*
4223
* 'token_cb()' - Parse test file-specific tokens and run tests.
4224
*/
4225
4226
static int /* O - 1 to continue, 0 to stop */
4227
token_cb(_ipp_file_t *f, /* I - IPP file data */
4228
_ipp_vars_t *vars, /* I - IPP variables */
4229
ipptool_test_t *data, /* I - Test data */
4230
const char *token) /* I - Current token */
4231
{
4232
char name[1024], /* Name string */
4233
temp[1024], /* Temporary string */
4234
value[1024], /* Value string */
4235
*ptr; /* Pointer into value */
4236
4237
4238
if (!token)
4239
{
4240
/*
4241
* Initialize state as needed (nothing for now...)
4242
*/
4243
4244
return (1);
4245
}
4246
else if (f->attrs)
4247
{
4248
/*
4249
* Parse until we see a close brace...
4250
*/
4251
4252
if (_cups_strcasecmp(token, "COUNT") &&
4253
_cups_strcasecmp(token, "DEFINE-MATCH") &&
4254
_cups_strcasecmp(token, "DEFINE-NO-MATCH") &&
4255
_cups_strcasecmp(token, "DEFINE-VALUE") &&
4256
_cups_strcasecmp(token, "DISPLAY-MATCH") &&
4257
_cups_strcasecmp(token, "IF-DEFINED") &&
4258
_cups_strcasecmp(token, "IF-NOT-DEFINED") &&
4259
_cups_strcasecmp(token, "IN-GROUP") &&
4260
_cups_strcasecmp(token, "OF-TYPE") &&
4261
_cups_strcasecmp(token, "REPEAT-LIMIT") &&
4262
_cups_strcasecmp(token, "REPEAT-MATCH") &&
4263
_cups_strcasecmp(token, "REPEAT-NO-MATCH") &&
4264
_cups_strcasecmp(token, "SAME-COUNT-AS") &&
4265
_cups_strcasecmp(token, "WITH-ALL-VALUES") &&
4266
_cups_strcasecmp(token, "WITH-ALL-HOSTNAMES") &&
4267
_cups_strcasecmp(token, "WITH-ALL-RESOURCES") &&
4268
_cups_strcasecmp(token, "WITH-ALL-SCHEMES") &&
4269
_cups_strcasecmp(token, "WITH-DISTINCT-VALUES") &&
4270
_cups_strcasecmp(token, "WITH-HOSTNAME") &&
4271
_cups_strcasecmp(token, "WITH-RESOURCE") &&
4272
_cups_strcasecmp(token, "WITH-SCHEME") &&
4273
_cups_strcasecmp(token, "WITH-VALUE") &&
4274
_cups_strcasecmp(token, "WITH-VALUE-FROM"))
4275
data->last_expect = NULL;
4276
4277
if (_cups_strcasecmp(token, "DEFINE-MATCH") &&
4278
_cups_strcasecmp(token, "DEFINE-NO-MATCH") &&
4279
_cups_strcasecmp(token, "IF-DEFINED") &&
4280
_cups_strcasecmp(token, "IF-NOT-DEFINED") &&
4281
_cups_strcasecmp(token, "REPEAT-LIMIT") &&
4282
_cups_strcasecmp(token, "REPEAT-MATCH") &&
4283
_cups_strcasecmp(token, "REPEAT-NO-MATCH"))
4284
data->last_status = NULL;
4285
4286
if (!strcmp(token, "}"))
4287
{
4288
return (do_test(f, data));
4289
}
4290
else if (!strcmp(token, "MONITOR-PRINTER-STATE"))
4291
{
4292
if (data->monitor_uri)
4293
{
4294
print_fatal_error(data, "Extra MONITOR-PRINTER-STATE seen on line %d of \"%s\".", f->linenum, f->filename);
4295
return (0);
4296
}
4297
4298
return (parse_monitor_printer_state(f, data));
4299
}
4300
else if (!strcmp(token, "COMPRESSION"))
4301
{
4302
/*
4303
* COMPRESSION none
4304
* COMPRESSION deflate
4305
* COMPRESSION gzip
4306
*/
4307
4308
if (_ippFileReadToken(f, temp, sizeof(temp)))
4309
{
4310
_ippVarsExpand(vars, data->compression, temp, sizeof(data->compression));
4311
#ifdef HAVE_LIBZ
4312
if (strcmp(data->compression, "none") && strcmp(data->compression, "deflate") &&
4313
strcmp(data->compression, "gzip"))
4314
#else
4315
if (strcmp(data->compression, "none"))
4316
#endif /* HAVE_LIBZ */
4317
{
4318
print_fatal_error(data, "Unsupported COMPRESSION value \"%s\" on line %d of \"%s\".", data->compression, f->linenum, f->filename);
4319
return (0);
4320
}
4321
4322
if (!strcmp(data->compression, "none"))
4323
data->compression[0] = '\0';
4324
}
4325
else
4326
{
4327
print_fatal_error(data, "Missing COMPRESSION value on line %d of \"%s\".", f->linenum, f->filename);
4328
return (0);
4329
}
4330
}
4331
else if (!strcmp(token, "DEFINE"))
4332
{
4333
/*
4334
* DEFINE name value
4335
*/
4336
4337
if (_ippFileReadToken(f, name, sizeof(name)) && _ippFileReadToken(f, temp, sizeof(temp)))
4338
{
4339
_ippVarsExpand(vars, value, temp, sizeof(value));
4340
_ippVarsSet(vars, name, value);
4341
}
4342
else
4343
{
4344
print_fatal_error(data, "Missing DEFINE name and/or value on line %d of \"%s\".", f->linenum, f->filename);
4345
return (0);
4346
}
4347
}
4348
else if (!strcmp(token, "IGNORE-ERRORS"))
4349
{
4350
/*
4351
* IGNORE-ERRORS yes
4352
* IGNORE-ERRORS no
4353
*/
4354
4355
if (_ippFileReadToken(f, temp, sizeof(temp)) && (!_cups_strcasecmp(temp, "yes") || !_cups_strcasecmp(temp, "no")))
4356
{
4357
data->ignore_errors = !_cups_strcasecmp(temp, "yes");
4358
}
4359
else
4360
{
4361
print_fatal_error(data, "Missing IGNORE-ERRORS value on line %d of \"%s\".", f->linenum, f->filename);
4362
return (0);
4363
}
4364
}
4365
else if (!_cups_strcasecmp(token, "NAME"))
4366
{
4367
/*
4368
* Name of test...
4369
*/
4370
4371
if (_ippFileReadToken(f, temp, sizeof(temp)))
4372
{
4373
_ippVarsExpand(vars, data->name, temp, sizeof(data->name));
4374
}
4375
else
4376
{
4377
print_fatal_error(data, "Missing NAME string on line %d of \"%s\".", f->linenum, f->filename);
4378
return (0);
4379
}
4380
}
4381
else if (!_cups_strcasecmp(token, "PAUSE"))
4382
{
4383
/*
4384
* Pause with a message...
4385
*/
4386
4387
if (_ippFileReadToken(f, temp, sizeof(temp)))
4388
{
4389
strlcpy(data->pause, temp, sizeof(data->pause));
4390
}
4391
else
4392
{
4393
print_fatal_error(data, "Missing PAUSE message on line %d of \"%s\".", f->linenum, f->filename);
4394
return (0);
4395
}
4396
}
4397
else if (!strcmp(token, "REQUEST-ID"))
4398
{
4399
/*
4400
* REQUEST-ID #
4401
* REQUEST-ID random
4402
*/
4403
4404
if (_ippFileReadToken(f, temp, sizeof(temp)))
4405
{
4406
if (isdigit(temp[0] & 255))
4407
{
4408
data->request_id = atoi(temp) - 1;
4409
}
4410
else if (!_cups_strcasecmp(temp, "random"))
4411
{
4412
data->request_id = (CUPS_RAND() % 1000) * 137;
4413
}
4414
else
4415
{
4416
print_fatal_error(data, "Bad REQUEST-ID value \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
4417
return (0);
4418
}
4419
}
4420
else
4421
{
4422
print_fatal_error(data, "Missing REQUEST-ID value on line %d of \"%s\".", f->linenum, f->filename);
4423
return (0);
4424
}
4425
}
4426
else if (!strcmp(token, "PASS-IF-DEFINED"))
4427
{
4428
/*
4429
* PASS-IF-DEFINED variable
4430
*/
4431
4432
if (_ippFileReadToken(f, name, sizeof(name)))
4433
{
4434
if (_ippVarsGet(vars, name))
4435
data->pass_test = 1;
4436
}
4437
else
4438
{
4439
print_fatal_error(data, "Missing PASS-IF-DEFINED value on line %d of \"%s\".", f->linenum, f->filename);
4440
return (0);
4441
}
4442
}
4443
else if (!strcmp(token, "PASS-IF-NOT-DEFINED"))
4444
{
4445
/*
4446
* PASS-IF-NOT-DEFINED variable
4447
*/
4448
4449
if (_ippFileReadToken(f, name, sizeof(name)))
4450
{
4451
if (!_ippVarsGet(vars, name))
4452
data->pass_test = 1;
4453
}
4454
else
4455
{
4456
print_fatal_error(data, "Missing PASS-IF-NOT-DEFINED value on line %d of \"%s\".", f->linenum, f->filename);
4457
return (0);
4458
}
4459
}
4460
else if (!strcmp(token, "SKIP-IF-DEFINED"))
4461
{
4462
/*
4463
* SKIP-IF-DEFINED variable
4464
*/
4465
4466
if (_ippFileReadToken(f, name, sizeof(name)))
4467
{
4468
if (_ippVarsGet(vars, name))
4469
data->skip_test = 1;
4470
}
4471
else
4472
{
4473
print_fatal_error(data, "Missing SKIP-IF-DEFINED value on line %d of \"%s\".", f->linenum, f->filename);
4474
return (0);
4475
}
4476
}
4477
else if (!strcmp(token, "SKIP-IF-MISSING"))
4478
{
4479
/*
4480
* SKIP-IF-MISSING filename
4481
*/
4482
4483
if (_ippFileReadToken(f, temp, sizeof(temp)))
4484
{
4485
char filename[1024]; /* Filename */
4486
4487
_ippVarsExpand(vars, value, temp, sizeof(value));
4488
get_filename(f->filename, filename, temp, sizeof(filename));
4489
4490
if (access(filename, R_OK))
4491
data->skip_test = 1;
4492
}
4493
else
4494
{
4495
print_fatal_error(data, "Missing SKIP-IF-MISSING filename on line %d of \"%s\".", f->linenum, f->filename);
4496
return (0);
4497
}
4498
}
4499
else if (!strcmp(token, "SKIP-IF-NOT-DEFINED"))
4500
{
4501
/*
4502
* SKIP-IF-NOT-DEFINED variable
4503
*/
4504
4505
if (_ippFileReadToken(f, name, sizeof(name)))
4506
{
4507
if (!_ippVarsGet(vars, name))
4508
data->skip_test = 1;
4509
}
4510
else
4511
{
4512
print_fatal_error(data, "Missing SKIP-IF-NOT-DEFINED value on line %d of \"%s\".", f->linenum, f->filename);
4513
return (0);
4514
}
4515
}
4516
else if (!strcmp(token, "SKIP-PREVIOUS-ERROR"))
4517
{
4518
/*
4519
* SKIP-PREVIOUS-ERROR yes
4520
* SKIP-PREVIOUS-ERROR no
4521
*/
4522
4523
if (_ippFileReadToken(f, temp, sizeof(temp)) && (!_cups_strcasecmp(temp, "yes") || !_cups_strcasecmp(temp, "no")))
4524
{
4525
data->skip_previous = !_cups_strcasecmp(temp, "yes");
4526
}
4527
else
4528
{
4529
print_fatal_error(data, "Missing SKIP-PREVIOUS-ERROR value on line %d of \"%s\".", f->linenum, f->filename);
4530
return (0);
4531
}
4532
}
4533
else if (!strcmp(token, "TEST-ID"))
4534
{
4535
/*
4536
* TEST-ID "string"
4537
*/
4538
4539
if (_ippFileReadToken(f, temp, sizeof(temp)))
4540
{
4541
_ippVarsExpand(vars, data->test_id, temp, sizeof(data->test_id));
4542
}
4543
else
4544
{
4545
print_fatal_error(data, "Missing TEST-ID value on line %d of \"%s\".", f->linenum, f->filename);
4546
return (0);
4547
}
4548
}
4549
else if (!strcmp(token, "TRANSFER"))
4550
{
4551
/*
4552
* TRANSFER auto
4553
* TRANSFER chunked
4554
* TRANSFER length
4555
*/
4556
4557
if (_ippFileReadToken(f, temp, sizeof(temp)))
4558
{
4559
if (!strcmp(temp, "auto"))
4560
{
4561
data->transfer = IPPTOOL_TRANSFER_AUTO;
4562
}
4563
else if (!strcmp(temp, "chunked"))
4564
{
4565
data->transfer = IPPTOOL_TRANSFER_CHUNKED;
4566
}
4567
else if (!strcmp(temp, "length"))
4568
{
4569
data->transfer = IPPTOOL_TRANSFER_LENGTH;
4570
}
4571
else
4572
{
4573
print_fatal_error(data, "Bad TRANSFER value \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
4574
return (0);
4575
}
4576
}
4577
else
4578
{
4579
print_fatal_error(data, "Missing TRANSFER value on line %d of \"%s\".", f->linenum, f->filename);
4580
return (0);
4581
}
4582
}
4583
else if (!_cups_strcasecmp(token, "VERSION"))
4584
{
4585
if (_ippFileReadToken(f, temp, sizeof(temp)))
4586
{
4587
if (!strcmp(temp, "0.0"))
4588
{
4589
data->version = 0;
4590
}
4591
else if (!strcmp(temp, "1.0"))
4592
{
4593
data->version = 10;
4594
}
4595
else if (!strcmp(temp, "1.1"))
4596
{
4597
data->version = 11;
4598
}
4599
else if (!strcmp(temp, "2.0"))
4600
{
4601
data->version = 20;
4602
}
4603
else if (!strcmp(temp, "2.1"))
4604
{
4605
data->version = 21;
4606
}
4607
else if (!strcmp(temp, "2.2"))
4608
{
4609
data->version = 22;
4610
}
4611
else
4612
{
4613
print_fatal_error(data, "Bad VERSION \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
4614
return (0);
4615
}
4616
}
4617
else
4618
{
4619
print_fatal_error(data, "Missing VERSION number on line %d of \"%s\".", f->linenum, f->filename);
4620
return (0);
4621
}
4622
}
4623
else if (!_cups_strcasecmp(token, "RESOURCE"))
4624
{
4625
/*
4626
* Resource name...
4627
*/
4628
4629
if (!_ippFileReadToken(f, data->resource, sizeof(data->resource)))
4630
{
4631
print_fatal_error(data, "Missing RESOURCE path on line %d of \"%s\".", f->linenum, f->filename);
4632
return (0);
4633
}
4634
}
4635
else if (!_cups_strcasecmp(token, "OPERATION"))
4636
{
4637
/*
4638
* Operation...
4639
*/
4640
4641
ipp_op_t op; /* Operation code */
4642
4643
if (!_ippFileReadToken(f, temp, sizeof(temp)))
4644
{
4645
print_fatal_error(data, "Missing OPERATION code on line %d of \"%s\".", f->linenum, f->filename);
4646
return (0);
4647
}
4648
4649
_ippVarsExpand(vars, value, temp, sizeof(value));
4650
4651
if ((op = ippOpValue(value)) == (ipp_op_t)-1 && (op = (ipp_op_t)strtol(value, NULL, 0)) == 0)
4652
{
4653
print_fatal_error(data, "Bad OPERATION code \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
4654
return (0);
4655
}
4656
4657
ippSetOperation(f->attrs, op);
4658
}
4659
else if (!_cups_strcasecmp(token, "GROUP"))
4660
{
4661
/*
4662
* Attribute group...
4663
*/
4664
4665
ipp_tag_t group_tag; /* Group tag */
4666
4667
if (!_ippFileReadToken(f, temp, sizeof(temp)))
4668
{
4669
print_fatal_error(data, "Missing GROUP tag on line %d of \"%s\".", f->linenum, f->filename);
4670
return (0);
4671
}
4672
4673
if ((group_tag = ippTagValue(temp)) == IPP_TAG_ZERO || group_tag >= IPP_TAG_UNSUPPORTED_VALUE)
4674
{
4675
print_fatal_error(data, "Bad GROUP tag \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
4676
return (0);
4677
}
4678
4679
if (group_tag == f->group_tag)
4680
ippAddSeparator(f->attrs);
4681
4682
f->group_tag = group_tag;
4683
}
4684
else if (!_cups_strcasecmp(token, "DELAY"))
4685
{
4686
/*
4687
* Delay before operation...
4688
*/
4689
4690
double dval; /* Delay value */
4691
4692
if (!_ippFileReadToken(f, temp, sizeof(temp)))
4693
{
4694
print_fatal_error(data, "Missing DELAY value on line %d of \"%s\".", f->linenum, f->filename);
4695
return (0);
4696
}
4697
4698
_ippVarsExpand(vars, value, temp, sizeof(value));
4699
4700
if ((dval = _cupsStrScand(value, &ptr, localeconv())) < 0.0 || (*ptr && *ptr != ','))
4701
{
4702
print_fatal_error(data, "Bad DELAY value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
4703
return (0);
4704
}
4705
4706
data->delay = (useconds_t)(1000000.0 * dval);
4707
4708
if (*ptr == ',')
4709
{
4710
if ((dval = _cupsStrScand(ptr + 1, &ptr, localeconv())) <= 0.0 || *ptr)
4711
{
4712
print_fatal_error(data, "Bad DELAY value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
4713
return (0);
4714
}
4715
4716
data->repeat_interval = (useconds_t)(1000000.0 * dval);
4717
}
4718
else
4719
data->repeat_interval = data->delay;
4720
}
4721
else if (!_cups_strcasecmp(token, "FILE"))
4722
{
4723
/*
4724
* File...
4725
*/
4726
4727
if (!_ippFileReadToken(f, temp, sizeof(temp)))
4728
{
4729
print_fatal_error(data, "Missing FILE filename on line %d of \"%s\".", f->linenum, f->filename);
4730
return (0);
4731
}
4732
4733
_ippVarsExpand(vars, value, temp, sizeof(value));
4734
get_filename(f->filename, data->file, value, sizeof(data->file));
4735
4736
if (access(data->file, R_OK))
4737
{
4738
print_fatal_error(data, "Filename \"%s\" (mapped to \"%s\") on line %d of \"%s\" cannot be read.", value, data->file, f->linenum, f->filename);
4739
return (0);
4740
}
4741
}
4742
else if (!_cups_strcasecmp(token, "STATUS"))
4743
{
4744
/*
4745
* Status...
4746
*/
4747
4748
if (data->num_statuses >= (int)(sizeof(data->statuses) / sizeof(data->statuses[0])))
4749
{
4750
print_fatal_error(data, "Too many STATUS's on line %d of \"%s\".", f->linenum, f->filename);
4751
return (0);
4752
}
4753
4754
if (!_ippFileReadToken(f, temp, sizeof(temp)))
4755
{
4756
print_fatal_error(data, "Missing STATUS code on line %d of \"%s\".", f->linenum, f->filename);
4757
return (0);
4758
}
4759
4760
if ((data->statuses[data->num_statuses].status = ippErrorValue(temp)) == (ipp_status_t)-1 && (data->statuses[data->num_statuses].status = (ipp_status_t)strtol(temp, NULL, 0)) == 0)
4761
{
4762
print_fatal_error(data, "Bad STATUS code \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
4763
return (0);
4764
}
4765
4766
data->last_status = data->statuses + data->num_statuses;
4767
data->num_statuses ++;
4768
4769
data->last_status->define_match = NULL;
4770
data->last_status->define_no_match = NULL;
4771
data->last_status->if_defined = NULL;
4772
data->last_status->if_not_defined = NULL;
4773
data->last_status->repeat_limit = 1000;
4774
data->last_status->repeat_match = 0;
4775
data->last_status->repeat_no_match = 0;
4776
}
4777
else if (!_cups_strcasecmp(token, "EXPECT") || !_cups_strcasecmp(token, "EXPECT-ALL"))
4778
{
4779
/*
4780
* Expected attributes...
4781
*/
4782
4783
int expect_all = !_cups_strcasecmp(token, "EXPECT-ALL");
4784
4785
if (data->num_expects >= (int)(sizeof(data->expects) / sizeof(data->expects[0])))
4786
{
4787
print_fatal_error(data, "Too many EXPECT's on line %d of \"%s\".", f->linenum, f->filename);
4788
return (0);
4789
}
4790
4791
if (!_ippFileReadToken(f, name, sizeof(name)))
4792
{
4793
print_fatal_error(data, "Missing EXPECT name on line %d of \"%s\".", f->linenum, f->filename);
4794
return (0);
4795
}
4796
4797
data->last_expect = data->expects + data->num_expects;
4798
data->num_expects ++;
4799
4800
memset(data->last_expect, 0, sizeof(ipptool_expect_t));
4801
data->last_expect->repeat_limit = 1000;
4802
data->last_expect->expect_all = expect_all;
4803
4804
if (name[0] == '!')
4805
{
4806
data->last_expect->not_expect = 1;
4807
data->last_expect->name = strdup(name + 1);
4808
}
4809
else if (name[0] == '?')
4810
{
4811
data->last_expect->optional = 1;
4812
data->last_expect->name = strdup(name + 1);
4813
}
4814
else
4815
data->last_expect->name = strdup(name);
4816
}
4817
else if (!_cups_strcasecmp(token, "COUNT"))
4818
{
4819
int count; /* Count value */
4820
4821
if (!_ippFileReadToken(f, temp, sizeof(temp)))
4822
{
4823
print_fatal_error(data, "Missing COUNT number on line %d of \"%s\".", f->linenum, f->filename);
4824
return (0);
4825
}
4826
4827
if ((count = atoi(temp)) <= 0)
4828
{
4829
print_fatal_error(data, "Bad COUNT \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
4830
return (0);
4831
}
4832
4833
if (data->last_expect)
4834
{
4835
data->last_expect->count = count;
4836
}
4837
else
4838
{
4839
print_fatal_error(data, "COUNT without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
4840
return (0);
4841
}
4842
}
4843
else if (!_cups_strcasecmp(token, "DEFINE-MATCH"))
4844
{
4845
if (!_ippFileReadToken(f, temp, sizeof(temp)))
4846
{
4847
print_fatal_error(data, "Missing DEFINE-MATCH variable on line %d of \"%s\".", f->linenum, f->filename);
4848
return (0);
4849
}
4850
4851
if (data->last_expect)
4852
{
4853
data->last_expect->define_match = strdup(temp);
4854
}
4855
else if (data->last_status)
4856
{
4857
data->last_status->define_match = strdup(temp);
4858
}
4859
else
4860
{
4861
print_fatal_error(data, "DEFINE-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f->linenum, f->filename);
4862
return (0);
4863
}
4864
}
4865
else if (!_cups_strcasecmp(token, "DEFINE-NO-MATCH"))
4866
{
4867
if (!_ippFileReadToken(f, temp, sizeof(temp)))
4868
{
4869
print_fatal_error(data, "Missing DEFINE-NO-MATCH variable on line %d of \"%s\".", f->linenum, f->filename);
4870
return (0);
4871
}
4872
4873
if (data->last_expect)
4874
{
4875
data->last_expect->define_no_match = strdup(temp);
4876
}
4877
else if (data->last_status)
4878
{
4879
data->last_status->define_no_match = strdup(temp);
4880
}
4881
else
4882
{
4883
print_fatal_error(data, "DEFINE-NO-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f->linenum, f->filename);
4884
return (0);
4885
}
4886
}
4887
else if (!_cups_strcasecmp(token, "DEFINE-VALUE"))
4888
{
4889
if (!_ippFileReadToken(f, temp, sizeof(temp)))
4890
{
4891
print_fatal_error(data, "Missing DEFINE-VALUE variable on line %d of \"%s\".", f->linenum, f->filename);
4892
return (0);
4893
}
4894
4895
if (data->last_expect)
4896
{
4897
data->last_expect->define_value = strdup(temp);
4898
}
4899
else
4900
{
4901
print_fatal_error(data, "DEFINE-VALUE without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
4902
return (0);
4903
}
4904
}
4905
else if (!_cups_strcasecmp(token, "DISPLAY-MATCH"))
4906
{
4907
if (!_ippFileReadToken(f, temp, sizeof(temp)))
4908
{
4909
print_fatal_error(data, "Missing DISPLAY-MATCH mesaage on line %d of \"%s\".", f->linenum, f->filename);
4910
return (0);
4911
}
4912
4913
if (data->last_expect)
4914
{
4915
data->last_expect->display_match = strdup(temp);
4916
}
4917
else
4918
{
4919
print_fatal_error(data, "DISPLAY-MATCH without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
4920
return (0);
4921
}
4922
}
4923
else if (!_cups_strcasecmp(token, "OF-TYPE"))
4924
{
4925
if (!_ippFileReadToken(f, temp, sizeof(temp)))
4926
{
4927
print_fatal_error(data, "Missing OF-TYPE value tag(s) on line %d of \"%s\".", f->linenum, f->filename);
4928
return (0);
4929
}
4930
4931
if (data->last_expect)
4932
{
4933
data->last_expect->of_type = strdup(temp);
4934
}
4935
else
4936
{
4937
print_fatal_error(data, "OF-TYPE without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
4938
return (0);
4939
}
4940
}
4941
else if (!_cups_strcasecmp(token, "IN-GROUP"))
4942
{
4943
ipp_tag_t in_group; /* IN-GROUP value */
4944
4945
if (!_ippFileReadToken(f, temp, sizeof(temp)))
4946
{
4947
print_fatal_error(data, "Missing IN-GROUP group tag on line %d of \"%s\".", f->linenum, f->filename);
4948
return (0);
4949
}
4950
4951
if ((in_group = ippTagValue(temp)) == IPP_TAG_ZERO || in_group >= IPP_TAG_UNSUPPORTED_VALUE)
4952
{
4953
print_fatal_error(data, "Bad IN-GROUP group tag \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
4954
return (0);
4955
}
4956
else if (data->last_expect)
4957
{
4958
data->last_expect->in_group = in_group;
4959
}
4960
else
4961
{
4962
print_fatal_error(data, "IN-GROUP without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
4963
return (0);
4964
}
4965
}
4966
else if (!_cups_strcasecmp(token, "REPEAT-LIMIT"))
4967
{
4968
if (!_ippFileReadToken(f, temp, sizeof(temp)))
4969
{
4970
print_fatal_error(data, "Missing REPEAT-LIMIT value on line %d of \"%s\".", f->linenum, f->filename);
4971
return (0);
4972
}
4973
else if (atoi(temp) <= 0)
4974
{
4975
print_fatal_error(data, "Bad REPEAT-LIMIT value on line %d of \"%s\".", f->linenum, f->filename);
4976
return (0);
4977
}
4978
4979
if (data->last_status)
4980
{
4981
data->last_status->repeat_limit = atoi(temp);
4982
}
4983
else if (data->last_expect)
4984
{
4985
data->last_expect->repeat_limit = atoi(temp);
4986
}
4987
else
4988
{
4989
print_fatal_error(data, "REPEAT-LIMIT without a preceding EXPECT or STATUS on line %d of \"%s\".", f->linenum, f->filename);
4990
return (0);
4991
}
4992
}
4993
else if (!_cups_strcasecmp(token, "REPEAT-MATCH"))
4994
{
4995
if (data->last_status)
4996
{
4997
data->last_status->repeat_match = 1;
4998
}
4999
else if (data->last_expect)
5000
{
5001
data->last_expect->repeat_match = 1;
5002
}
5003
else
5004
{
5005
print_fatal_error(data, "REPEAT-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f->linenum, f->filename);
5006
return (0);
5007
}
5008
}
5009
else if (!_cups_strcasecmp(token, "REPEAT-NO-MATCH"))
5010
{
5011
if (data->last_status)
5012
{
5013
data->last_status->repeat_no_match = 1;
5014
}
5015
else if (data->last_expect)
5016
{
5017
data->last_expect->repeat_no_match = 1;
5018
}
5019
else
5020
{
5021
print_fatal_error(data, "REPEAT-NO-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f->linenum, f->filename);
5022
return (0);
5023
}
5024
}
5025
else if (!_cups_strcasecmp(token, "SAME-COUNT-AS"))
5026
{
5027
if (!_ippFileReadToken(f, temp, sizeof(temp)))
5028
{
5029
print_fatal_error(data, "Missing SAME-COUNT-AS name on line %d of \"%s\".", f->linenum, f->filename);
5030
return (0);
5031
}
5032
5033
if (data->last_expect)
5034
{
5035
data->last_expect->same_count_as = strdup(temp);
5036
}
5037
else
5038
{
5039
print_fatal_error(data, "SAME-COUNT-AS without a preceding EXPECT on line %d of \"%s\".", f->linenum, f->filename);
5040
return (0);
5041
}
5042
}
5043
else if (!_cups_strcasecmp(token, "IF-DEFINED"))
5044
{
5045
if (!_ippFileReadToken(f, temp, sizeof(temp)))
5046
{
5047
print_fatal_error(data, "Missing IF-DEFINED name on line %d of \"%s\".", f->linenum, f->filename);
5048
return (0);
5049
}
5050
5051
if (data->last_expect)
5052
{
5053
data->last_expect->if_defined = strdup(temp);
5054
}
5055
else if (data->last_status)
5056
{
5057
data->last_status->if_defined = strdup(temp);
5058
}
5059
else
5060
{
5061
print_fatal_error(data, "IF-DEFINED without a preceding EXPECT or STATUS on line %d of \"%s\".", f->linenum, f->filename);
5062
return (0);
5063
}
5064
}
5065
else if (!_cups_strcasecmp(token, "IF-NOT-DEFINED"))
5066
{
5067
if (!_ippFileReadToken(f, temp, sizeof(temp)))
5068
{
5069
print_fatal_error(data, "Missing IF-NOT-DEFINED name on line %d of \"%s\".", f->linenum, f->filename);
5070
return (0);
5071
}
5072
5073
if (data->last_expect)
5074
{
5075
data->last_expect->if_not_defined = strdup(temp);
5076
}
5077
else if (data->last_status)
5078
{
5079
data->last_status->if_not_defined = strdup(temp);
5080
}
5081
else
5082
{
5083
print_fatal_error(data, "IF-NOT-DEFINED without a preceding EXPECT or STATUS on line %d of \"%s\".", f->linenum, f->filename);
5084
return (0);
5085
}
5086
}
5087
else if (!_cups_strcasecmp(token, "WITH-DISTINCT-VALUES"))
5088
{
5089
if (data->last_expect)
5090
{
5091
data->last_expect->with_distinct = 1;
5092
}
5093
else
5094
{
5095
print_fatal_error(data, "%s without a preceding EXPECT on line %d of \"%s\".", token, f->linenum, f->filename);
5096
return (0);
5097
}
5098
}
5099
else if (!_cups_strcasecmp(token, "WITH-ALL-VALUES") ||
5100
!_cups_strcasecmp(token, "WITH-ALL-HOSTNAMES") ||
5101
!_cups_strcasecmp(token, "WITH-ALL-RESOURCES") ||
5102
!_cups_strcasecmp(token, "WITH-ALL-SCHEMES") ||
5103
!_cups_strcasecmp(token, "WITH-HOSTNAME") ||
5104
!_cups_strcasecmp(token, "WITH-RESOURCE") ||
5105
!_cups_strcasecmp(token, "WITH-SCHEME") ||
5106
!_cups_strcasecmp(token, "WITH-VALUE"))
5107
{
5108
off_t lastpos; /* Last file position */
5109
int lastline; /* Last line number */
5110
5111
if (data->last_expect)
5112
{
5113
if (!_cups_strcasecmp(token, "WITH-ALL-HOSTNAMES") || !_cups_strcasecmp(token, "WITH-HOSTNAME"))
5114
data->last_expect->with_flags = IPPTOOL_WITH_HOSTNAME;
5115
else if (!_cups_strcasecmp(token, "WITH-ALL-RESOURCES") || !_cups_strcasecmp(token, "WITH-RESOURCE"))
5116
data->last_expect->with_flags = IPPTOOL_WITH_RESOURCE;
5117
else if (!_cups_strcasecmp(token, "WITH-ALL-SCHEMES") || !_cups_strcasecmp(token, "WITH-SCHEME"))
5118
data->last_expect->with_flags = IPPTOOL_WITH_SCHEME;
5119
5120
if (!_cups_strncasecmp(token, "WITH-ALL-", 9))
5121
data->last_expect->with_flags |= IPPTOOL_WITH_ALL;
5122
}
5123
5124
if (!_ippFileReadToken(f, temp, sizeof(temp)))
5125
{
5126
print_fatal_error(data, "Missing %s value on line %d of \"%s\".", token, f->linenum, f->filename);
5127
return (0);
5128
}
5129
5130
/*
5131
* Read additional comma-delimited values - needed since legacy test files
5132
* will have unquoted WITH-VALUE values with commas...
5133
*/
5134
5135
ptr = temp + strlen(temp);
5136
5137
for (;;)
5138
{
5139
lastpos = cupsFileTell(f->fp);
5140
lastline = f->linenum;
5141
ptr += strlen(ptr);
5142
5143
if (!_ippFileReadToken(f, ptr, (sizeof(temp) - (size_t)(ptr - temp))))
5144
break;
5145
5146
if (!strcmp(ptr, ","))
5147
{
5148
/*
5149
* Append a value...
5150
*/
5151
5152
ptr += strlen(ptr);
5153
5154
if (!_ippFileReadToken(f, ptr, (sizeof(temp) - (size_t)(ptr - temp))))
5155
break;
5156
}
5157
else
5158
{
5159
/*
5160
* Not another value, stop here...
5161
*/
5162
5163
cupsFileSeek(f->fp, lastpos);
5164
f->linenum = lastline;
5165
*ptr = '\0';
5166
break;
5167
}
5168
}
5169
5170
if (data->last_expect)
5171
{
5172
/*
5173
* Expand any variables in the value and then save it.
5174
*/
5175
5176
_ippVarsExpand(vars, value, temp, sizeof(value));
5177
5178
ptr = value + strlen(value) - 1;
5179
5180
if (value[0] == '/' && ptr > value && *ptr == '/')
5181
{
5182
/*
5183
* WITH-VALUE is a POSIX extended regular expression.
5184
*/
5185
5186
data->last_expect->with_value = calloc(1, (size_t)(ptr - value));
5187
data->last_expect->with_flags |= IPPTOOL_WITH_REGEX;
5188
5189
if (data->last_expect->with_value)
5190
memcpy(data->last_expect->with_value, value + 1, (size_t)(ptr - value - 1));
5191
}
5192
else
5193
{
5194
/*
5195
* WITH-VALUE is a literal value...
5196
*/
5197
5198
for (ptr = value; *ptr; ptr ++)
5199
{
5200
if (*ptr == '\\' && ptr[1])
5201
{
5202
/*
5203
* Remove \ from \foo...
5204
*/
5205
5206
_cups_strcpy(ptr, ptr + 1);
5207
}
5208
}
5209
5210
data->last_expect->with_value = strdup(value);
5211
data->last_expect->with_flags |= IPPTOOL_WITH_LITERAL;
5212
}
5213
}
5214
else
5215
{
5216
print_fatal_error(data, "%s without a preceding EXPECT on line %d of \"%s\".", token, f->linenum, f->filename);
5217
return (0);
5218
}
5219
}
5220
else if (!_cups_strcasecmp(token, "WITH-VALUE-FROM"))
5221
{
5222
if (!_ippFileReadToken(f, temp, sizeof(temp)))
5223
{
5224
print_fatal_error(data, "Missing %s value on line %d of \"%s\".", token, f->linenum, f->filename);
5225
return (0);
5226
}
5227
5228
if (data->last_expect)
5229
{
5230
/*
5231
* Expand any variables in the value and then save it.
5232
*/
5233
5234
_ippVarsExpand(vars, value, temp, sizeof(value));
5235
5236
data->last_expect->with_value_from = strdup(value);
5237
data->last_expect->with_flags = IPPTOOL_WITH_LITERAL;
5238
}
5239
else
5240
{
5241
print_fatal_error(data, "%s without a preceding EXPECT on line %d of \"%s\".", token, f->linenum, f->filename);
5242
return (0);
5243
}
5244
}
5245
else if (!_cups_strcasecmp(token, "DISPLAY"))
5246
{
5247
/*
5248
* Display attributes...
5249
*/
5250
5251
if (data->num_displayed >= (int)(sizeof(data->displayed) / sizeof(data->displayed[0])))
5252
{
5253
print_fatal_error(data, "Too many DISPLAY's on line %d of \"%s\".", f->linenum, f->filename);
5254
return (0);
5255
}
5256
5257
if (!_ippFileReadToken(f, temp, sizeof(temp)))
5258
{
5259
print_fatal_error(data, "Missing DISPLAY name on line %d of \"%s\".", f->linenum, f->filename);
5260
return (0);
5261
}
5262
5263
data->displayed[data->num_displayed] = strdup(temp);
5264
data->num_displayed ++;
5265
}
5266
else
5267
{
5268
print_fatal_error(data, "Unexpected token %s seen on line %d of \"%s\".", token, f->linenum, f->filename);
5269
return (0);
5270
}
5271
}
5272
else
5273
{
5274
/*
5275
* Scan for the start of a test (open brace)...
5276
*/
5277
5278
if (!strcmp(token, "{"))
5279
{
5280
/*
5281
* Start new test...
5282
*/
5283
5284
if (data->show_header)
5285
{
5286
if (data->output == IPPTOOL_OUTPUT_PLIST)
5287
print_xml_header(data);
5288
5289
if (data->output == IPPTOOL_OUTPUT_TEST || (data->output == IPPTOOL_OUTPUT_PLIST && data->outfile != cupsFileStdout()))
5290
cupsFilePrintf(cupsFileStdout(), "\"%s\":\n", f->filename);
5291
5292
data->show_header = 0;
5293
}
5294
5295
data->compression[0] = '\0';
5296
data->delay = 0;
5297
data->num_expects = 0;
5298
data->last_expect = NULL;
5299
data->file[0] = '\0';
5300
data->ignore_errors = data->def_ignore_errors;
5301
strlcpy(data->name, f->filename, sizeof(data->name));
5302
if ((ptr = strrchr(data->name, '.')) != NULL)
5303
*ptr = '\0';
5304
data->repeat_interval = 5000000;
5305
strlcpy(data->resource, data->vars->resource, sizeof(data->resource));
5306
data->skip_previous = 0;
5307
data->pass_test = 0;
5308
data->skip_test = 0;
5309
data->num_statuses = 0;
5310
data->last_status = NULL;
5311
data->test_id[0] = '\0';
5312
data->transfer = data->def_transfer;
5313
data->version = data->def_version;
5314
5315
free(data->monitor_uri);
5316
data->monitor_uri = NULL;
5317
data->monitor_delay = 0;
5318
data->monitor_interval = 5000000;
5319
data->num_monitor_expects = 0;
5320
5321
_ippVarsSet(vars, "date-current", iso_date(ippTimeToDate(time(NULL))));
5322
5323
f->attrs = ippNew();
5324
f->group_tag = IPP_TAG_ZERO;
5325
}
5326
else if (!strcmp(token, "DEFINE"))
5327
{
5328
/*
5329
* DEFINE name value
5330
*/
5331
5332
if (_ippFileReadToken(f, name, sizeof(name)) && _ippFileReadToken(f, temp, sizeof(temp)))
5333
{
5334
_ippVarsSet(vars, "date-current", iso_date(ippTimeToDate(time(NULL))));
5335
_ippVarsExpand(vars, value, temp, sizeof(value));
5336
_ippVarsSet(vars, name, value);
5337
}
5338
else
5339
{
5340
print_fatal_error(data, "Missing DEFINE name and/or value on line %d of \"%s\".", f->linenum, f->filename);
5341
return (0);
5342
}
5343
}
5344
else if (!strcmp(token, "DEFINE-DEFAULT"))
5345
{
5346
/*
5347
* DEFINE-DEFAULT name value
5348
*/
5349
5350
if (_ippFileReadToken(f, name, sizeof(name)) && _ippFileReadToken(f, temp, sizeof(temp)))
5351
{
5352
if (!_ippVarsGet(vars, name))
5353
{
5354
_ippVarsSet(vars, "date-current", iso_date(ippTimeToDate(time(NULL))));
5355
_ippVarsExpand(vars, value, temp, sizeof(value));
5356
_ippVarsSet(vars, name, value);
5357
}
5358
}
5359
else
5360
{
5361
print_fatal_error(data, "Missing DEFINE-DEFAULT name and/or value on line %d of \"%s\".", f->linenum, f->filename);
5362
return (0);
5363
}
5364
}
5365
else if (!strcmp(token, "FILE-ID"))
5366
{
5367
/*
5368
* FILE-ID "string"
5369
*/
5370
5371
if (_ippFileReadToken(f, temp, sizeof(temp)))
5372
{
5373
_ippVarsSet(vars, "date-current", iso_date(ippTimeToDate(time(NULL))));
5374
_ippVarsExpand(vars, data->file_id, temp, sizeof(data->file_id));
5375
}
5376
else
5377
{
5378
print_fatal_error(data, "Missing FILE-ID value on line %d of \"%s\".", f->linenum, f->filename);
5379
return (0);
5380
}
5381
}
5382
else if (!strcmp(token, "IGNORE-ERRORS"))
5383
{
5384
/*
5385
* IGNORE-ERRORS yes
5386
* IGNORE-ERRORS no
5387
*/
5388
5389
if (_ippFileReadToken(f, temp, sizeof(temp)) && (!_cups_strcasecmp(temp, "yes") || !_cups_strcasecmp(temp, "no")))
5390
{
5391
data->def_ignore_errors = !_cups_strcasecmp(temp, "yes");
5392
}
5393
else
5394
{
5395
print_fatal_error(data, "Missing IGNORE-ERRORS value on line %d of \"%s\".", f->linenum, f->filename);
5396
return (0);
5397
}
5398
}
5399
else if (!strcmp(token, "INCLUDE"))
5400
{
5401
/*
5402
* INCLUDE "filename"
5403
* INCLUDE <filename>
5404
*/
5405
5406
if (_ippFileReadToken(f, temp, sizeof(temp)))
5407
{
5408
/*
5409
* Map the filename to and then run the tests...
5410
*/
5411
5412
ipptool_test_t inc_data; /* Data for included file */
5413
char filename[1024]; /* Mapped filename */
5414
5415
memcpy(&inc_data, data, sizeof(inc_data));
5416
inc_data.http = NULL;
5417
inc_data.pass = 1;
5418
inc_data.prev_pass = 1;
5419
inc_data.show_header = 1;
5420
5421
if (!do_tests(get_filename(f->filename, filename, temp, sizeof(filename)), &inc_data) && data->stop_after_include_error)
5422
{
5423
data->pass = data->prev_pass = 0;
5424
return (0);
5425
}
5426
}
5427
else
5428
{
5429
print_fatal_error(data, "Missing INCLUDE filename on line %d of \"%s\".", f->linenum, f->filename);
5430
return (0);
5431
}
5432
5433
data->show_header = 1;
5434
}
5435
else if (!strcmp(token, "INCLUDE-IF-DEFINED"))
5436
{
5437
/*
5438
* INCLUDE-IF-DEFINED name "filename"
5439
* INCLUDE-IF-DEFINED name <filename>
5440
*/
5441
5442
if (_ippFileReadToken(f, name, sizeof(name)) && _ippFileReadToken(f, temp, sizeof(temp)))
5443
{
5444
/*
5445
* Map the filename to and then run the tests...
5446
*/
5447
5448
ipptool_test_t inc_data; /* Data for included file */
5449
char filename[1024]; /* Mapped filename */
5450
5451
memcpy(&inc_data, data, sizeof(inc_data));
5452
inc_data.http = NULL;
5453
inc_data.pass = 1;
5454
inc_data.prev_pass = 1;
5455
inc_data.show_header = 1;
5456
5457
if (!do_tests(get_filename(f->filename, filename, temp, sizeof(filename)), &inc_data) && data->stop_after_include_error)
5458
{
5459
data->pass = data->prev_pass = 0;
5460
return (0);
5461
}
5462
}
5463
else
5464
{
5465
print_fatal_error(data, "Missing INCLUDE-IF-DEFINED name or filename on line %d of \"%s\".", f->linenum, f->filename);
5466
return (0);
5467
}
5468
5469
data->show_header = 1;
5470
}
5471
else if (!strcmp(token, "INCLUDE-IF-NOT-DEFINED"))
5472
{
5473
/*
5474
* INCLUDE-IF-NOT-DEFINED name "filename"
5475
* INCLUDE-IF-NOT-DEFINED name <filename>
5476
*/
5477
5478
if (_ippFileReadToken(f, name, sizeof(name)) && _ippFileReadToken(f, temp, sizeof(temp)))
5479
{
5480
/*
5481
* Map the filename to and then run the tests...
5482
*/
5483
5484
ipptool_test_t inc_data; /* Data for included file */
5485
char filename[1024]; /* Mapped filename */
5486
5487
memcpy(&inc_data, data, sizeof(inc_data));
5488
inc_data.http = NULL;
5489
inc_data.pass = 1;
5490
inc_data.prev_pass = 1;
5491
inc_data.show_header = 1;
5492
5493
if (!do_tests(get_filename(f->filename, filename, temp, sizeof(filename)), &inc_data) && data->stop_after_include_error)
5494
{
5495
data->pass = data->prev_pass = 0;
5496
return (0);
5497
}
5498
}
5499
else
5500
{
5501
print_fatal_error(data, "Missing INCLUDE-IF-NOT-DEFINED name or filename on line %d of \"%s\".", f->linenum, f->filename);
5502
return (0);
5503
}
5504
5505
data->show_header = 1;
5506
}
5507
else if (!strcmp(token, "SKIP-IF-DEFINED"))
5508
{
5509
/*
5510
* SKIP-IF-DEFINED variable
5511
*/
5512
5513
if (_ippFileReadToken(f, name, sizeof(name)))
5514
{
5515
if (_ippVarsGet(vars, name))
5516
data->skip_test = 1;
5517
}
5518
else
5519
{
5520
print_fatal_error(data, "Missing SKIP-IF-DEFINED variable on line %d of \"%s\".", f->linenum, f->filename);
5521
return (0);
5522
}
5523
}
5524
else if (!strcmp(token, "SKIP-IF-NOT-DEFINED"))
5525
{
5526
/*
5527
* SKIP-IF-NOT-DEFINED variable
5528
*/
5529
5530
if (_ippFileReadToken(f, name, sizeof(name)))
5531
{
5532
if (!_ippVarsGet(vars, name))
5533
data->skip_test = 1;
5534
}
5535
else
5536
{
5537
print_fatal_error(data, "Missing SKIP-IF-NOT-DEFINED variable on line %d of \"%s\".", f->linenum, f->filename);
5538
return (0);
5539
}
5540
}
5541
else if (!strcmp(token, "STOP-AFTER-INCLUDE-ERROR"))
5542
{
5543
/*
5544
* STOP-AFTER-INCLUDE-ERROR yes
5545
* STOP-AFTER-INCLUDE-ERROR no
5546
*/
5547
5548
if (_ippFileReadToken(f, temp, sizeof(temp)) && (!_cups_strcasecmp(temp, "yes") || !_cups_strcasecmp(temp, "no")))
5549
{
5550
data->stop_after_include_error = !_cups_strcasecmp(temp, "yes");
5551
}
5552
else
5553
{
5554
print_fatal_error(data, "Missing STOP-AFTER-INCLUDE-ERROR value on line %d of \"%s\".", f->linenum, f->filename);
5555
return (0);
5556
}
5557
}
5558
else if (!strcmp(token, "TRANSFER"))
5559
{
5560
/*
5561
* TRANSFER auto
5562
* TRANSFER chunked
5563
* TRANSFER length
5564
*/
5565
5566
if (_ippFileReadToken(f, temp, sizeof(temp)))
5567
{
5568
if (!strcmp(temp, "auto"))
5569
data->def_transfer = IPPTOOL_TRANSFER_AUTO;
5570
else if (!strcmp(temp, "chunked"))
5571
data->def_transfer = IPPTOOL_TRANSFER_CHUNKED;
5572
else if (!strcmp(temp, "length"))
5573
data->def_transfer = IPPTOOL_TRANSFER_LENGTH;
5574
else
5575
{
5576
print_fatal_error(data, "Bad TRANSFER value \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
5577
return (0);
5578
}
5579
}
5580
else
5581
{
5582
print_fatal_error(data, "Missing TRANSFER value on line %d of \"%s\".", f->linenum, f->filename);
5583
return (0);
5584
}
5585
}
5586
else if (!strcmp(token, "VERSION"))
5587
{
5588
if (_ippFileReadToken(f, temp, sizeof(temp)))
5589
{
5590
if (!strcmp(temp, "1.0"))
5591
data->def_version = 10;
5592
else if (!strcmp(temp, "1.1"))
5593
data->def_version = 11;
5594
else if (!strcmp(temp, "2.0"))
5595
data->def_version = 20;
5596
else if (!strcmp(temp, "2.1"))
5597
data->def_version = 21;
5598
else if (!strcmp(temp, "2.2"))
5599
data->def_version = 22;
5600
else
5601
{
5602
print_fatal_error(data, "Bad VERSION \"%s\" on line %d of \"%s\".", temp, f->linenum, f->filename);
5603
return (0);
5604
}
5605
}
5606
else
5607
{
5608
print_fatal_error(data, "Missing VERSION number on line %d of \"%s\".", f->linenum, f->filename);
5609
return (0);
5610
}
5611
}
5612
else
5613
{
5614
print_fatal_error(data, "Unexpected token %s seen on line %d of \"%s\".", token, f->linenum, f->filename);
5615
return (0);
5616
}
5617
}
5618
5619
return (1);
5620
}
5621
5622
5623
/*
5624
* 'usage()' - Show program usage.
5625
*/
5626
5627
static void
5628
usage(void)
5629
{
5630
_cupsLangPuts(stderr, _("Usage: ipptool [options] URI filename [ ... filenameN ]"));
5631
_cupsLangPuts(stderr, _("Options:"));
5632
_cupsLangPuts(stderr, _("--ippserver filename Produce ippserver attribute file"));
5633
_cupsLangPuts(stderr, _("--stop-after-include-error\n"
5634
" Stop tests after a failed INCLUDE"));
5635
_cupsLangPuts(stderr, _("--version Show version"));
5636
_cupsLangPuts(stderr, _("-4 Connect using IPv4"));
5637
_cupsLangPuts(stderr, _("-6 Connect using IPv6"));
5638
_cupsLangPuts(stderr, _("-C Send requests using chunking (default)"));
5639
_cupsLangPuts(stderr, _("-E Test with encryption using HTTP Upgrade to TLS"));
5640
_cupsLangPuts(stderr, _("-I Ignore errors"));
5641
_cupsLangPuts(stderr, _("-L Send requests using content-length"));
5642
_cupsLangPuts(stderr, _("-P filename.plist Produce XML plist to a file and test report to standard output"));
5643
_cupsLangPuts(stderr, _("-R Repeat tests on server-error-busy"));
5644
_cupsLangPuts(stderr, _("-S Test with encryption using HTTPS"));
5645
_cupsLangPuts(stderr, _("-T seconds Set the receive/send timeout in seconds"));
5646
_cupsLangPuts(stderr, _("-V version Set default IPP version"));
5647
_cupsLangPuts(stderr, _("-X Produce XML plist instead of plain text"));
5648
_cupsLangPuts(stderr, _("-c Produce CSV output"));
5649
_cupsLangPuts(stderr, _("-d name=value Set named variable to value"));
5650
_cupsLangPuts(stderr, _("-f filename Set default request filename"));
5651
_cupsLangPuts(stderr, _("-h Validate HTTP response headers"));
5652
_cupsLangPuts(stderr, _("-i seconds Repeat the last file with the given time interval"));
5653
_cupsLangPuts(stderr, _("-l Produce plain text output"));
5654
_cupsLangPuts(stderr, _("-n count Repeat the last file the given number of times"));
5655
_cupsLangPuts(stderr, _("-q Run silently"));
5656
_cupsLangPuts(stderr, _("-t Produce a test report"));
5657
_cupsLangPuts(stderr, _("-v Be verbose"));
5658
5659
exit(1);
5660
}
5661
5662
5663
/*
5664
* 'with_distinct_values()' - Verify that an attribute contains unique values.
5665
*/
5666
5667
static int // O - 1 if distinct, 0 if duplicate
5668
with_distinct_values(
5669
cups_array_t *errors, // I - Array of errors
5670
ipp_attribute_t *attr) // I - Attribute to test
5671
{
5672
int i, // Looping var
5673
count; // Number of values
5674
ipp_tag_t value_tag; // Value syntax
5675
const char *value; // Current value
5676
char buffer[8192]; // Temporary buffer
5677
cups_array_t *values; // Array of values as strings
5678
5679
5680
// If there is only 1 value, it must be distinct
5681
if ((count = ippGetCount(attr)) == 1)
5682
return (1);
5683
5684
// Only check integers, enums, rangeOfInteger, resolution, and nul-terminated
5685
// strings...
5686
switch (value_tag = ippGetValueTag(attr))
5687
{
5688
case IPP_TAG_INTEGER :
5689
case IPP_TAG_ENUM :
5690
case IPP_TAG_RANGE :
5691
case IPP_TAG_RESOLUTION :
5692
case IPP_TAG_KEYWORD :
5693
case IPP_TAG_URISCHEME :
5694
case IPP_TAG_CHARSET :
5695
case IPP_TAG_LANGUAGE :
5696
case IPP_TAG_MIMETYPE :
5697
case IPP_TAG_BEGIN_COLLECTION :
5698
break;
5699
5700
default :
5701
add_stringf(errors, "WITH-DISTINCT-VALUES %s not supported for 1setOf %s", ippGetName(attr), ippTagString(value_tag));
5702
return (0);
5703
}
5704
5705
// Collect values and determine they are all unique...
5706
values = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free);
5707
5708
for (i = 0; i < count; i ++)
5709
{
5710
switch (value_tag)
5711
{
5712
case IPP_TAG_INTEGER :
5713
case IPP_TAG_ENUM :
5714
snprintf(buffer, sizeof(buffer), "%d", ippGetInteger(attr, i));
5715
value = buffer;
5716
break;
5717
case IPP_TAG_RANGE :
5718
{
5719
int upper, lower = ippGetRange(attr, i, &upper);
5720
// Range values
5721
5722
snprintf(buffer, sizeof(buffer), "%d-%d", lower, upper);
5723
value = buffer;
5724
}
5725
break;
5726
case IPP_TAG_RESOLUTION :
5727
{
5728
ipp_res_t units; // Resolution units
5729
int yres, xres = ippGetResolution(attr, i, &yres, &units);
5730
// Resolution values
5731
5732
if (xres == yres)
5733
snprintf(buffer, sizeof(buffer), "%d%s", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
5734
else
5735
snprintf(buffer, sizeof(buffer), "%dx%d%s", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
5736
value = buffer;
5737
}
5738
break;
5739
case IPP_TAG_KEYWORD :
5740
case IPP_TAG_URISCHEME :
5741
case IPP_TAG_CHARSET :
5742
case IPP_TAG_LANGUAGE :
5743
case IPP_TAG_MIMETYPE :
5744
value = ippGetString(attr, i, NULL);
5745
break;
5746
case IPP_TAG_BEGIN_COLLECTION :
5747
{
5748
ipp_t *col = ippGetCollection(attr, i);
5749
// Collection value
5750
ipp_attribute_t *member; // Member attribute
5751
char *bufptr, // Pointer into buffer
5752
*bufend, // End of buffer
5753
prefix; // Prefix character
5754
5755
for (prefix = '{', bufptr = buffer, bufend = buffer + sizeof(buffer) - 2, member = ippFirstAttribute(col); member && bufptr < bufend; member = ippNextAttribute(col))
5756
{
5757
*bufptr++ = prefix;
5758
prefix = ' ';
5759
5760
ippAttributeString(member, bufptr, (size_t)(bufend - bufptr));
5761
bufptr += strlen(bufptr);
5762
}
5763
5764
*bufptr++ = '}';
5765
*bufptr = '\0';
5766
value = buffer;
5767
}
5768
break;
5769
default : // Should never happen
5770
value = "unsupported";
5771
break;
5772
}
5773
5774
if (cupsArrayFind(values, (void *)value))
5775
add_stringf(errors, "DUPLICATE: %s=%s", ippGetName(attr), value);
5776
else
5777
cupsArrayAdd(values, (void *)value);
5778
}
5779
5780
// Cleanup...
5781
i = cupsArrayCount(values) == count;
5782
cupsArrayDelete(values);
5783
5784
return (i);
5785
}
5786
5787
5788
/*
5789
* 'with_flags_string()' - Return the "WITH-xxx" predicate that corresponds to
5790
* the flags.
5791
*/
5792
5793
static const char * /* O - WITH-xxx string */
5794
with_flags_string(int flags) /* I - WITH flags */
5795
{
5796
if (flags & IPPTOOL_WITH_ALL)
5797
{
5798
if (flags & IPPTOOL_WITH_HOSTNAME)
5799
return ("WITH-ALL-HOSTNAMES");
5800
else if (flags & IPPTOOL_WITH_RESOURCE)
5801
return ("WITH-ALL-RESOURCES");
5802
else if (flags & IPPTOOL_WITH_SCHEME)
5803
return ("WITH-ALL-SCHEMES");
5804
else
5805
return ("WITH-ALL-VALUES");
5806
}
5807
else if (flags & IPPTOOL_WITH_HOSTNAME)
5808
return ("WITH-HOSTNAME");
5809
else if (flags & IPPTOOL_WITH_RESOURCE)
5810
return ("WITH-RESOURCE");
5811
else if (flags & IPPTOOL_WITH_SCHEME)
5812
return ("WITH-SCHEME");
5813
else
5814
return ("WITH-VALUE");
5815
}
5816
5817
5818
/*
5819
* 'with_value()' - Test a WITH-VALUE predicate.
5820
*/
5821
5822
static int /* O - 1 on match, 0 on non-match */
5823
with_value(ipptool_test_t *data, /* I - Test data */
5824
cups_array_t *errors, /* I - Errors array */
5825
char *value, /* I - Value string */
5826
int flags, /* I - Flags for match */
5827
ipp_attribute_t *attr, /* I - Attribute to compare */
5828
char *matchbuf, /* I - Buffer to hold matching value */
5829
size_t matchlen) /* I - Length of match buffer */
5830
{
5831
int i, /* Looping var */
5832
count, /* Number of values */
5833
match; /* Match? */
5834
char temp[1024], /* Temporary value string */
5835
*valptr; /* Pointer into value */
5836
const char *name; /* Attribute name */
5837
5838
5839
*matchbuf = '\0';
5840
match = (flags & IPPTOOL_WITH_ALL) ? 1 : 0;
5841
5842
/*
5843
* NULL matches everything.
5844
*/
5845
5846
if (!value || !*value)
5847
return (1);
5848
5849
/*
5850
* Compare the value string to the attribute value.
5851
*/
5852
5853
name = ippGetName(attr);
5854
count = ippGetCount(attr);
5855
5856
switch (ippGetValueTag(attr))
5857
{
5858
case IPP_TAG_INTEGER :
5859
case IPP_TAG_ENUM :
5860
for (i = 0; i < count; i ++)
5861
{
5862
char op, /* Comparison operator */
5863
*nextptr; /* Next pointer */
5864
int intvalue, /* Integer value */
5865
attrvalue = ippGetInteger(attr, i),
5866
/* Attribute value */
5867
valmatch = 0; /* Does the current value match? */
5868
5869
valptr = value;
5870
5871
while (isspace(*valptr & 255) || isdigit(*valptr & 255) ||
5872
*valptr == '-' || *valptr == ',' || *valptr == '<' ||
5873
*valptr == '=' || *valptr == '>')
5874
{
5875
op = '=';
5876
while (*valptr && !isdigit(*valptr & 255) && *valptr != '-')
5877
{
5878
if (*valptr == '<' || *valptr == '>' || *valptr == '=')
5879
op = *valptr;
5880
valptr ++;
5881
}
5882
5883
if (!*valptr)
5884
break;
5885
5886
intvalue = (int)strtol(valptr, &nextptr, 0);
5887
if (nextptr == valptr)
5888
break;
5889
valptr = nextptr;
5890
5891
if ((op == '=' && attrvalue == intvalue) ||
5892
(op == '<' && attrvalue < intvalue) ||
5893
(op == '>' && attrvalue > intvalue))
5894
{
5895
if (!matchbuf[0])
5896
snprintf(matchbuf, matchlen, "%d", attrvalue);
5897
5898
valmatch = 1;
5899
break;
5900
}
5901
}
5902
5903
if (flags & IPPTOOL_WITH_ALL)
5904
{
5905
if (!valmatch)
5906
{
5907
match = 0;
5908
break;
5909
}
5910
}
5911
else if (valmatch)
5912
{
5913
match = 1;
5914
break;
5915
}
5916
}
5917
5918
if (!match && errors)
5919
{
5920
for (i = 0; i < count; i ++)
5921
add_stringf(data->errors, "GOT: %s=%d", name, ippGetInteger(attr, i));
5922
}
5923
break;
5924
5925
case IPP_TAG_RANGE :
5926
for (i = 0; i < count; i ++)
5927
{
5928
char op, /* Comparison operator */
5929
*nextptr; /* Next pointer */
5930
int intvalue, /* Integer value */
5931
lower, /* Lower range */
5932
upper, /* Upper range */
5933
valmatch = 0; /* Does the current value match? */
5934
5935
lower = ippGetRange(attr, i, &upper);
5936
valptr = value;
5937
5938
while (isspace(*valptr & 255) || isdigit(*valptr & 255) ||
5939
*valptr == '-' || *valptr == ',' || *valptr == '<' ||
5940
*valptr == '=' || *valptr == '>')
5941
{
5942
op = '=';
5943
while (*valptr && !isdigit(*valptr & 255) && *valptr != '-')
5944
{
5945
if (*valptr == '<' || *valptr == '>' || *valptr == '=')
5946
op = *valptr;
5947
valptr ++;
5948
}
5949
5950
if (!*valptr)
5951
break;
5952
5953
intvalue = (int)strtol(valptr, &nextptr, 0);
5954
if (nextptr == valptr)
5955
break;
5956
valptr = nextptr;
5957
5958
if ((op == '=' && (lower == intvalue || upper == intvalue)) ||
5959
(op == '<' && upper < intvalue) ||
5960
(op == '>' && upper > intvalue))
5961
{
5962
if (!matchbuf[0])
5963
snprintf(matchbuf, matchlen, "%d-%d", lower, upper);
5964
5965
valmatch = 1;
5966
break;
5967
}
5968
}
5969
5970
if (flags & IPPTOOL_WITH_ALL)
5971
{
5972
if (!valmatch)
5973
{
5974
match = 0;
5975
break;
5976
}
5977
}
5978
else if (valmatch)
5979
{
5980
match = 1;
5981
break;
5982
}
5983
}
5984
5985
if (!match && errors)
5986
{
5987
for (i = 0; i < count; i ++)
5988
{
5989
int lower, upper; /* Range values */
5990
5991
lower = ippGetRange(attr, i, &upper);
5992
add_stringf(data->errors, "GOT: %s=%d-%d", name, lower, upper);
5993
}
5994
}
5995
break;
5996
5997
case IPP_TAG_BOOLEAN :
5998
for (i = 0; i < count; i ++)
5999
{
6000
if ((!strcmp(value, "true") || !strcmp(value, "1")) == ippGetBoolean(attr, i))
6001
{
6002
if (!matchbuf[0])
6003
strlcpy(matchbuf, value, matchlen);
6004
6005
if (!(flags & IPPTOOL_WITH_ALL))
6006
{
6007
match = 1;
6008
break;
6009
}
6010
}
6011
else if (flags & IPPTOOL_WITH_ALL)
6012
{
6013
match = 0;
6014
break;
6015
}
6016
}
6017
6018
if (!match && errors)
6019
{
6020
for (i = 0; i < count; i ++)
6021
add_stringf(data->errors, "GOT: %s=%s", name, ippGetBoolean(attr, i) ? "true" : "false");
6022
}
6023
break;
6024
6025
case IPP_TAG_RESOLUTION :
6026
for (i = 0; i < count; i ++)
6027
{
6028
int xres, yres; /* Resolution values */
6029
ipp_res_t units; /* Resolution units */
6030
6031
xres = ippGetResolution(attr, i, &yres, &units);
6032
if (xres == yres)
6033
snprintf(temp, sizeof(temp), "%d%s", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
6034
else
6035
snprintf(temp, sizeof(temp), "%dx%d%s", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
6036
6037
if (!strcmp(value, temp))
6038
{
6039
if (!matchbuf[0])
6040
strlcpy(matchbuf, value, matchlen);
6041
6042
if (!(flags & IPPTOOL_WITH_ALL))
6043
{
6044
match = 1;
6045
break;
6046
}
6047
}
6048
else if (flags & IPPTOOL_WITH_ALL)
6049
{
6050
match = 0;
6051
break;
6052
}
6053
}
6054
6055
if (!match && errors)
6056
{
6057
for (i = 0; i < count; i ++)
6058
{
6059
int xres, yres; /* Resolution values */
6060
ipp_res_t units; /* Resolution units */
6061
6062
xres = ippGetResolution(attr, i, &yres, &units);
6063
if (xres == yres)
6064
snprintf(temp, sizeof(temp), "%d%s", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
6065
else
6066
snprintf(temp, sizeof(temp), "%dx%d%s", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
6067
6068
if (strcmp(value, temp))
6069
add_stringf(data->errors, "GOT: %s=%s", name, temp);
6070
}
6071
}
6072
break;
6073
6074
case IPP_TAG_NOVALUE :
6075
case IPP_TAG_UNKNOWN :
6076
return (1);
6077
6078
case IPP_TAG_CHARSET :
6079
case IPP_TAG_KEYWORD :
6080
case IPP_TAG_LANGUAGE :
6081
case IPP_TAG_MIMETYPE :
6082
case IPP_TAG_NAME :
6083
case IPP_TAG_NAMELANG :
6084
case IPP_TAG_TEXT :
6085
case IPP_TAG_TEXTLANG :
6086
case IPP_TAG_URI :
6087
case IPP_TAG_URISCHEME :
6088
if (flags & IPPTOOL_WITH_REGEX)
6089
{
6090
/*
6091
* Value is an extended, case-sensitive POSIX regular expression...
6092
*/
6093
6094
regex_t re; /* Regular expression */
6095
6096
if ((i = regcomp(&re, value, REG_EXTENDED | REG_NOSUB)) != 0)
6097
{
6098
regerror(i, &re, temp, sizeof(temp));
6099
6100
print_fatal_error(data, "Unable to compile WITH-VALUE regular expression \"%s\" - %s", value, temp);
6101
return (0);
6102
}
6103
6104
/*
6105
* See if ALL of the values match the given regular expression.
6106
*/
6107
6108
for (i = 0; i < count; i ++)
6109
{
6110
if (!regexec(&re, get_string(attr, i, flags, temp, sizeof(temp)),
6111
0, NULL, 0))
6112
{
6113
if (!matchbuf[0])
6114
strlcpy(matchbuf, get_string(attr, i, flags, temp, sizeof(temp)), matchlen);
6115
6116
if (!(flags & IPPTOOL_WITH_ALL))
6117
{
6118
match = 1;
6119
break;
6120
}
6121
}
6122
else if (flags & IPPTOOL_WITH_ALL)
6123
{
6124
match = 0;
6125
break;
6126
}
6127
}
6128
6129
regfree(&re);
6130
}
6131
else if (ippGetValueTag(attr) == IPP_TAG_URI && !(flags & (IPPTOOL_WITH_SCHEME | IPPTOOL_WITH_HOSTNAME | IPPTOOL_WITH_RESOURCE)))
6132
{
6133
/*
6134
* Value is a literal URI string, see if the value(s) match...
6135
*/
6136
6137
for (i = 0; i < count; i ++)
6138
{
6139
if (!compare_uris(value, get_string(attr, i, flags, temp, sizeof(temp))))
6140
{
6141
if (!matchbuf[0])
6142
strlcpy(matchbuf, get_string(attr, i, flags, temp, sizeof(temp)), matchlen);
6143
6144
if (!(flags & IPPTOOL_WITH_ALL))
6145
{
6146
match = 1;
6147
break;
6148
}
6149
}
6150
else if (flags & IPPTOOL_WITH_ALL)
6151
{
6152
match = 0;
6153
break;
6154
}
6155
}
6156
}
6157
else
6158
{
6159
/*
6160
* Value is a literal string, see if the value(s) match...
6161
*/
6162
6163
for (i = 0; i < count; i ++)
6164
{
6165
int result;
6166
6167
switch (ippGetValueTag(attr))
6168
{
6169
case IPP_TAG_URI :
6170
/*
6171
* Some URI components are case-sensitive, some not...
6172
*/
6173
6174
if (flags & (IPPTOOL_WITH_SCHEME | IPPTOOL_WITH_HOSTNAME))
6175
result = _cups_strcasecmp(value, get_string(attr, i, flags, temp, sizeof(temp)));
6176
else
6177
result = strcmp(value, get_string(attr, i, flags, temp, sizeof(temp)));
6178
break;
6179
6180
case IPP_TAG_MIMETYPE :
6181
case IPP_TAG_NAME :
6182
case IPP_TAG_NAMELANG :
6183
case IPP_TAG_TEXT :
6184
case IPP_TAG_TEXTLANG :
6185
/*
6186
* mimeMediaType, nameWithoutLanguage, nameWithLanguage,
6187
* textWithoutLanguage, and textWithLanguage are defined to
6188
* be case-insensitive strings...
6189
*/
6190
6191
result = _cups_strcasecmp(value, get_string(attr, i, flags, temp, sizeof(temp)));
6192
break;
6193
6194
default :
6195
/*
6196
* Other string syntaxes are defined as lowercased so we use
6197
* case-sensitive comparisons to catch problems...
6198
*/
6199
6200
result = strcmp(value, get_string(attr, i, flags, temp, sizeof(temp)));
6201
break;
6202
}
6203
6204
if (!result)
6205
{
6206
if (!matchbuf[0])
6207
strlcpy(matchbuf, get_string(attr, i, flags, temp, sizeof(temp)), matchlen);
6208
6209
if (!(flags & IPPTOOL_WITH_ALL))
6210
{
6211
match = 1;
6212
break;
6213
}
6214
}
6215
else if (flags & IPPTOOL_WITH_ALL)
6216
{
6217
match = 0;
6218
break;
6219
}
6220
}
6221
}
6222
6223
if (!match && errors)
6224
{
6225
for (i = 0; i < count; i ++)
6226
add_stringf(data->errors, "GOT: %s=\"%s\"", name, ippGetString(attr, i, NULL));
6227
}
6228
break;
6229
6230
case IPP_TAG_STRING :
6231
if (flags & IPPTOOL_WITH_REGEX)
6232
{
6233
/*
6234
* Value is an extended, case-sensitive POSIX regular expression...
6235
*/
6236
6237
void *adata; /* Pointer to octetString data */
6238
int adatalen; /* Length of octetString */
6239
regex_t re; /* Regular expression */
6240
6241
if ((i = regcomp(&re, value, REG_EXTENDED | REG_NOSUB)) != 0)
6242
{
6243
regerror(i, &re, temp, sizeof(temp));
6244
6245
print_fatal_error(data, "Unable to compile WITH-VALUE regular expression \"%s\" - %s", value, temp);
6246
return (0);
6247
}
6248
6249
/*
6250
* See if ALL of the values match the given regular expression.
6251
*/
6252
6253
for (i = 0; i < count; i ++)
6254
{
6255
if ((adata = ippGetOctetString(attr, i, &adatalen)) == NULL || adatalen >= (int)sizeof(temp))
6256
{
6257
match = 0;
6258
break;
6259
}
6260
memcpy(temp, adata, (size_t)adatalen);
6261
temp[adatalen] = '\0';
6262
6263
if (!regexec(&re, temp, 0, NULL, 0))
6264
{
6265
if (!matchbuf[0])
6266
strlcpy(matchbuf, temp, matchlen);
6267
6268
if (!(flags & IPPTOOL_WITH_ALL))
6269
{
6270
match = 1;
6271
break;
6272
}
6273
}
6274
else if (flags & IPPTOOL_WITH_ALL)
6275
{
6276
match = 0;
6277
break;
6278
}
6279
}
6280
6281
regfree(&re);
6282
6283
if (!match && errors)
6284
{
6285
for (i = 0; i < count; i ++)
6286
{
6287
adata = ippGetOctetString(attr, i, &adatalen);
6288
copy_hex_string(temp, adata, adatalen, sizeof(temp));
6289
add_stringf(data->errors, "GOT: %s=\"%s\"", name, temp);
6290
}
6291
}
6292
}
6293
else
6294
{
6295
/*
6296
* Value is a literal or hex-encoded string...
6297
*/
6298
6299
unsigned char withdata[1023], /* WITH-VALUE data */
6300
*adata; /* Pointer to octetString data */
6301
int withlen, /* Length of WITH-VALUE data */
6302
adatalen; /* Length of octetString */
6303
6304
if (*value == '<')
6305
{
6306
/*
6307
* Grab hex-encoded value...
6308
*/
6309
6310
if ((withlen = (int)strlen(value)) & 1 || withlen > (int)(2 * (sizeof(withdata) + 1)))
6311
{
6312
print_fatal_error(data, "Bad WITH-VALUE hex value.");
6313
return (0);
6314
}
6315
6316
withlen = withlen / 2 - 1;
6317
6318
for (valptr = value + 1, adata = withdata; *valptr; valptr += 2)
6319
{
6320
int ch; /* Current character/byte */
6321
6322
if (isdigit(valptr[0]))
6323
ch = (valptr[0] - '0') << 4;
6324
else if (isalpha(valptr[0]))
6325
ch = (tolower(valptr[0]) - 'a' + 10) << 4;
6326
else
6327
break;
6328
6329
if (isdigit(valptr[1]))
6330
ch |= valptr[1] - '0';
6331
else if (isalpha(valptr[1]))
6332
ch |= tolower(valptr[1]) - 'a' + 10;
6333
else
6334
break;
6335
6336
*adata++ = (unsigned char)ch;
6337
}
6338
6339
if (*valptr)
6340
{
6341
print_fatal_error(data, "Bad WITH-VALUE hex value.");
6342
return (0);
6343
}
6344
}
6345
else
6346
{
6347
/*
6348
* Copy literal string value...
6349
*/
6350
6351
withlen = (int)strlen(value);
6352
6353
memcpy(withdata, value, (size_t)withlen);
6354
}
6355
6356
for (i = 0; i < count; i ++)
6357
{
6358
adata = ippGetOctetString(attr, i, &adatalen);
6359
6360
if (withlen == adatalen && !memcmp(withdata, adata, (size_t)withlen))
6361
{
6362
if (!matchbuf[0])
6363
copy_hex_string(matchbuf, adata, adatalen, matchlen);
6364
6365
if (!(flags & IPPTOOL_WITH_ALL))
6366
{
6367
match = 1;
6368
break;
6369
}
6370
}
6371
else if (flags & IPPTOOL_WITH_ALL)
6372
{
6373
match = 0;
6374
break;
6375
}
6376
}
6377
6378
if (!match && errors)
6379
{
6380
for (i = 0; i < count; i ++)
6381
{
6382
adata = ippGetOctetString(attr, i, &adatalen);
6383
copy_hex_string(temp, adata, adatalen, sizeof(temp));
6384
add_stringf(data->errors, "GOT: %s=\"%s\"", name, temp);
6385
}
6386
}
6387
}
6388
break;
6389
6390
default :
6391
break;
6392
}
6393
6394
return (match);
6395
}
6396
6397
6398
/*
6399
* 'with_value_from()' - Test a WITH-VALUE-FROM predicate.
6400
*/
6401
6402
static int /* O - 1 on match, 0 on non-match */
6403
with_value_from(
6404
cups_array_t *errors, /* I - Errors array */
6405
ipp_attribute_t *fromattr, /* I - "From" attribute */
6406
ipp_attribute_t *attr, /* I - Attribute to compare */
6407
char *matchbuf, /* I - Buffer to hold matching value */
6408
size_t matchlen) /* I - Length of match buffer */
6409
{
6410
int i, j, /* Looping vars */
6411
count = ippGetCount(attr), /* Number of attribute values */
6412
match = 1; /* Match? */
6413
6414
6415
*matchbuf = '\0';
6416
6417
/*
6418
* Compare the from value(s) to the attribute value(s)...
6419
*/
6420
6421
switch (ippGetValueTag(attr))
6422
{
6423
case IPP_TAG_INTEGER :
6424
if (ippGetValueTag(fromattr) != IPP_TAG_INTEGER && ippGetValueTag(fromattr) != IPP_TAG_RANGE)
6425
goto wrong_value_tag;
6426
6427
for (i = 0; i < count; i ++)
6428
{
6429
int value = ippGetInteger(attr, i);
6430
/* Current integer value */
6431
6432
if (ippContainsInteger(fromattr, value))
6433
{
6434
if (!matchbuf[0])
6435
snprintf(matchbuf, matchlen, "%d", value);
6436
}
6437
else
6438
{
6439
add_stringf(errors, "GOT: %s=%d", ippGetName(attr), value);
6440
match = 0;
6441
}
6442
}
6443
break;
6444
6445
case IPP_TAG_ENUM :
6446
if (ippGetValueTag(fromattr) != IPP_TAG_ENUM)
6447
goto wrong_value_tag;
6448
6449
for (i = 0; i < count; i ++)
6450
{
6451
int value = ippGetInteger(attr, i);
6452
/* Current integer value */
6453
6454
if (ippContainsInteger(fromattr, value))
6455
{
6456
if (!matchbuf[0])
6457
snprintf(matchbuf, matchlen, "%d", value);
6458
}
6459
else
6460
{
6461
add_stringf(errors, "GOT: %s=%d", ippGetName(attr), value);
6462
match = 0;
6463
}
6464
}
6465
break;
6466
6467
case IPP_TAG_RESOLUTION :
6468
if (ippGetValueTag(fromattr) != IPP_TAG_RESOLUTION)
6469
goto wrong_value_tag;
6470
6471
for (i = 0; i < count; i ++)
6472
{
6473
int xres, yres;
6474
ipp_res_t units;
6475
int fromcount = ippGetCount(fromattr);
6476
int fromxres, fromyres;
6477
ipp_res_t fromunits;
6478
6479
xres = ippGetResolution(attr, i, &yres, &units);
6480
6481
for (j = 0; j < fromcount; j ++)
6482
{
6483
fromxres = ippGetResolution(fromattr, j, &fromyres, &fromunits);
6484
if (fromxres == xres && fromyres == yres && fromunits == units)
6485
break;
6486
}
6487
6488
if (j < fromcount)
6489
{
6490
if (!matchbuf[0])
6491
{
6492
if (xres == yres)
6493
snprintf(matchbuf, matchlen, "%d%s", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
6494
else
6495
snprintf(matchbuf, matchlen, "%dx%d%s", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
6496
}
6497
}
6498
else
6499
{
6500
if (xres == yres)
6501
add_stringf(errors, "GOT: %s=%d%s", ippGetName(attr), xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
6502
else
6503
add_stringf(errors, "GOT: %s=%dx%d%s", ippGetName(attr), xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
6504
6505
match = 0;
6506
}
6507
}
6508
break;
6509
6510
case IPP_TAG_NOVALUE :
6511
case IPP_TAG_UNKNOWN :
6512
return (1);
6513
6514
case IPP_TAG_CHARSET :
6515
case IPP_TAG_KEYWORD :
6516
case IPP_TAG_LANGUAGE :
6517
case IPP_TAG_MIMETYPE :
6518
case IPP_TAG_NAME :
6519
case IPP_TAG_NAMELANG :
6520
case IPP_TAG_TEXT :
6521
case IPP_TAG_TEXTLANG :
6522
case IPP_TAG_URISCHEME :
6523
for (i = 0; i < count; i ++)
6524
{
6525
const char *value = ippGetString(attr, i, NULL);
6526
/* Current string value */
6527
6528
if (ippContainsString(fromattr, value))
6529
{
6530
if (!matchbuf[0])
6531
strlcpy(matchbuf, value, matchlen);
6532
}
6533
else
6534
{
6535
add_stringf(errors, "GOT: %s='%s'", ippGetName(attr), value);
6536
match = 0;
6537
}
6538
}
6539
break;
6540
6541
case IPP_TAG_URI :
6542
for (i = 0; i < count; i ++)
6543
{
6544
const char *value = ippGetString(attr, i, NULL);
6545
/* Current string value */
6546
int fromcount = ippGetCount(fromattr);
6547
6548
for (j = 0; j < fromcount; j ++)
6549
{
6550
if (!compare_uris(value, ippGetString(fromattr, j, NULL)))
6551
{
6552
if (!matchbuf[0])
6553
strlcpy(matchbuf, value, matchlen);
6554
break;
6555
}
6556
}
6557
6558
if (j >= fromcount)
6559
{
6560
add_stringf(errors, "GOT: %s='%s'", ippGetName(attr), value);
6561
match = 0;
6562
}
6563
}
6564
break;
6565
6566
default :
6567
match = 0;
6568
break;
6569
}
6570
6571
return (match);
6572
6573
/* value tag mismatch between fromattr and attr */
6574
wrong_value_tag :
6575
6576
add_stringf(errors, "GOT: %s OF-TYPE %s", ippGetName(attr), ippTagString(ippGetValueTag(attr)));
6577
6578
return (0);
6579
}
6580
6581