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