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