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