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