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