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