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