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