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