]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/type.c
<rdar://problem/11131245> PDF detection in cups needs to be tightened up
[thirdparty/cups.git] / scheduler / type.c
1 /*
2 * "$Id$"
3 *
4 * MIME typing routines for CUPS.
5 *
6 * Copyright 2007-2013 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
40 typedef 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
53 static int mime_compare_types(mime_type_t *t0, mime_type_t *t1);
54 static int mime_check_rules(const char *filename, _mime_filebuf_t *fb,
55 mime_magic_t *rules);
56 static int mime_patmatch(const char *s, const char *pat);
57
58
59 /*
60 * Local globals...
61 */
62
63 #ifdef DEBUG
64 static 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
87 mime_type_t * /* O - New (or existing) MIME type */
88 mimeAddType(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
155 int /* O - 0 on success, -1 on failure */
156 mimeAddTypeRule(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, "regex"))
429 op = MIME_MAGIC_REGEX;
430 else if (!strcmp(name, "string"))
431 op = MIME_MAGIC_STRING;
432 else if (!strcmp(name, "istring"))
433 op = MIME_MAGIC_ISTRING;
434 else if (!strcmp(name, "char"))
435 op = MIME_MAGIC_CHAR;
436 else if (!strcmp(name, "short"))
437 op = MIME_MAGIC_SHORT;
438 else if (!strcmp(name, "int"))
439 op = MIME_MAGIC_INT;
440 else if (!strcmp(name, "locale"))
441 op = MIME_MAGIC_LOCALE;
442 else if (!strcmp(name, "contains"))
443 op = MIME_MAGIC_CONTAINS;
444 else if (!strcmp(name, "priority") && num_values == 1)
445 {
446 mt->priority = atoi(value[0]);
447 continue;
448 }
449 else
450 return (-1);
451 }
452 else
453 {
454 /*
455 * This is just a filename match on the extension...
456 */
457
458 snprintf(value[0], sizeof(value[0]), "*.%s", name);
459 length[0] = strlen(value[0]);
460 op = MIME_MAGIC_MATCH;
461 }
462
463 /*
464 * Add a rule for this operation.
465 */
466
467 if ((temp = calloc(1, sizeof(mime_magic_t))) == NULL)
468 return (-1);
469
470 temp->invert = invert;
471 if (current != NULL)
472 {
473 temp->parent = current->parent;
474 current->next = temp;
475 }
476 else
477 mt->rules = temp;
478
479 temp->prev = current;
480
481 if (logic == MIME_MAGIC_NOP)
482 {
483 /*
484 * Add parenthetical grouping...
485 */
486
487 DEBUG_printf(("1mimeAddTypeRule: Making new OR group %p for "
488 "parenthesis.", temp));
489
490 temp->op = MIME_MAGIC_OR;
491
492 if ((temp->child = calloc(1, sizeof(mime_magic_t))) == NULL)
493 return (-1);
494
495 temp->child->parent = temp;
496 temp->child->invert = temp->invert;
497 temp->invert = 0;
498
499 temp = temp->child;
500 logic = MIME_MAGIC_OR;
501 }
502
503 DEBUG_printf(("1mimeAddTypeRule: Adding %p: %s, op=MIME_MAGIC_%s(%d), "
504 "logic=MIME_MAGIC_%s, invert=%d.", temp, name,
505 debug_ops[op], op, debug_ops[logic], invert));
506
507 /*
508 * Fill in data for the rule...
509 */
510
511 current = temp;
512 temp->op = op;
513 invert = 0;
514
515 switch (op)
516 {
517 case MIME_MAGIC_MATCH :
518 if (length[0] > (sizeof(temp->value.matchv) - 1))
519 return (-1);
520 strlcpy(temp->value.matchv, value[0], sizeof(temp->value.matchv));
521 break;
522 case MIME_MAGIC_ASCII :
523 case MIME_MAGIC_PRINTABLE :
524 temp->offset = strtol(value[0], NULL, 0);
525 temp->length = strtol(value[1], NULL, 0);
526 if (temp->length > MIME_MAX_BUFFER)
527 temp->length = MIME_MAX_BUFFER;
528 break;
529 case MIME_MAGIC_REGEX :
530 temp->offset = strtol(value[0], NULL, 0);
531 temp->length = MIME_MAX_BUFFER;
532 if (regcomp(&(temp->value.rev), value[1], REG_NOSUB | REG_EXTENDED))
533 return (-1);
534 break;
535 case MIME_MAGIC_STRING :
536 case MIME_MAGIC_ISTRING :
537 temp->offset = strtol(value[0], NULL, 0);
538 if (length[1] > sizeof(temp->value.stringv))
539 return (-1);
540 temp->length = length[1];
541 memcpy(temp->value.stringv, value[1], length[1]);
542 break;
543 case MIME_MAGIC_CHAR :
544 temp->offset = strtol(value[0], NULL, 0);
545 if (length[1] == 1)
546 temp->value.charv = value[1][0];
547 else
548 temp->value.charv = (unsigned char)strtol(value[1], NULL, 0);
549
550 DEBUG_printf(("1mimeAddTypeRule: CHAR(%d,0x%02x)", temp->offset,
551 temp->value.charv));
552 break;
553 case MIME_MAGIC_SHORT :
554 temp->offset = strtol(value[0], NULL, 0);
555 temp->value.shortv = (unsigned short)strtol(value[1], NULL, 0);
556 break;
557 case MIME_MAGIC_INT :
558 temp->offset = strtol(value[0], NULL, 0);
559 temp->value.intv = (unsigned)strtol(value[1], NULL, 0);
560 break;
561 case MIME_MAGIC_LOCALE :
562 if (length[0] > (sizeof(temp->value.localev) - 1))
563 return (-1);
564
565 strlcpy(temp->value.localev, value[0], sizeof(temp->value.localev));
566 break;
567 case MIME_MAGIC_CONTAINS :
568 temp->offset = strtol(value[0], NULL, 0);
569 temp->region = strtol(value[1], NULL, 0);
570 if (length[2] > sizeof(temp->value.stringv))
571 return (-1);
572 temp->length = length[2];
573 memcpy(temp->value.stringv, value[2], length[2]);
574 break;
575 }
576 }
577 else
578 break;
579 }
580
581 return (0);
582 }
583
584
585 /*
586 * 'mimeFileType()' - Determine the type of a file.
587 */
588
589 mime_type_t * /* O - Type of file */
590 mimeFileType(mime_t *mime, /* I - MIME database */
591 const char *pathname, /* I - Name of file to check on disk */
592 const char *filename, /* I - Original filename or NULL */
593 int *compression) /* O - Is the file compressed? */
594 {
595 _mime_filebuf_t fb; /* File buffer */
596 const char *base; /* Base filename of file */
597 mime_type_t *type, /* File type */
598 *best; /* Best match */
599
600
601 DEBUG_printf(("mimeFileType(mime=%p, pathname=\"%s\", filename=\"%s\", "
602 "compression=%p)", mime, pathname, filename, compression));
603
604 /*
605 * Range check input parameters...
606 */
607
608 if (!mime || !pathname)
609 {
610 DEBUG_puts("1mimeFileType: Returning NULL.");
611 return (NULL);
612 }
613
614 /*
615 * Try to open the file...
616 */
617
618 if ((fb.fp = cupsFileOpen(pathname, "r")) == NULL)
619 {
620 DEBUG_printf(("1mimeFileType: Unable to open \"%s\": %s", pathname,
621 strerror(errno)));
622 DEBUG_puts("1mimeFileType: Returning NULL.");
623 return (NULL);
624 }
625
626 fb.offset = -1;
627 fb.length = 0;
628
629 /*
630 * Figure out the base filename (without directory portion)...
631 */
632
633 if (filename)
634 {
635 if ((base = strrchr(filename, '/')) != NULL)
636 base ++;
637 else
638 base = filename;
639 }
640 else if ((base = strrchr(pathname, '/')) != NULL)
641 base ++;
642 else
643 base = pathname;
644
645 /*
646 * Then check it against all known types...
647 */
648
649 for (type = (mime_type_t *)cupsArrayFirst(mime->types), best = NULL;
650 type;
651 type = (mime_type_t *)cupsArrayNext(mime->types))
652 if (mime_check_rules(base, &fb, type->rules))
653 {
654 if (!best || type->priority > best->priority)
655 best = type;
656 }
657
658 /*
659 * Finally, close the file and return a match (if any)...
660 */
661
662 if (compression)
663 {
664 *compression = cupsFileCompression(fb.fp);
665 DEBUG_printf(("1mimeFileType: *compression=%d", *compression));
666 }
667
668 cupsFileClose(fb.fp);
669
670 DEBUG_printf(("1mimeFileType: Returning %p(%s/%s).", best,
671 best ? best->super : "???", best ? best->type : "???"));
672 return (best);
673 }
674
675
676 /*
677 * 'mimeType()' - Lookup a file type.
678 */
679
680 mime_type_t * /* O - Matching file type definition */
681 mimeType(mime_t *mime, /* I - MIME database */
682 const char *super, /* I - Super-type name */
683 const char *type) /* I - Type name */
684 {
685 mime_type_t key, /* MIME type search key */
686 *mt; /* Matching type */
687
688
689 DEBUG_printf(("mimeType(mime=%p, super=\"%s\", type=\"%s\")", mime, super,
690 type));
691
692 /*
693 * Range check input...
694 */
695
696 if (!mime || !super || !type)
697 {
698 DEBUG_puts("1mimeType: Returning NULL.");
699 return (NULL);
700 }
701
702 /*
703 * Lookup the type in the array...
704 */
705
706 strlcpy(key.super, super, sizeof(key.super));
707 strlcpy(key.type, type, sizeof(key.type));
708
709 mt = (mime_type_t *)cupsArrayFind(mime->types, &key);
710 DEBUG_printf(("1mimeType: Returning %p.", mt));
711 return (mt);
712 }
713
714
715 /*
716 * 'mime_compare_types()' - Compare two MIME super/type names.
717 */
718
719 static int /* O - Result of comparison */
720 mime_compare_types(mime_type_t *t0, /* I - First type */
721 mime_type_t *t1) /* I - Second type */
722 {
723 int i; /* Result of comparison */
724
725
726 if ((i = _cups_strcasecmp(t0->super, t1->super)) == 0)
727 i = _cups_strcasecmp(t0->type, t1->type);
728
729 return (i);
730 }
731
732
733 /*
734 * 'mime_check_rules()' - Check each rule in a list.
735 */
736
737 static int /* O - 1 if match, 0 if no match */
738 mime_check_rules(
739 const char *filename, /* I - Filename */
740 _mime_filebuf_t *fb, /* I - File to check */
741 mime_magic_t *rules) /* I - Rules to check */
742 {
743 int n; /* Looping var */
744 int region; /* Region to look at */
745 int logic, /* Logic to apply */
746 result, /* Result of test */
747 intv; /* Integer value */
748 short shortv; /* Short value */
749 unsigned char *bufptr; /* Pointer into buffer */
750
751
752 DEBUG_printf(("4mime_check_rules(filename=\"%s\", fb=%p, rules=%p)", filename,
753 fb, rules));
754
755 if (rules == NULL)
756 return (0);
757
758 if (rules->parent == NULL)
759 logic = MIME_MAGIC_OR;
760 else
761 logic = rules->parent->op;
762
763 result = 0;
764
765 while (rules != NULL)
766 {
767 /*
768 * Compute the result of this rule...
769 */
770
771 switch (rules->op)
772 {
773 case MIME_MAGIC_MATCH :
774 result = mime_patmatch(filename, rules->value.matchv);
775 break;
776
777 case MIME_MAGIC_ASCII :
778 /*
779 * Load the buffer if necessary...
780 */
781
782 if (fb->offset < 0 || rules->offset < fb->offset ||
783 (rules->offset + rules->length) > (fb->offset + fb->length))
784 {
785 /*
786 * Reload file buffer...
787 */
788
789 cupsFileSeek(fb->fp, rules->offset);
790 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
791 sizeof(fb->buffer));
792 fb->offset = rules->offset;
793 }
794
795 /*
796 * Test for ASCII printable characters plus standard control chars.
797 */
798
799 if ((rules->offset + rules->length) > (fb->offset + fb->length))
800 n = fb->offset + fb->length - rules->offset;
801 else
802 n = rules->length;
803
804 bufptr = fb->buffer + rules->offset - fb->offset;
805 while (n > 0)
806 if ((*bufptr >= 32 && *bufptr <= 126) ||
807 (*bufptr >= 8 && *bufptr <= 13) ||
808 *bufptr == 26 || *bufptr == 27)
809 {
810 n --;
811 bufptr ++;
812 }
813 else
814 break;
815
816 result = (n == 0);
817 break;
818
819 case MIME_MAGIC_PRINTABLE :
820 /*
821 * Load the buffer if necessary...
822 */
823
824 if (fb->offset < 0 || rules->offset < fb->offset ||
825 (rules->offset + rules->length) > (fb->offset + fb->length))
826 {
827 /*
828 * Reload file buffer...
829 */
830
831 cupsFileSeek(fb->fp, rules->offset);
832 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
833 sizeof(fb->buffer));
834 fb->offset = rules->offset;
835 }
836
837 /*
838 * Test for 8-bit printable characters plus standard control chars.
839 */
840
841 if ((rules->offset + rules->length) > (fb->offset + fb->length))
842 n = fb->offset + fb->length - rules->offset;
843 else
844 n = rules->length;
845
846 bufptr = fb->buffer + rules->offset - fb->offset;
847
848 while (n > 0)
849 if (*bufptr >= 128 ||
850 (*bufptr >= 32 && *bufptr <= 126) ||
851 (*bufptr >= 8 && *bufptr <= 13) ||
852 *bufptr == 26 || *bufptr == 27)
853 {
854 n --;
855 bufptr ++;
856 }
857 else
858 break;
859
860 result = (n == 0);
861 break;
862
863 case MIME_MAGIC_REGEX :
864 DEBUG_printf(("5mime_check_rules: regex(%d, \"%s\")", rules->offset,
865 rules->value.stringv));
866
867 /*
868 * Load the buffer if necessary...
869 */
870
871 if (fb->offset < 0 || rules->offset < fb->offset ||
872 (rules->offset + rules->length) > (fb->offset + fb->length))
873 {
874 /*
875 * Reload file buffer...
876 */
877
878 cupsFileSeek(fb->fp, rules->offset);
879 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
880 sizeof(fb->buffer));
881 fb->offset = rules->offset;
882
883 DEBUG_printf(("5mime_check_rules: loaded %d byte fb->buffer at %d, starts "
884 "with \"%c%c%c%c\".",
885 fb->length, fb->offset, fb->buffer[0], fb->buffer[1],
886 fb->buffer[2], fb->buffer[3]));
887 }
888
889 /*
890 * Compare the buffer against the string. If the file is too
891 * short then don't compare - it can't match...
892 */
893
894 {
895 char temp[MIME_MAX_BUFFER + 1];
896 /* Temporary buffer */
897
898 memcpy(temp, fb->buffer, fb->length);
899 temp[fb->length] = '\0';
900 result = !regexec(&(rules->value.rev), temp, 0, NULL, 0);
901 }
902
903 DEBUG_printf(("5mime_check_rules: result=%d", result));
904 break;
905
906 case MIME_MAGIC_STRING :
907 DEBUG_printf(("5mime_check_rules: string(%d, \"%s\")", rules->offset,
908 rules->value.stringv));
909
910 /*
911 * Load the buffer if necessary...
912 */
913
914 if (fb->offset < 0 || rules->offset < fb->offset ||
915 (rules->offset + rules->length) > (fb->offset + fb->length))
916 {
917 /*
918 * Reload file buffer...
919 */
920
921 cupsFileSeek(fb->fp, rules->offset);
922 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
923 sizeof(fb->buffer));
924 fb->offset = rules->offset;
925
926 DEBUG_printf(("5mime_check_rules: loaded %d byte fb->buffer at %d, starts "
927 "with \"%c%c%c%c\".",
928 fb->length, fb->offset, fb->buffer[0], fb->buffer[1],
929 fb->buffer[2], fb->buffer[3]));
930 }
931
932 /*
933 * Compare the buffer against the string. If the file is too
934 * short then don't compare - it can't match...
935 */
936
937 if ((rules->offset + rules->length) > (fb->offset + fb->length))
938 result = 0;
939 else
940 result = (memcmp(fb->buffer + rules->offset - fb->offset,
941 rules->value.stringv, rules->length) == 0);
942 DEBUG_printf(("5mime_check_rules: result=%d", result));
943 break;
944
945 case MIME_MAGIC_ISTRING :
946 /*
947 * Load the buffer if necessary...
948 */
949
950 if (fb->offset < 0 || rules->offset < fb->offset ||
951 (rules->offset + rules->length) > (fb->offset + fb->length))
952 {
953 /*
954 * Reload file buffer...
955 */
956
957 cupsFileSeek(fb->fp, rules->offset);
958 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
959 sizeof(fb->buffer));
960 fb->offset = rules->offset;
961 }
962
963 /*
964 * Compare the buffer against the string. If the file is too
965 * short then don't compare - it can't match...
966 */
967
968 if ((rules->offset + rules->length) > (fb->offset + fb->length))
969 result = 0;
970 else
971 result = (_cups_strncasecmp((char *)fb->buffer + rules->offset -
972 fb->offset,
973 rules->value.stringv, rules->length) == 0);
974 break;
975
976 case MIME_MAGIC_CHAR :
977 /*
978 * Load the buffer if necessary...
979 */
980
981 if (fb->offset < 0 || rules->offset < fb->offset)
982 {
983 /*
984 * Reload file buffer...
985 */
986
987 cupsFileSeek(fb->fp, rules->offset);
988 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
989 sizeof(fb->buffer));
990 fb->offset = rules->offset;
991 }
992
993 /*
994 * Compare the character values; if the file is too short, it
995 * can't match...
996 */
997
998 if (fb->length < 1)
999 result = 0;
1000 else
1001 result = (fb->buffer[rules->offset - fb->offset] ==
1002 rules->value.charv);
1003 break;
1004
1005 case MIME_MAGIC_SHORT :
1006 /*
1007 * Load the buffer if necessary...
1008 */
1009
1010 if (fb->offset < 0 || rules->offset < fb->offset ||
1011 (rules->offset + 2) > (fb->offset + fb->length))
1012 {
1013 /*
1014 * Reload file buffer...
1015 */
1016
1017 cupsFileSeek(fb->fp, rules->offset);
1018 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
1019 sizeof(fb->buffer));
1020 fb->offset = rules->offset;
1021 }
1022
1023 /*
1024 * Compare the short values; if the file is too short, it
1025 * can't match...
1026 */
1027
1028 if (fb->length < 2)
1029 result = 0;
1030 else
1031 {
1032 bufptr = fb->buffer + rules->offset - fb->offset;
1033 shortv = (bufptr[0] << 8) | bufptr[1];
1034 result = (shortv == rules->value.shortv);
1035 }
1036 break;
1037
1038 case MIME_MAGIC_INT :
1039 /*
1040 * Load the buffer if necessary...
1041 */
1042
1043 if (fb->offset < 0 || rules->offset < fb->offset ||
1044 (rules->offset + 4) > (fb->offset + fb->length))
1045 {
1046 /*
1047 * Reload file buffer...
1048 */
1049
1050 cupsFileSeek(fb->fp, rules->offset);
1051 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
1052 sizeof(fb->buffer));
1053 fb->offset = rules->offset;
1054 }
1055
1056 /*
1057 * Compare the int values; if the file is too short, it
1058 * can't match...
1059 */
1060
1061 if (fb->length < 4)
1062 result = 0;
1063 else
1064 {
1065 bufptr = fb->buffer + rules->offset - fb->offset;
1066 intv = (((((bufptr[0] << 8) | bufptr[1]) << 8) |
1067 bufptr[2]) << 8) | bufptr[3];
1068 result = (intv == rules->value.intv);
1069 }
1070 break;
1071
1072 case MIME_MAGIC_LOCALE :
1073 #if defined(WIN32) || defined(__EMX__) || defined(__APPLE__)
1074 result = (strcmp(rules->value.localev,
1075 setlocale(LC_ALL, "")) == 0);
1076 #else
1077 result = (strcmp(rules->value.localev,
1078 setlocale(LC_MESSAGES, "")) == 0);
1079 #endif /* __APPLE__ */
1080 break;
1081
1082 case MIME_MAGIC_CONTAINS :
1083 /*
1084 * Load the buffer if necessary...
1085 */
1086
1087 if (fb->offset < 0 || rules->offset < fb->offset ||
1088 (rules->offset + rules->region) > (fb->offset + fb->length))
1089 {
1090 /*
1091 * Reload file buffer...
1092 */
1093
1094 cupsFileSeek(fb->fp, rules->offset);
1095 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
1096 sizeof(fb->buffer));
1097 fb->offset = rules->offset;
1098 }
1099
1100 /*
1101 * Compare the buffer against the string. If the file is too
1102 * short then don't compare - it can't match...
1103 */
1104
1105 if ((rules->offset + rules->length) > (fb->offset + fb->length))
1106 result = 0;
1107 else
1108 {
1109 if (fb->length > rules->region)
1110 region = rules->region - rules->length;
1111 else
1112 region = fb->length - rules->length;
1113
1114 for (n = 0; n < region; n ++)
1115 if ((result = (memcmp(fb->buffer + rules->offset - fb->offset + n,
1116 rules->value.stringv,
1117 rules->length) == 0)) != 0)
1118 break;
1119 }
1120 break;
1121
1122 default :
1123 if (rules->child != NULL)
1124 result = mime_check_rules(filename, fb, rules->child);
1125 else
1126 result = 0;
1127 break;
1128 }
1129
1130 /*
1131 * If the logic is inverted, invert the result...
1132 */
1133
1134 if (rules->invert)
1135 result = !result;
1136
1137 /*
1138 * OK, now if the current logic is OR and this result is true, the this
1139 * rule set is true. If the current logic is AND and this result is false,
1140 * the the rule set is false...
1141 */
1142
1143 DEBUG_printf(("5mime_check_rules: result of test %p (MIME_MAGIC_%s) is %d",
1144 rules, debug_ops[rules->op], result));
1145
1146 if ((result && logic == MIME_MAGIC_OR) ||
1147 (!result && logic == MIME_MAGIC_AND))
1148 return (result);
1149
1150 /*
1151 * Otherwise the jury is still out on this one, so move to the next rule.
1152 */
1153
1154 rules = rules->next;
1155 }
1156
1157 return (result);
1158 }
1159
1160
1161 /*
1162 * 'mime_patmatch()' - Pattern matching.
1163 */
1164
1165 static int /* O - 1 if match, 0 if no match */
1166 mime_patmatch(const char *s, /* I - String to match against */
1167 const char *pat) /* I - Pattern to match against */
1168 {
1169 /*
1170 * Range check the input...
1171 */
1172
1173 if (s == NULL || pat == NULL)
1174 return (0);
1175
1176 /*
1177 * Loop through the pattern and match strings, and stop if we come to a
1178 * point where the strings don't match or we find a complete match.
1179 */
1180
1181 while (*s != '\0' && *pat != '\0')
1182 {
1183 if (*pat == '*')
1184 {
1185 /*
1186 * Wildcard - 0 or more characters...
1187 */
1188
1189 pat ++;
1190 if (*pat == '\0')
1191 return (1); /* Last pattern char is *, so everything matches... */
1192
1193 /*
1194 * Test all remaining combinations until we get to the end of the string.
1195 */
1196
1197 while (*s != '\0')
1198 {
1199 if (mime_patmatch(s, pat))
1200 return (1);
1201
1202 s ++;
1203 }
1204 }
1205 else if (*pat == '?')
1206 {
1207 /*
1208 * Wildcard - 1 character...
1209 */
1210
1211 pat ++;
1212 s ++;
1213 continue;
1214 }
1215 else if (*pat == '[')
1216 {
1217 /*
1218 * Match a character from the input set [chars]...
1219 */
1220
1221 pat ++;
1222 while (*pat != ']' && *pat != '\0')
1223 if (*s == *pat)
1224 break;
1225 else
1226 pat ++;
1227
1228 if (*pat == ']' || *pat == '\0')
1229 return (0);
1230
1231 while (*pat != ']' && *pat != '\0')
1232 pat ++;
1233
1234 if (*pat == ']')
1235 pat ++;
1236
1237 continue;
1238 }
1239 else if (*pat == '\\')
1240 {
1241 /*
1242 * Handle quoted characters...
1243 */
1244
1245 pat ++;
1246 }
1247
1248 /*
1249 * Stop if the pattern and string don't match...
1250 */
1251
1252 if (*pat++ != *s++)
1253 return (0);
1254 }
1255
1256 /*
1257 * Done parsing the pattern and string; return 1 if the last character
1258 * matches and 0 otherwise...
1259 */
1260
1261 return (*s == *pat);
1262 }
1263
1264
1265 /*
1266 * End of "$Id$".
1267 */