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