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