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