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