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