1/* 2 * "$Id: type.c 11093 2013-07-03 20:48:42Z msweet $" 3 * 4 * MIME typing routines for CUPS. 5 * 6 * Copyright 2007-2012 by Apple Inc. 7 * Copyright 1997-2006 by Easy Software Products, all rights reserved. 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 * Contents: 16 * 17 * mimeAddType() - Add a MIME type to a database. 18 * mimeAddTypeRule() - Add a detection rule for a file type. 19 * mimeFileType() - Determine the type of a file. 20 * mimeType() - Lookup a file type. 21 * mime_compare_types() - Compare two MIME super/type names. 22 * mime_check_rules() - Check each rule in a list. 23 * mime_patmatch() - Pattern matching. 24 */ 25 26/* 27 * Include necessary headers... 28 */ 29 30#include <cups/string-private.h> 31#include <cups/debug-private.h> 32#include <locale.h> 33#include "mime.h" 34 35 36/* 37 * Local types... 38 */ 39 40typedef struct _mime_filebuf_s /**** File buffer for MIME typing ****/ 41{ 42 cups_file_t *fp; /* File pointer */ 43 int offset, /* Offset in file */ 44 length; /* Length of buffered data */ 45 unsigned char buffer[MIME_MAX_BUFFER];/* Buffered data */ 46} _mime_filebuf_t; 47 48 49/* 50 * Local functions... 51 */ 52 53static int mime_compare_types(mime_type_t *t0, mime_type_t *t1); 54static int mime_check_rules(const char *filename, _mime_filebuf_t *fb, 55 mime_magic_t *rules); 56static int mime_patmatch(const char *s, const char *pat); 57 58 59/* 60 * Local globals... 61 */ 62 63#ifdef DEBUG 64static const char * const debug_ops[] = 65 { /* Test names... */ 66 "NOP", /* No operation */ 67 "AND", /* Logical AND of all children */ 68 "OR", /* Logical OR of all children */ 69 "MATCH", /* Filename match */ 70 "ASCII", /* ASCII characters in range */ 71 "PRINTABLE", /* Printable characters (32-255) */ 72 "STRING", /* String matches */ 73 "CHAR", /* Character/byte matches */ 74 "SHORT", /* Short/16-bit word matches */ 75 "INT", /* Integer/32-bit word matches */ 76 "LOCALE", /* Current locale matches string */ 77 "CONTAINS", /* File contains a string */ 78 "ISTRING" /* Case-insensitive string matches */ 79 }; 80#endif /* DEBUG */ 81 82 83/* 84 * 'mimeAddType()' - Add a MIME type to a database. 85 */ 86 87mime_type_t * /* O - New (or existing) MIME type */ 88mimeAddType(mime_t *mime, /* I - MIME database */ 89 const char *super, /* I - Super-type name */ 90 const char *type) /* I - Type name */ 91{ 92 mime_type_t *temp; /* New MIME type */ 93 size_t typelen; /* Length of type name */ 94 95 96 DEBUG_printf(("mimeAddType(mime=%p, super=\"%s\", type=\"%s\")", mime, super, 97 type)); 98 99 /* 100 * Range check input... 101 */ 102 103 if (!mime || !super || !type) 104 { 105 DEBUG_puts("1mimeAddType: Returning NULL (bad arguments)."); 106 return (NULL); 107 } 108 109 /* 110 * See if the type already exists; if so, return the existing type... 111 */ 112 113 if ((temp = mimeType(mime, super, type)) != NULL) 114 { 115 DEBUG_printf(("1mimeAddType: Returning %p (existing).", temp)); 116 return (temp); 117 } 118 119 /* 120 * The type doesn't exist; add it... 121 */ 122 123 if (!mime->types) 124 mime->types = cupsArrayNew((cups_array_func_t)mime_compare_types, NULL); 125 126 if (!mime->types) 127 { 128 DEBUG_puts("1mimeAddType: Returning NULL (no types)."); 129 return (NULL); 130 } 131 132 typelen = strlen(type) + 1; 133 134 if ((temp = calloc(1, sizeof(mime_type_t) - MIME_MAX_TYPE + typelen)) == NULL) 135 { 136 DEBUG_puts("1mimeAddType: Returning NULL (out of memory)."); 137 return (NULL); 138 } 139 140 strlcpy(temp->super, super, sizeof(temp->super)); 141 memcpy(temp->type, type, typelen); 142 temp->priority = 100; 143 144 cupsArrayAdd(mime->types, temp); 145 146 DEBUG_printf(("1mimeAddType: Returning %p (new).", temp)); 147 return (temp); 148} 149 150 151/* 152 * 'mimeAddTypeRule()' - Add a detection rule for a file type. 153 */ 154 155int /* O - 0 on success, -1 on failure */ 156mimeAddTypeRule(mime_type_t *mt, /* I - Type to add to */ 157 const char *rule) /* I - Rule to add */ 158{ 159 int num_values, /* Number of values seen */ 160 op, /* Operation code */ 161 logic, /* Logic for next rule */ 162 invert; /* Invert following rule? */ 163 char name[255], /* Name in rule string */ 164 value[3][255], /* Value in rule string */ 165 *ptr, /* Position in name or value */ 166 quote; /* Quote character */ 167 int length[3]; /* Length of each parameter */ 168 mime_magic_t *temp, /* New rule */ 169 *current; /* Current rule */ 170 171 172 DEBUG_printf(("mimeAddTypeRule(mt=%p(%s/%s), rule=\"%s\")", mt, 173 mt ? mt->super : "???", mt ? mt->type : "???", rule)); 174 175 /* 176 * Range check input... 177 */ 178 179 if (!mt || !rule) 180 return (-1); 181 182 /* 183 * Find the last rule in the top-level of the rules tree. 184 */ 185 186 for (current = mt->rules; current; current = current->next) 187 if (!current->next) 188 break; 189 190 /* 191 * Parse the rules string. Most rules are either a file extension or a 192 * comparison function: 193 * 194 * extension 195 * function(parameters) 196 */ 197 198 logic = MIME_MAGIC_NOP; 199 invert = 0; 200 201 while (*rule != '\0') 202 { 203 while (isspace(*rule & 255)) 204 rule ++; 205 206 if (*rule == '(') 207 { 208 DEBUG_puts("1mimeAddTypeRule: New parenthesis group"); 209 logic = MIME_MAGIC_NOP; 210 rule ++; 211 } 212 else if (*rule == ')') 213 { 214 DEBUG_puts("1mimeAddTypeRule: Close paren..."); 215 if (current == NULL || current->parent == NULL) 216 return (-1); 217 218 current = current->parent; 219 220 if (current->parent == NULL) 221 logic = MIME_MAGIC_OR; 222 else 223 logic = current->parent->op; 224 225 rule ++; 226 } 227 else if (*rule == '+' && current != NULL) 228 { 229 if (logic != MIME_MAGIC_AND && 230 current != NULL && current->prev != NULL) 231 { 232 /* 233 * OK, we have more than 1 rule in the current tree level... Make a 234 * new group tree and move the previous rule to it... 235 */ 236 237 if ((temp = calloc(1, sizeof(mime_magic_t))) == NULL) 238 return (-1); 239 240 temp->op = MIME_MAGIC_AND; 241 temp->child = current; 242 temp->parent = current->parent; 243 current->prev->next = temp; 244 temp->prev = current->prev; 245 246 current->prev = NULL; 247 current->parent = temp; 248 249 DEBUG_printf(("1mimeAddTypeRule: Creating new AND group %p.", temp)); 250 } 251 else if (current->parent) 252 { 253 DEBUG_printf(("1mimeAddTypeRule: Setting group %p op to AND.", 254 current->parent)); 255 current->parent->op = MIME_MAGIC_AND; 256 } 257 258 logic = MIME_MAGIC_AND; 259 rule ++; 260 } 261 else if (*rule == ',') 262 { 263 if (logic != MIME_MAGIC_OR && current != NULL) 264 { 265 /* 266 * OK, we have two possibilities; either this is the top-level rule or 267 * we have a bunch of AND rules at this level. 268 */ 269 270 if (current->parent == NULL) 271 { 272 /* 273 * This is the top-level rule; we have to move *all* of the AND rules 274 * down a level, as AND has precedence over OR. 275 */ 276 277 if ((temp = calloc(1, sizeof(mime_magic_t))) == NULL) 278 return (-1); 279 280 DEBUG_printf(("1mimeAddTypeRule: Creating new AND group %p inside OR " 281 "group.", temp)); 282 283 while (current->prev != NULL) 284 { 285 current->parent = temp; 286 current = current->prev; 287 } 288 289 current->parent = temp; 290 temp->op = MIME_MAGIC_AND; 291 temp->child = current; 292 293 mt->rules = current = temp; 294 } 295 else 296 { 297 /* 298 * This isn't the top rule, so go up one level... 299 */ 300 301 DEBUG_puts("1mimeAddTypeRule: Going up one level."); 302 current = current->parent; 303 } 304 } 305 306 logic = MIME_MAGIC_OR; 307 rule ++; 308 } 309 else if (*rule == '!') 310 { 311 DEBUG_puts("1mimeAddTypeRule: NOT"); 312 invert = 1; 313 rule ++; 314 } 315 else if (isalnum(*rule & 255)) 316 { 317 /* 318 * Read an extension name or a function... 319 */ 320 321 ptr = name; 322 while (isalnum(*rule & 255) && (ptr - name) < (sizeof(name) - 1)) 323 *ptr++ = *rule++; 324 325 *ptr = '\0'; 326 327 if (*rule == '(') 328 { 329 /* 330 * Read function parameters... 331 */ 332 333 rule ++; 334 for (num_values = 0; 335 num_values < (sizeof(value) / sizeof(value[0])); 336 num_values ++) 337 { 338 ptr = value[num_values]; 339 340 while ((ptr - value[num_values]) < (sizeof(value[0]) - 1) && 341 *rule != '\0' && *rule != ',' && *rule != ')') 342 { 343 if (isspace(*rule & 255)) 344 { 345 /* 346 * Ignore whitespace... 347 */ 348 349 rule ++; 350 continue; 351 } 352 else if (*rule == '\"' || *rule == '\'') 353 { 354 /* 355 * Copy quoted strings literally... 356 */ 357 358 quote = *rule++; 359 360 while (*rule != '\0' && *rule != quote && 361 (ptr - value[num_values]) < (sizeof(value[0]) - 1)) 362 *ptr++ = *rule++; 363 364 if (*rule == quote) 365 rule ++; 366 else 367 return (-1); 368 } 369 else if (*rule == '<') 370 { 371 rule ++; 372 373 while (*rule != '>' && *rule != '\0' && 374 (ptr - value[num_values]) < (sizeof(value[0]) - 1)) 375 { 376 if (isxdigit(rule[0] & 255) && isxdigit(rule[1] & 255)) 377 { 378 if (isdigit(*rule)) 379 *ptr = (*rule++ - '0') << 4; 380 else 381 *ptr = (tolower(*rule++) - 'a' + 10) << 4; 382 383 if (isdigit(*rule)) 384 *ptr++ |= *rule++ - '0'; 385 else 386 *ptr++ |= tolower(*rule++) - 'a' + 10; 387 } 388 else 389 return (-1); 390 } 391 392 if (*rule == '>') 393 rule ++; 394 else 395 return (-1); 396 } 397 else 398 *ptr++ = *rule++; 399 } 400 401 *ptr = '\0'; 402 length[num_values] = ptr - value[num_values]; 403 404 if (*rule != ',') 405 { 406 num_values ++; 407 break; 408 } 409 410 rule ++; 411 } 412 413 if (*rule != ')') 414 return (-1); 415 416 rule ++; 417 418 /* 419 * Figure out the function... 420 */ 421 422 if (!strcmp(name, "match")) 423 op = MIME_MAGIC_MATCH; 424 else if (!strcmp(name, "ascii")) 425 op = MIME_MAGIC_ASCII; 426 else if (!strcmp(name, "printable")) 427 op = MIME_MAGIC_PRINTABLE; 428 else if (!strcmp(name, "string")) 429 op = MIME_MAGIC_STRING; 430 else if (!strcmp(name, "istring")) 431 op = MIME_MAGIC_ISTRING; 432 else if (!strcmp(name, "char")) 433 op = MIME_MAGIC_CHAR; 434 else if (!strcmp(name, "short")) 435 op = MIME_MAGIC_SHORT; 436 else if (!strcmp(name, "int")) 437 op = MIME_MAGIC_INT; 438 else if (!strcmp(name, "locale")) 439 op = MIME_MAGIC_LOCALE; 440 else if (!strcmp(name, "contains")) 441 op = MIME_MAGIC_CONTAINS; 442 else if (!strcmp(name, "priority") && num_values == 1) 443 { 444 mt->priority = atoi(value[0]); 445 continue; 446 } 447 else 448 return (-1); 449 } 450 else 451 { 452 /* 453 * This is just a filename match on the extension... 454 */ 455 456 snprintf(value[0], sizeof(value[0]), "*.%s", name); 457 length[0] = strlen(value[0]); 458 op = MIME_MAGIC_MATCH; 459 } 460 461 /* 462 * Add a rule for this operation. 463 */ 464 465 if ((temp = calloc(1, sizeof(mime_magic_t))) == NULL) 466 return (-1); 467 468 temp->invert = invert; 469 if (current != NULL) 470 { 471 temp->parent = current->parent; 472 current->next = temp; 473 } 474 else 475 mt->rules = temp; 476 477 temp->prev = current; 478 479 if (logic == MIME_MAGIC_NOP) 480 { 481 /* 482 * Add parenthetical grouping... 483 */ 484 485 DEBUG_printf(("1mimeAddTypeRule: Making new OR group %p for " 486 "parenthesis.", temp)); 487 488 temp->op = MIME_MAGIC_OR; 489 490 if ((temp->child = calloc(1, sizeof(mime_magic_t))) == NULL) 491 return (-1); 492 493 temp->child->parent = temp; 494 temp->child->invert = temp->invert; 495 temp->invert = 0; 496 497 temp = temp->child; 498 logic = MIME_MAGIC_OR; 499 } 500 501 DEBUG_printf(("1mimeAddTypeRule: Adding %p: %s, op=MIME_MAGIC_%s(%d), " 502 "logic=MIME_MAGIC_%s, invert=%d.", temp, name, 503 debug_ops[op], op, debug_ops[logic], invert)); 504 505 /* 506 * Fill in data for the rule... 507 */ 508 509 current = temp; 510 temp->op = op; 511 invert = 0; 512 513 switch (op) 514 { 515 case MIME_MAGIC_MATCH : 516 if (length[0] > (sizeof(temp->value.matchv) - 1)) 517 return (-1); 518 strlcpy(temp->value.matchv, value[0], sizeof(temp->value.matchv)); 519 break; 520 case MIME_MAGIC_ASCII : 521 case MIME_MAGIC_PRINTABLE : 522 temp->offset = strtol(value[0], NULL, 0); 523 temp->length = strtol(value[1], NULL, 0); 524 if (temp->length > MIME_MAX_BUFFER) 525 temp->length = MIME_MAX_BUFFER; 526 break; 527 case MIME_MAGIC_STRING : 528 case MIME_MAGIC_ISTRING : 529 temp->offset = strtol(value[0], NULL, 0); 530 if (length[1] > sizeof(temp->value.stringv)) 531 return (-1); 532 temp->length = length[1]; 533 memcpy(temp->value.stringv, value[1], length[1]); 534 break; 535 case MIME_MAGIC_CHAR : 536 temp->offset = strtol(value[0], NULL, 0); 537 if (length[1] == 1) 538 temp->value.charv = value[1][0]; 539 else 540 temp->value.charv = (unsigned char)strtol(value[1], NULL, 0); 541 542 DEBUG_printf(("1mimeAddTypeRule: CHAR(%d,0x%02x)", temp->offset, 543 temp->value.charv)); 544 break; 545 case MIME_MAGIC_SHORT : 546 temp->offset = strtol(value[0], NULL, 0); 547 temp->value.shortv = (unsigned short)strtol(value[1], NULL, 0); 548 break; 549 case MIME_MAGIC_INT : 550 temp->offset = strtol(value[0], NULL, 0); 551 temp->value.intv = (unsigned)strtol(value[1], NULL, 0); 552 break; 553 case MIME_MAGIC_LOCALE : 554 if (length[0] > (sizeof(temp->value.localev) - 1)) 555 return (-1); 556 557 strlcpy(temp->value.localev, value[0], sizeof(temp->value.localev)); 558 break; 559 case MIME_MAGIC_CONTAINS : 560 temp->offset = strtol(value[0], NULL, 0); 561 temp->region = strtol(value[1], NULL, 0); 562 if (length[2] > sizeof(temp->value.stringv)) 563 return (-1); 564 temp->length = length[2]; 565 memcpy(temp->value.stringv, value[2], length[2]); 566 break; 567 } 568 } 569 else 570 break; 571 } 572 573 return (0); 574} 575 576 577/* 578 * 'mimeFileType()' - Determine the type of a file. 579 */ 580 581mime_type_t * /* O - Type of file */ 582mimeFileType(mime_t *mime, /* I - MIME database */ 583 const char *pathname, /* I - Name of file to check on disk */ 584 const char *filename, /* I - Original filename or NULL */ 585 int *compression) /* O - Is the file compressed? */ 586{ 587 _mime_filebuf_t fb; /* File buffer */ 588 const char *base; /* Base filename of file */ 589 mime_type_t *type, /* File type */ 590 *best; /* Best match */ 591 592 593 DEBUG_printf(("mimeFileType(mime=%p, pathname=\"%s\", filename=\"%s\", " 594 "compression=%p)", mime, pathname, filename, compression)); 595 596 /* 597 * Range check input parameters... 598 */ 599 600 if (!mime || !pathname) 601 { 602 DEBUG_puts("1mimeFileType: Returning NULL."); 603 return (NULL); 604 } 605 606 /* 607 * Try to open the file... 608 */ 609 610 if ((fb.fp = cupsFileOpen(pathname, "r")) == NULL) 611 { 612 DEBUG_printf(("1mimeFileType: Unable to open \"%s\": %s", pathname, 613 strerror(errno))); 614 DEBUG_puts("1mimeFileType: Returning NULL."); 615 return (NULL); 616 } 617 618 fb.offset = -1; 619 fb.length = 0; 620 621 /* 622 * Figure out the base filename (without directory portion)... 623 */ 624 625 if (filename) 626 { 627 if ((base = strrchr(filename, '/')) != NULL) 628 base ++; 629 else 630 base = filename; 631 } 632 else if ((base = strrchr(pathname, '/')) != NULL) 633 base ++; 634 else 635 base = pathname; 636 637 /* 638 * Then check it against all known types... 639 */ 640 641 for (type = (mime_type_t *)cupsArrayFirst(mime->types), best = NULL; 642 type; 643 type = (mime_type_t *)cupsArrayNext(mime->types)) 644 if (mime_check_rules(base, &fb, type->rules)) 645 { 646 if (!best || type->priority > best->priority) 647 best = type; 648 } 649 650 /* 651 * Finally, close the file and return a match (if any)... 652 */ 653 654 if (compression) 655 { 656 *compression = cupsFileCompression(fb.fp); 657 DEBUG_printf(("1mimeFileType: *compression=%d", *compression)); 658 } 659 660 cupsFileClose(fb.fp); 661 662 DEBUG_printf(("1mimeFileType: Returning %p(%s/%s).", best, 663 best ? best->super : "???", best ? best->type : "???")); 664 return (best); 665} 666 667 668/* 669 * 'mimeType()' - Lookup a file type. 670 */ 671 672mime_type_t * /* O - Matching file type definition */ 673mimeType(mime_t *mime, /* I - MIME database */ 674 const char *super, /* I - Super-type name */ 675 const char *type) /* I - Type name */ 676{ 677 mime_type_t key, /* MIME type search key */ 678 *mt; /* Matching type */ 679 680 681 DEBUG_printf(("mimeType(mime=%p, super=\"%s\", type=\"%s\")", mime, super, 682 type)); 683 684 /* 685 * Range check input... 686 */ 687 688 if (!mime || !super || !type) 689 { 690 DEBUG_puts("1mimeType: Returning NULL."); 691 return (NULL); 692 } 693 694 /* 695 * Lookup the type in the array... 696 */ 697 698 strlcpy(key.super, super, sizeof(key.super)); 699 strlcpy(key.type, type, sizeof(key.type)); 700 701 mt = (mime_type_t *)cupsArrayFind(mime->types, &key); 702 DEBUG_printf(("1mimeType: Returning %p.", mt)); 703 return (mt); 704} 705 706 707/* 708 * 'mime_compare_types()' - Compare two MIME super/type names. 709 */ 710 711static int /* O - Result of comparison */ 712mime_compare_types(mime_type_t *t0, /* I - First type */ 713 mime_type_t *t1) /* I - Second type */ 714{ 715 int i; /* Result of comparison */ 716 717 718 if ((i = _cups_strcasecmp(t0->super, t1->super)) == 0) 719 i = _cups_strcasecmp(t0->type, t1->type); 720 721 return (i); 722} 723 724 725/* 726 * 'mime_check_rules()' - Check each rule in a list. 727 */ 728 729static int /* O - 1 if match, 0 if no match */ 730mime_check_rules( 731 const char *filename, /* I - Filename */ 732 _mime_filebuf_t *fb, /* I - File to check */ 733 mime_magic_t *rules) /* I - Rules to check */ 734{ 735 int n; /* Looping var */ 736 int region; /* Region to look at */ 737 int logic, /* Logic to apply */ 738 result, /* Result of test */ 739 intv; /* Integer value */ 740 short shortv; /* Short value */ 741 unsigned char *bufptr; /* Pointer into buffer */ 742 743 744 DEBUG_printf(("4mime_check_rules(filename=\"%s\", fb=%p, rules=%p)", filename, 745 fb, rules)); 746 747 if (rules == NULL) 748 return (0); 749 750 if (rules->parent == NULL) 751 logic = MIME_MAGIC_OR; 752 else 753 logic = rules->parent->op; 754 755 result = 0; 756 757 while (rules != NULL) 758 { 759 /* 760 * Compute the result of this rule... 761 */ 762 763 switch (rules->op) 764 { 765 case MIME_MAGIC_MATCH : 766 result = mime_patmatch(filename, rules->value.matchv); 767 break; 768 769 case MIME_MAGIC_ASCII : 770 /* 771 * Load the buffer if necessary... 772 */ 773 774 if (fb->offset < 0 || rules->offset < fb->offset || 775 (rules->offset + rules->length) > (fb->offset + fb->length)) 776 { 777 /* 778 * Reload file buffer... 779 */ 780 781 cupsFileSeek(fb->fp, rules->offset); 782 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer, 783 sizeof(fb->buffer)); 784 fb->offset = rules->offset; 785 } 786 787 /* 788 * Test for ASCII printable characters plus standard control chars. 789 */ 790 791 if ((rules->offset + rules->length) > (fb->offset + fb->length)) 792 n = fb->offset + fb->length - rules->offset; 793 else 794 n = rules->length; 795 796 bufptr = fb->buffer + rules->offset - fb->offset; 797 while (n > 0) 798 if ((*bufptr >= 32 && *bufptr <= 126) || 799 (*bufptr >= 8 && *bufptr <= 13) || 800 *bufptr == 26 || *bufptr == 27) 801 { 802 n --; 803 bufptr ++; 804 } 805 else 806 break; 807 808 result = (n == 0); 809 break; 810 811 case MIME_MAGIC_PRINTABLE : 812 /* 813 * Load the buffer if necessary... 814 */ 815 816 if (fb->offset < 0 || rules->offset < fb->offset || 817 (rules->offset + rules->length) > (fb->offset + fb->length)) 818 { 819 /* 820 * Reload file buffer... 821 */ 822 823 cupsFileSeek(fb->fp, rules->offset); 824 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer, 825 sizeof(fb->buffer)); 826 fb->offset = rules->offset; 827 } 828 829 /* 830 * Test for 8-bit printable characters plus standard control chars. 831 */ 832 833 if ((rules->offset + rules->length) > (fb->offset + fb->length)) 834 n = fb->offset + fb->length - rules->offset; 835 else 836 n = rules->length; 837 838 bufptr = fb->buffer + rules->offset - fb->offset; 839 840 while (n > 0) 841 if (*bufptr >= 128 || 842 (*bufptr >= 32 && *bufptr <= 126) || 843 (*bufptr >= 8 && *bufptr <= 13) || 844 *bufptr == 26 || *bufptr == 27) 845 { 846 n --; 847 bufptr ++; 848 } 849 else 850 break; 851 852 result = (n == 0); 853 break; 854 855 case MIME_MAGIC_STRING : 856 DEBUG_printf(("5mime_check_rules: string(%d, \"%s\")", rules->offset, 857 rules->value.stringv)); 858 859 /* 860 * Load the buffer if necessary... 861 */ 862 863 if (fb->offset < 0 || rules->offset < fb->offset || 864 (rules->offset + rules->length) > (fb->offset + fb->length)) 865 { 866 /* 867 * Reload file buffer... 868 */ 869 870 cupsFileSeek(fb->fp, rules->offset); 871 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer, 872 sizeof(fb->buffer)); 873 fb->offset = rules->offset; 874 875 DEBUG_printf(("5mime_check_rules: loaded %d byte fb->buffer at %d, starts " 876 "with \"%c%c%c%c\".", 877 fb->length, fb->offset, fb->buffer[0], fb->buffer[1], 878 fb->buffer[2], fb->buffer[3])); 879 } 880 881 /* 882 * Compare the buffer against the string. If the file is too 883 * short then don't compare - it can't match... 884 */ 885 886 if ((rules->offset + rules->length) > (fb->offset + fb->length)) 887 result = 0; 888 else 889 result = (memcmp(fb->buffer + rules->offset - fb->offset, 890 rules->value.stringv, rules->length) == 0); 891 DEBUG_printf(("5mime_check_rules: result=%d", result)); 892 break; 893 894 case MIME_MAGIC_ISTRING : 895 /* 896 * Load the buffer if necessary... 897 */ 898 899 if (fb->offset < 0 || rules->offset < fb->offset || 900 (rules->offset + rules->length) > (fb->offset + fb->length)) 901 { 902 /* 903 * Reload file buffer... 904 */ 905 906 cupsFileSeek(fb->fp, rules->offset); 907 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer, 908 sizeof(fb->buffer)); 909 fb->offset = rules->offset; 910 } 911 912 /* 913 * Compare the buffer against the string. If the file is too 914 * short then don't compare - it can't match... 915 */ 916 917 if ((rules->offset + rules->length) > (fb->offset + fb->length)) 918 result = 0; 919 else 920 result = (_cups_strncasecmp((char *)fb->buffer + rules->offset - 921 fb->offset, 922 rules->value.stringv, rules->length) == 0); 923 break; 924 925 case MIME_MAGIC_CHAR : 926 /* 927 * Load the buffer if necessary... 928 */ 929 930 if (fb->offset < 0 || rules->offset < fb->offset) 931 { 932 /* 933 * Reload file buffer... 934 */ 935 936 cupsFileSeek(fb->fp, rules->offset); 937 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer, 938 sizeof(fb->buffer)); 939 fb->offset = rules->offset; 940 } 941 942 /* 943 * Compare the character values; if the file is too short, it 944 * can't match... 945 */ 946 947 if (fb->length < 1) 948 result = 0; 949 else 950 result = (fb->buffer[rules->offset - fb->offset] == 951 rules->value.charv); 952 break; 953 954 case MIME_MAGIC_SHORT : 955 /* 956 * Load the buffer if necessary... 957 */ 958 959 if (fb->offset < 0 || rules->offset < fb->offset || 960 (rules->offset + 2) > (fb->offset + fb->length)) 961 { 962 /* 963 * Reload file buffer... 964 */ 965 966 cupsFileSeek(fb->fp, rules->offset); 967 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer, 968 sizeof(fb->buffer)); 969 fb->offset = rules->offset; 970 } 971 972 /* 973 * Compare the short values; if the file is too short, it 974 * can't match... 975 */ 976 977 if (fb->length < 2) 978 result = 0; 979 else 980 { 981 bufptr = fb->buffer + rules->offset - fb->offset; 982 shortv = (bufptr[0] << 8) | bufptr[1]; 983 result = (shortv == rules->value.shortv); 984 } 985 break; 986 987 case MIME_MAGIC_INT : 988 /* 989 * Load the buffer if necessary... 990 */ 991 992 if (fb->offset < 0 || rules->offset < fb->offset || 993 (rules->offset + 4) > (fb->offset + fb->length)) 994 { 995 /* 996 * Reload file buffer... 997 */ 998 999 cupsFileSeek(fb->fp, rules->offset); 1000 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer, 1001 sizeof(fb->buffer)); 1002 fb->offset = rules->offset; 1003 } 1004 1005 /* 1006 * Compare the int values; if the file is too short, it 1007 * can't match... 1008 */ 1009 1010 if (fb->length < 4) 1011 result = 0; 1012 else 1013 { 1014 bufptr = fb->buffer + rules->offset - fb->offset; 1015 intv = (((((bufptr[0] << 8) | bufptr[1]) << 8) | 1016 bufptr[2]) << 8) | bufptr[3]; 1017 result = (intv == rules->value.intv); 1018 } 1019 break; 1020 1021 case MIME_MAGIC_LOCALE : 1022#if defined(WIN32) || defined(__EMX__) || defined(__APPLE__) 1023 result = (strcmp(rules->value.localev, 1024 setlocale(LC_ALL, "")) == 0); 1025#else 1026 result = (strcmp(rules->value.localev, 1027 setlocale(LC_MESSAGES, "")) == 0); 1028#endif /* __APPLE__ */ 1029 break; 1030 1031 case MIME_MAGIC_CONTAINS : 1032 /* 1033 * Load the buffer if necessary... 1034 */ 1035 1036 if (fb->offset < 0 || rules->offset < fb->offset || 1037 (rules->offset + rules->region) > (fb->offset + fb->length)) 1038 { 1039 /* 1040 * Reload file buffer... 1041 */ 1042 1043 cupsFileSeek(fb->fp, rules->offset); 1044 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer, 1045 sizeof(fb->buffer)); 1046 fb->offset = rules->offset; 1047 } 1048 1049 /* 1050 * Compare the buffer against the string. If the file is too 1051 * short then don't compare - it can't match... 1052 */ 1053 1054 if ((rules->offset + rules->length) > (fb->offset + fb->length)) 1055 result = 0; 1056 else 1057 { 1058 if (fb->length > rules->region) 1059 region = rules->region - rules->length; 1060 else 1061 region = fb->length - rules->length; 1062 1063 for (n = 0; n < region; n ++) 1064 if ((result = (memcmp(fb->buffer + rules->offset - fb->offset + n, 1065 rules->value.stringv, 1066 rules->length) == 0)) != 0) 1067 break; 1068 } 1069 break; 1070 1071 default : 1072 if (rules->child != NULL) 1073 result = mime_check_rules(filename, fb, rules->child); 1074 else 1075 result = 0; 1076 break; 1077 } 1078 1079 /* 1080 * If the logic is inverted, invert the result... 1081 */ 1082 1083 if (rules->invert) 1084 result = !result; 1085 1086 /* 1087 * OK, now if the current logic is OR and this result is true, the this 1088 * rule set is true. If the current logic is AND and this result is false, 1089 * the the rule set is false... 1090 */ 1091 1092 DEBUG_printf(("5mime_check_rules: result of test %p (MIME_MAGIC_%s) is %d", 1093 rules, debug_ops[rules->op], result)); 1094 1095 if ((result && logic == MIME_MAGIC_OR) || 1096 (!result && logic == MIME_MAGIC_AND)) 1097 return (result); 1098 1099 /* 1100 * Otherwise the jury is still out on this one, so move to the next rule. 1101 */ 1102 1103 rules = rules->next; 1104 } 1105 1106 return (result); 1107} 1108 1109 1110/* 1111 * 'mime_patmatch()' - Pattern matching. 1112 */ 1113 1114static int /* O - 1 if match, 0 if no match */ 1115mime_patmatch(const char *s, /* I - String to match against */ 1116 const char *pat) /* I - Pattern to match against */ 1117{ 1118 /* 1119 * Range check the input... 1120 */ 1121 1122 if (s == NULL || pat == NULL) 1123 return (0); 1124 1125 /* 1126 * Loop through the pattern and match strings, and stop if we come to a 1127 * point where the strings don't match or we find a complete match. 1128 */ 1129 1130 while (*s != '\0' && *pat != '\0') 1131 { 1132 if (*pat == '*') 1133 { 1134 /* 1135 * Wildcard - 0 or more characters... 1136 */ 1137 1138 pat ++; 1139 if (*pat == '\0') 1140 return (1); /* Last pattern char is *, so everything matches... */ 1141 1142 /* 1143 * Test all remaining combinations until we get to the end of the string. 1144 */ 1145 1146 while (*s != '\0') 1147 { 1148 if (mime_patmatch(s, pat)) 1149 return (1); 1150 1151 s ++; 1152 } 1153 } 1154 else if (*pat == '?') 1155 { 1156 /* 1157 * Wildcard - 1 character... 1158 */ 1159 1160 pat ++; 1161 s ++; 1162 continue; 1163 } 1164 else if (*pat == '[') 1165 { 1166 /* 1167 * Match a character from the input set [chars]... 1168 */ 1169 1170 pat ++; 1171 while (*pat != ']' && *pat != '\0') 1172 if (*s == *pat) 1173 break; 1174 else 1175 pat ++; 1176 1177 if (*pat == ']' || *pat == '\0') 1178 return (0); 1179 1180 while (*pat != ']' && *pat != '\0') 1181 pat ++; 1182 1183 if (*pat == ']') 1184 pat ++; 1185 1186 continue; 1187 } 1188 else if (*pat == '\\') 1189 { 1190 /* 1191 * Handle quoted characters... 1192 */ 1193 1194 pat ++; 1195 } 1196 1197 /* 1198 * Stop if the pattern and string don't match... 1199 */ 1200 1201 if (*pat++ != *s++) 1202 return (0); 1203 } 1204 1205 /* 1206 * Done parsing the pattern and string; return 1 if the last character 1207 * matches and 0 otherwise... 1208 */ 1209 1210 return (*s == *pat); 1211} 1212 1213 1214/* 1215 * End of "$Id: type.c 11093 2013-07-03 20:48:42Z msweet $". 1216 */ 1217