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