]>
Commit | Line | Data |
---|---|---|
79df52e3 | 1 | /* |
839c43aa | 2 | * "$Id: type.c,v 1.11.2.2 2002/01/02 18:05:06 mike Exp $" |
79df52e3 | 3 | * |
0b74af7d | 4 | * MIME typing routines for the Common UNIX Printing System (CUPS). |
79df52e3 | 5 | * |
839c43aa | 6 | * Copyright 1997-2002 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 |
79df52e3 | 18 | * Hollywood, Maryland 20636-3111 USA |
19 | * | |
20 | * Voice: (301) 373-9603 | |
21 | * EMail: cups-info@cups.org | |
22 | * WWW: http://www.cups.org | |
23 | * | |
24 | * Contents: | |
25 | * | |
0b74af7d | 26 | * mimeAddType() - Add a MIME type to a database. |
27 | * mimeAddRule() - Add a detection rule for a file type. | |
28 | * mimeFileType() - Determine the type of a file. | |
29 | * mimeType() - Lookup a file type. | |
30 | * compare() - Compare two MIME super/type names. | |
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 | |
49 | /* | |
50 | * Local functions... | |
51 | */ | |
52 | ||
53 | static int compare(mime_type_t **, mime_type_t **); | |
063e1ac7 | 54 | static int checkrules(const char *, FILE *, mime_magic_t *); |
55 | static int patmatch(const char *, const char *); | |
0b74af7d | 56 | |
57 | ||
58 | /* | |
59 | * 'mimeAddType()' - Add a MIME type to a database. | |
60 | */ | |
61 | ||
62 | mime_type_t * /* O - New (or existing) MIME type */ | |
063e1ac7 | 63 | mimeAddType(mime_t *mime, /* I - MIME database */ |
64 | const char *super, /* I - Super-type name */ | |
65 | const char *type) /* I - Type name */ | |
0b74af7d | 66 | { |
67 | mime_type_t *temp, /* New MIME type */ | |
68 | **types; /* New MIME types array */ | |
69 | ||
70 | ||
71 | /* | |
72 | * Range check input... | |
73 | */ | |
74 | ||
75 | if (mime == NULL || super == NULL || type == NULL) | |
76 | return (NULL); | |
77 | ||
78 | if (strlen(super) > (MIME_MAX_SUPER - 1) || | |
79 | strlen(type) > (MIME_MAX_TYPE - 1)) | |
80 | return (NULL); | |
81 | ||
82 | /* | |
83 | * See if the type already exists; if so, return the existing type... | |
84 | */ | |
85 | ||
86 | if ((temp = mimeType(mime, super, type)) != NULL) | |
87 | return (temp); | |
88 | ||
89 | /* | |
90 | * The type doesn't exist; add it... | |
91 | */ | |
92 | ||
93 | if ((temp = calloc(1, sizeof(mime_type_t))) == NULL) | |
94 | return (NULL); | |
95 | ||
96 | if (mime->num_types == 0) | |
97 | types = (mime_type_t **)malloc(sizeof(mime_type_t *)); | |
98 | else | |
99 | types = (mime_type_t **)realloc(mime->types, sizeof(mime_type_t *) * (mime->num_types + 1)); | |
100 | ||
101 | if (types == NULL) | |
102 | { | |
103 | free(temp); | |
104 | return (NULL); | |
e8fda7b9 | 105 | } |
0b74af7d | 106 | |
107 | mime->types = types; | |
108 | types += mime->num_types; | |
109 | mime->num_types ++; | |
110 | ||
111 | *types = temp; | |
970017a4 | 112 | strncpy(temp->super, super, sizeof(temp->super) - 1); |
113 | strncpy(temp->type, type, sizeof(temp->type) - 1); | |
0b74af7d | 114 | |
115 | if (mime->num_types > 1) | |
116 | qsort(mime->types, mime->num_types, sizeof(mime_type_t *), | |
117 | (int (*)(const void *, const void *))compare); | |
118 | ||
119 | return (temp); | |
120 | } | |
121 | ||
122 | ||
123 | /* | |
124 | * 'mimeAddRule()' - Add a detection rule for a file type. | |
125 | */ | |
126 | ||
127 | int /* O - 0 on success, -1 on failure */ | |
128 | mimeAddTypeRule(mime_type_t *mt, /* I - Type to add to */ | |
063e1ac7 | 129 | const char *rule) /* I - Rule to add */ |
0b74af7d | 130 | { |
131 | int num_values, /* Number of values seen */ | |
132 | op, /* Operation code */ | |
133 | logic, /* Logic for next rule */ | |
134 | invert; /* Invert following rule? */ | |
135 | char name[255], /* Name in rule string */ | |
8289fa53 | 136 | value[3][255], /* Value in rule string */ |
0b74af7d | 137 | *ptr, /* Position in name or value */ |
138 | quote; /* Quote character */ | |
8289fa53 | 139 | int length[3]; /* Length of each parameter */ |
0b74af7d | 140 | mime_magic_t *temp, /* New rule */ |
141 | *current; /* Current rule */ | |
142 | ||
143 | ||
144 | /* | |
145 | * Range check input... | |
146 | */ | |
147 | ||
148 | if (mt == NULL || rule == NULL) | |
149 | return (-1); | |
150 | ||
151 | /* | |
152 | * Find the last rule in the top-level of the rules tree. | |
153 | */ | |
154 | ||
155 | for (current = mt->rules; current != NULL; current = current->next) | |
156 | if (current->next == NULL) | |
157 | break; | |
158 | ||
159 | /* | |
160 | * Parse the rules string. Most rules are either a file extension or a | |
161 | * comparison function: | |
162 | * | |
163 | * extension | |
164 | * function(parameters) | |
165 | */ | |
166 | ||
167 | logic = MIME_MAGIC_NOP; | |
168 | invert = 0; | |
169 | ||
8cf6bc6e | 170 | DEBUG_printf(("%s/%s: %s\n", mt->super, mt->type, rule)); |
171 | ||
0b74af7d | 172 | while (*rule != '\0') |
173 | { | |
174 | while (isspace(*rule)) | |
175 | rule ++; | |
176 | ||
177 | if (*rule == '(') | |
178 | { | |
8cf6bc6e | 179 | DEBUG_puts("new parenthesis group"); |
0b74af7d | 180 | logic = MIME_MAGIC_NOP; |
181 | rule ++; | |
182 | } | |
183 | else if (*rule == ')') | |
184 | { | |
8cf6bc6e | 185 | DEBUG_puts("close paren..."); |
0b74af7d | 186 | if (current == NULL || current->parent == NULL) |
187 | return (-1); | |
188 | ||
189 | current = current->parent; | |
190 | ||
191 | if (current->parent == NULL) | |
192 | logic = MIME_MAGIC_OR; | |
193 | else | |
194 | logic = current->parent->op; | |
195 | ||
196 | rule ++; | |
197 | } | |
198 | else if (*rule == '+' && current != NULL) | |
199 | { | |
200 | if (logic != MIME_MAGIC_AND && | |
201 | current != NULL && current->prev != NULL && current->prev->prev != NULL) | |
202 | { | |
203 | /* | |
204 | * OK, we have more than 1 rule in the current tree level... Make a | |
205 | * new group tree and move the previous rule to it... | |
206 | */ | |
207 | ||
208 | if ((temp = calloc(1, sizeof(mime_magic_t))) == NULL) | |
209 | return (-1); | |
210 | ||
211 | temp->op = MIME_MAGIC_AND; | |
212 | temp->child = current; | |
213 | temp->parent = current->parent; | |
214 | current->prev->next = temp; | |
215 | temp->prev = current->prev; | |
216 | ||
217 | current->prev = NULL; | |
218 | current->parent = temp; | |
8cf6bc6e | 219 | |
220 | DEBUG_printf(("creating new AND group %p...\n", temp)); | |
0b74af7d | 221 | } |
222 | else | |
8cf6bc6e | 223 | { |
224 | DEBUG_printf(("setting group %p op to AND...\n", current->parent)); | |
0b74af7d | 225 | current->parent->op = MIME_MAGIC_AND; |
8cf6bc6e | 226 | } |
0b74af7d | 227 | |
228 | logic = MIME_MAGIC_AND; | |
229 | rule ++; | |
230 | } | |
231 | else if (*rule == ',') | |
232 | { | |
233 | if (logic != MIME_MAGIC_OR && current != NULL) | |
234 | { | |
235 | /* | |
236 | * OK, we have two possibilities; either this is the top-level rule or | |
237 | * we have a bunch of AND rules at this level. | |
238 | */ | |
239 | ||
240 | if (current->parent == NULL) | |
241 | { | |
242 | /* | |
243 | * This is the top-level rule; we have to move *all* of the AND rules | |
244 | * down a level, as AND has precedence over OR. | |
245 | */ | |
246 | ||
247 | if ((temp = calloc(1, sizeof(mime_magic_t))) == NULL) | |
248 | return (-1); | |
249 | ||
8cf6bc6e | 250 | DEBUG_printf(("creating new AND group %p inside OR group\n", temp)); |
251 | ||
0b74af7d | 252 | while (current->prev != NULL) |
253 | { | |
254 | current->parent = temp; | |
255 | current = current->prev; | |
e8fda7b9 | 256 | } |
0b74af7d | 257 | |
258 | current->parent = temp; | |
259 | temp->op = MIME_MAGIC_AND; | |
260 | temp->child = current; | |
261 | ||
262 | mt->rules = current = temp; | |
263 | } | |
264 | else | |
265 | { | |
266 | /* | |
267 | * This isn't the top rule, so go up one level... | |
268 | */ | |
269 | ||
8cf6bc6e | 270 | DEBUG_puts("going up one level"); |
0b74af7d | 271 | current = current->parent; |
e8fda7b9 | 272 | } |
273 | } | |
0b74af7d | 274 | |
275 | logic = MIME_MAGIC_OR; | |
276 | rule ++; | |
277 | } | |
278 | else if (*rule == '!') | |
279 | { | |
8cf6bc6e | 280 | DEBUG_puts("NOT"); |
0b74af7d | 281 | invert = 1; |
282 | rule ++; | |
283 | } | |
284 | else if (isalnum(*rule)) | |
285 | { | |
286 | /* | |
287 | * Read an extension name or a function... | |
288 | */ | |
289 | ||
290 | for (ptr = name; isalnum(*rule) && (ptr - name) < (sizeof(name) - 1);) | |
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 | { | |
312 | if (isspace(*rule)) | |
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 | { |
345 | if (isxdigit(rule[0]) && isxdigit(rule[1])) | |
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 | ||
388 | if (strcmp(name, "match") == 0) | |
389 | op = MIME_MAGIC_MATCH; | |
390 | else if (strcmp(name, "ascii") == 0) | |
391 | op = MIME_MAGIC_ASCII; | |
392 | else if (strcmp(name, "printable") == 0) | |
393 | op = MIME_MAGIC_PRINTABLE; | |
394 | else if (strcmp(name, "string") == 0) | |
395 | op = MIME_MAGIC_STRING; | |
396 | else if (strcmp(name, "char") == 0) | |
397 | op = MIME_MAGIC_CHAR; | |
398 | else if (strcmp(name, "short") == 0) | |
399 | op = MIME_MAGIC_SHORT; | |
400 | else if (strcmp(name, "int") == 0) | |
401 | op = MIME_MAGIC_INT; | |
402 | else if (strcmp(name, "locale") == 0) | |
403 | op = MIME_MAGIC_LOCALE; | |
3d9e2586 | 404 | else if (strcmp(name, "contains") == 0) |
405 | op = MIME_MAGIC_CONTAINS; | |
0b74af7d | 406 | else |
407 | return (-1); | |
408 | } | |
409 | else | |
410 | { | |
411 | /* | |
412 | * This is just a filename match on the extension... | |
413 | */ | |
414 | ||
04de52f8 | 415 | snprintf(value[0], sizeof(value[0]), "*.%s", name); |
42d48bd2 | 416 | length[0] = strlen(value[0]); |
0b74af7d | 417 | num_values = 1; |
418 | op = MIME_MAGIC_MATCH; | |
e8fda7b9 | 419 | } |
0b74af7d | 420 | |
421 | /* | |
422 | * Add a rule for this operation. | |
423 | */ | |
424 | ||
425 | if ((temp = calloc(1, sizeof(mime_magic_t))) == NULL) | |
426 | return (-1); | |
427 | ||
428 | temp->invert = invert; | |
429 | if (current != NULL) | |
430 | { | |
431 | temp->parent = current->parent; | |
432 | current->next = temp; | |
433 | } | |
434 | else | |
435 | mt->rules = temp; | |
436 | ||
437 | temp->prev = current; | |
438 | ||
439 | if (logic == MIME_MAGIC_NOP) | |
440 | { | |
441 | /* | |
442 | * Add parenthetical grouping... | |
443 | */ | |
444 | ||
8cf6bc6e | 445 | DEBUG_printf(("making new OR group %p for parenthesis...\n", temp)); |
446 | ||
0b74af7d | 447 | temp->op = MIME_MAGIC_OR; |
448 | ||
449 | if ((temp->child = calloc(1, sizeof(mime_magic_t))) == NULL) | |
450 | return (-1); | |
451 | ||
452 | temp->child->parent = temp; | |
453 | ||
454 | temp = temp->child; | |
455 | logic = MIME_MAGIC_OR; | |
e8fda7b9 | 456 | } |
0b74af7d | 457 | |
8cf6bc6e | 458 | DEBUG_printf(("adding %p: %s, op = %d, logic = %d, invert = %d\n", |
459 | temp, name, op, logic, invert)); | |
460 | ||
0b74af7d | 461 | /* |
462 | * Fill in data for the rule... | |
463 | */ | |
464 | ||
465 | current = temp; | |
466 | temp->op = op; | |
467 | invert = 0; | |
468 | ||
469 | switch (op) | |
470 | { | |
471 | case MIME_MAGIC_MATCH : | |
472 | if (length[0] > (sizeof(temp->value.matchv) - 1)) | |
473 | return (-1); | |
474 | strcpy(temp->value.matchv, value[0]); | |
475 | break; | |
476 | case MIME_MAGIC_ASCII : | |
477 | case MIME_MAGIC_PRINTABLE : | |
8289fa53 | 478 | temp->offset = strtol(value[0], NULL, 0); |
479 | temp->length = strtol(value[1], NULL, 0); | |
0b74af7d | 480 | if (temp->length > MIME_MAX_BUFFER) |
481 | temp->length = MIME_MAX_BUFFER; | |
482 | break; | |
483 | case MIME_MAGIC_STRING : | |
8289fa53 | 484 | temp->offset = strtol(value[0], NULL, 0); |
0b74af7d | 485 | if (length[1] > sizeof(temp->value.stringv)) |
486 | return (-1); | |
487 | temp->length = length[1]; | |
488 | memcpy(temp->value.stringv, value[1], length[1]); | |
489 | break; | |
490 | case MIME_MAGIC_CHAR : | |
8289fa53 | 491 | temp->offset = strtol(value[0], NULL, 0); |
0b74af7d | 492 | if (length[1] == 1) |
493 | temp->value.charv = value[1][0]; | |
494 | else | |
8289fa53 | 495 | temp->value.charv = strtol(value[1], NULL, 0); |
0b74af7d | 496 | break; |
497 | case MIME_MAGIC_SHORT : | |
8289fa53 | 498 | temp->offset = strtol(value[0], NULL, 0); |
499 | temp->value.shortv = strtol(value[1], NULL, 0); | |
0b74af7d | 500 | break; |
501 | case MIME_MAGIC_INT : | |
8289fa53 | 502 | temp->offset = strtol(value[0], NULL, 0); |
503 | temp->value.intv = strtol(value[1], NULL, 0); | |
0b74af7d | 504 | break; |
505 | case MIME_MAGIC_LOCALE : | |
506 | if (length[0] > (sizeof(temp->value.localev) - 1)) | |
507 | return (-1); | |
508 | ||
509 | strcpy(temp->value.localev, value[0]); | |
510 | break; | |
3d9e2586 | 511 | case MIME_MAGIC_CONTAINS : |
8289fa53 | 512 | temp->offset = strtol(value[0], NULL, 0); |
513 | temp->region = strtol(value[1], NULL, 0); | |
3d9e2586 | 514 | if (length[2] > sizeof(temp->value.stringv)) |
515 | return (-1); | |
516 | temp->length = length[2]; | |
517 | memcpy(temp->value.stringv, value[2], length[2]); | |
518 | break; | |
e8fda7b9 | 519 | } |
0b74af7d | 520 | } |
521 | else | |
522 | break; | |
e8fda7b9 | 523 | } |
0b74af7d | 524 | |
525 | return (0); | |
526 | } | |
527 | ||
528 | ||
529 | /* | |
530 | * 'mimeFileType()' - Determine the type of a file. | |
531 | */ | |
532 | ||
063e1ac7 | 533 | mime_type_t * /* O - Type of file */ |
534 | mimeFileType(mime_t *mime, /* I - MIME database */ | |
535 | const char *pathname) /* I - Name of file to check */ | |
0b74af7d | 536 | { |
537 | int i; /* Looping var */ | |
538 | FILE *fp; /* File pointer */ | |
539 | mime_type_t **types; /* File types */ | |
063e1ac7 | 540 | const char *filename; /* Base filename of file */ |
0b74af7d | 541 | |
542 | ||
543 | /* | |
544 | * Range check input parameters... | |
545 | */ | |
546 | ||
547 | if (mime == NULL || pathname == NULL) | |
548 | return (NULL); | |
549 | ||
550 | /* | |
551 | * Try to open the file... | |
552 | */ | |
553 | ||
554 | if ((fp = fopen(pathname, "r")) == NULL) | |
555 | return (NULL); | |
556 | ||
557 | /* | |
558 | * Figure out the filename (without directory portion)... | |
559 | */ | |
560 | ||
561 | if ((filename = strrchr(pathname, '/')) != NULL) | |
562 | filename ++; | |
563 | else | |
564 | filename = pathname; | |
565 | ||
566 | /* | |
567 | * Then check it against all known types... | |
568 | */ | |
569 | ||
570 | for (i = mime->num_types, types = mime->types; i > 0; i --, types ++) | |
571 | if (checkrules(filename, fp, (*types)->rules)) | |
572 | break; | |
573 | ||
574 | /* | |
575 | * Finally, close the file and return a match (if any)... | |
576 | */ | |
577 | ||
578 | fclose(fp); | |
579 | ||
580 | if (i > 0) | |
581 | return (*types); | |
582 | else | |
583 | return (NULL); | |
584 | } | |
585 | ||
586 | ||
587 | /* | |
588 | * 'mimeType()' - Lookup a file type. | |
589 | */ | |
590 | ||
591 | mime_type_t * /* O - Matching file type definition */ | |
063e1ac7 | 592 | mimeType(mime_t *mime, /* I - MIME database */ |
593 | const char *super, /* I - Super-type name */ | |
594 | const char *type) /* I - Type name */ | |
0b74af7d | 595 | { |
596 | mime_type_t key, /* MIME type search key*/ | |
597 | *keyptr, /* Key pointer... */ | |
598 | **match; /* Matching pointer */ | |
599 | ||
600 | /* | |
601 | * Range check input... | |
602 | */ | |
603 | ||
604 | if (mime == NULL || super == NULL || type == NULL) | |
605 | return (NULL); | |
606 | ||
607 | if (strlen(super) > (MIME_MAX_SUPER - 1) || | |
608 | strlen(type) > (MIME_MAX_TYPE - 1)) | |
609 | return (NULL); | |
610 | ||
611 | if (mime->num_types == 0) | |
612 | return (NULL); | |
613 | ||
614 | /* | |
615 | * Lookup the type in the array... | |
616 | */ | |
617 | ||
970017a4 | 618 | strncpy(key.super, super, sizeof(key.super) - 1); |
619 | key.super[sizeof(key.super) - 1] = '\0'; | |
620 | strncpy(key.type, type, sizeof(key.type) - 1); | |
621 | key.type[sizeof(key.type) - 1] = '\0'; | |
622 | ||
0b74af7d | 623 | keyptr = &key; |
624 | ||
625 | match = (mime_type_t **)bsearch(&keyptr, mime->types, mime->num_types, | |
626 | sizeof(mime_type_t *), | |
627 | (int (*)(const void *, const void *))compare); | |
628 | ||
629 | if (match == NULL) | |
630 | return (NULL); | |
631 | else | |
632 | return (*match); | |
633 | } | |
634 | ||
635 | ||
636 | /* | |
637 | * 'compare()' - Compare two MIME super/type names. | |
638 | */ | |
639 | ||
640 | static int /* O - Result of comparison */ | |
641 | compare(mime_type_t **t0, /* I - First type */ | |
642 | mime_type_t **t1) /* I - Second type */ | |
643 | { | |
644 | int i; /* Result of comparison */ | |
645 | ||
646 | ||
3b960317 | 647 | if ((i = strcasecmp((*t0)->super, (*t1)->super)) == 0) |
648 | i = strcasecmp((*t0)->type, (*t1)->type); | |
0b74af7d | 649 | |
650 | return (i); | |
651 | } | |
652 | ||
653 | ||
654 | /* | |
655 | * 'checkrules()' - Check each rule in a list. | |
656 | */ | |
657 | ||
658 | static int /* O - 1 if match, 0 if no match */ | |
063e1ac7 | 659 | checkrules(const char *filename, /* I - Filename */ |
0b74af7d | 660 | FILE *fp, /* I - File to check */ |
661 | mime_magic_t *rules) /* I - Rules to check */ | |
662 | { | |
663 | int n; /* Looping var */ | |
3d9e2586 | 664 | int region; /* Region to look at */ |
0b74af7d | 665 | int logic, /* Logic to apply */ |
666 | result, /* Result of test */ | |
667 | intv; /* Integer value */ | |
668 | short shortv; /* Short value */ | |
669 | unsigned char buffer[MIME_MAX_BUFFER],/* Input buffer */ | |
670 | *bufptr; /* Current buffer position */ | |
671 | int bufoffset, /* Offset in file for buffer */ | |
672 | buflength; /* Length of data in buffer */ | |
673 | ||
674 | ||
675 | if (rules == NULL) | |
676 | return (0); | |
677 | ||
678 | if (rules->parent == NULL) | |
679 | logic = MIME_MAGIC_OR; | |
680 | else | |
681 | logic = rules->parent->op; | |
682 | ||
683 | bufoffset = -1; | |
d21a7597 | 684 | buflength = 0; |
3d9e2586 | 685 | result = 0; |
0b74af7d | 686 | |
687 | while (rules != NULL) | |
688 | { | |
689 | /* | |
690 | * Compute the result of this rule... | |
691 | */ | |
692 | ||
693 | switch (rules->op) | |
694 | { | |
695 | case MIME_MAGIC_MATCH : | |
696 | result = patmatch(filename, rules->value.matchv); | |
697 | break; | |
698 | ||
699 | case MIME_MAGIC_ASCII : | |
700 | /* | |
701 | * Load the buffer if necessary... | |
702 | */ | |
703 | ||
704 | if (bufoffset < 0 || rules->offset < bufoffset || | |
705 | (rules->offset + rules->length) > (bufoffset + buflength)) | |
706 | { | |
707 | /* | |
708 | * Reload file buffer... | |
709 | */ | |
710 | ||
711 | fseek(fp, rules->offset, SEEK_SET); | |
712 | buflength = fread(buffer, 1, sizeof(buffer), fp); | |
713 | bufoffset = rules->offset; | |
e8fda7b9 | 714 | } |
0b74af7d | 715 | |
716 | /* | |
717 | * Test for ASCII printable characters plus standard control chars. | |
718 | */ | |
719 | ||
720 | if ((rules->offset + rules->length) > (bufoffset + buflength)) | |
721 | n = bufoffset + buflength - rules->offset; | |
722 | else | |
723 | n = rules->length; | |
724 | ||
725 | bufptr = buffer + rules->offset - bufoffset; | |
726 | while (n > 0) | |
727 | if ((*bufptr >= 32 && *bufptr <= 126) || | |
45011ac7 | 728 | (*bufptr >= 8 && *bufptr <= 13) || |
729 | *bufptr == 26 || *bufptr == 27) | |
0b74af7d | 730 | { |
731 | n --; | |
732 | bufptr ++; | |
733 | } | |
734 | else | |
735 | break; | |
736 | ||
737 | result = (n == 0); | |
738 | break; | |
739 | ||
740 | case MIME_MAGIC_PRINTABLE : | |
741 | /* | |
742 | * Load the buffer if necessary... | |
743 | */ | |
744 | ||
745 | if (bufoffset < 0 || rules->offset < bufoffset || | |
746 | (rules->offset + rules->length) > (bufoffset + buflength)) | |
747 | { | |
748 | /* | |
749 | * Reload file buffer... | |
750 | */ | |
751 | ||
752 | fseek(fp, rules->offset, SEEK_SET); | |
753 | buflength = fread(buffer, 1, sizeof(buffer), fp); | |
754 | bufoffset = rules->offset; | |
e8fda7b9 | 755 | } |
0b74af7d | 756 | |
757 | /* | |
caa58f49 | 758 | * Test for 8-bit printable characters plus standard control chars. |
0b74af7d | 759 | */ |
760 | ||
761 | if ((rules->offset + rules->length) > (bufoffset + buflength)) | |
762 | n = bufoffset + buflength - rules->offset; | |
763 | else | |
764 | n = rules->length; | |
765 | ||
5081f753 | 766 | bufptr = buffer + rules->offset - bufoffset; |
0b74af7d | 767 | |
768 | while (n > 0) | |
caa58f49 | 769 | if (*bufptr >= 128 || |
0b74af7d | 770 | (*bufptr >= 32 && *bufptr <= 126) || |
45011ac7 | 771 | (*bufptr >= 8 && *bufptr <= 13) || |
772 | *bufptr == 26 || *bufptr == 27) | |
0b74af7d | 773 | { |
0b74af7d | 774 | n --; |
775 | bufptr ++; | |
776 | } | |
777 | else | |
778 | break; | |
779 | ||
5081f753 | 780 | result = (n == 0); |
0b74af7d | 781 | break; |
782 | ||
783 | case MIME_MAGIC_STRING : | |
784 | /* | |
785 | * Load the buffer if necessary... | |
786 | */ | |
787 | ||
788 | if (bufoffset < 0 || rules->offset < bufoffset || | |
789 | (rules->offset + rules->length) > (bufoffset + buflength)) | |
790 | { | |
791 | /* | |
792 | * Reload file buffer... | |
793 | */ | |
794 | ||
795 | fseek(fp, rules->offset, SEEK_SET); | |
796 | buflength = fread(buffer, 1, sizeof(buffer), fp); | |
797 | bufoffset = rules->offset; | |
e8fda7b9 | 798 | } |
0b74af7d | 799 | |
800 | /* | |
801 | * Compare the buffer against the string. If the file is too | |
802 | * short then don't compare - it can't match... | |
803 | */ | |
804 | ||
805 | if ((rules->offset + rules->length) > (bufoffset + buflength)) | |
806 | result = 0; | |
807 | else | |
808 | result = (memcmp(buffer + rules->offset - bufoffset, | |
809 | rules->value.stringv, rules->length) == 0); | |
810 | break; | |
811 | ||
812 | case MIME_MAGIC_CHAR : | |
813 | /* | |
814 | * Load the buffer if necessary... | |
815 | */ | |
816 | ||
817 | if (bufoffset < 0 || rules->offset < bufoffset) | |
818 | { | |
819 | /* | |
820 | * Reload file buffer... | |
821 | */ | |
822 | ||
823 | fseek(fp, rules->offset, SEEK_SET); | |
824 | buflength = fread(buffer, 1, sizeof(buffer), fp); | |
825 | bufoffset = rules->offset; | |
e8fda7b9 | 826 | } |
0b74af7d | 827 | |
828 | /* | |
829 | * Compare the character values; if the file is too short, it | |
830 | * can't match... | |
831 | */ | |
832 | ||
833 | if (buflength < 1) | |
834 | result = 0; | |
835 | else | |
836 | result = (buffer[rules->offset - bufoffset] == rules->value.charv); | |
837 | break; | |
838 | ||
839 | case MIME_MAGIC_SHORT : | |
840 | /* | |
841 | * Load the buffer if necessary... | |
842 | */ | |
843 | ||
844 | if (bufoffset < 0 || rules->offset < bufoffset || | |
845 | (rules->offset + 2) > (bufoffset + buflength)) | |
846 | { | |
847 | /* | |
848 | * Reload file buffer... | |
849 | */ | |
850 | ||
851 | fseek(fp, rules->offset, SEEK_SET); | |
852 | buflength = fread(buffer, 1, sizeof(buffer), fp); | |
853 | bufoffset = rules->offset; | |
e8fda7b9 | 854 | } |
0b74af7d | 855 | |
856 | /* | |
857 | * Compare the short values; if the file is too short, it | |
858 | * can't match... | |
859 | */ | |
860 | ||
861 | if (buflength < 2) | |
862 | result = 0; | |
863 | else | |
864 | { | |
865 | bufptr = buffer + rules->offset - bufoffset; | |
866 | shortv = (bufptr[0] << 8) | bufptr[1]; | |
867 | result = (shortv == rules->value.shortv); | |
e8fda7b9 | 868 | } |
0b74af7d | 869 | break; |
870 | ||
871 | case MIME_MAGIC_INT : | |
872 | /* | |
873 | * Load the buffer if necessary... | |
874 | */ | |
875 | ||
876 | if (bufoffset < 0 || rules->offset < bufoffset || | |
877 | (rules->offset + 4) > (bufoffset + buflength)) | |
878 | { | |
879 | /* | |
880 | * Reload file buffer... | |
881 | */ | |
882 | ||
883 | fseek(fp, rules->offset, SEEK_SET); | |
884 | buflength = fread(buffer, 1, sizeof(buffer), fp); | |
885 | bufoffset = rules->offset; | |
e8fda7b9 | 886 | } |
0b74af7d | 887 | |
888 | /* | |
889 | * Compare the int values; if the file is too short, it | |
890 | * can't match... | |
891 | */ | |
892 | ||
893 | if (buflength < 4) | |
894 | result = 0; | |
895 | else | |
896 | { | |
897 | bufptr = buffer + rules->offset - bufoffset; | |
898 | intv = (((((bufptr[0] << 8) | bufptr[1]) << 8) | bufptr[2]) << 8) | | |
899 | bufptr[3];; | |
900 | result = (intv == rules->value.intv); | |
e8fda7b9 | 901 | } |
0b74af7d | 902 | break; |
903 | ||
904 | case MIME_MAGIC_LOCALE : | |
753453e4 | 905 | result = (strcmp(rules->value.localev, setlocale(LC_MESSAGES, NULL)) == 0); |
0b74af7d | 906 | break; |
907 | ||
3d9e2586 | 908 | case MIME_MAGIC_CONTAINS : |
909 | /* | |
910 | * Load the buffer if necessary... | |
911 | */ | |
912 | ||
913 | if (bufoffset < 0 || rules->offset < bufoffset || | |
914 | (rules->offset + rules->region) > (bufoffset + buflength)) | |
915 | { | |
916 | /* | |
917 | * Reload file buffer... | |
918 | */ | |
919 | ||
920 | fseek(fp, rules->offset, SEEK_SET); | |
921 | buflength = fread(buffer, 1, sizeof(buffer), fp); | |
922 | bufoffset = rules->offset; | |
923 | } | |
924 | ||
925 | /* | |
926 | * Compare the buffer against the string. If the file is too | |
927 | * short then don't compare - it can't match... | |
928 | */ | |
929 | ||
930 | if ((rules->offset + rules->length) > (bufoffset + buflength)) | |
931 | result = 0; | |
932 | else | |
933 | { | |
934 | if (buflength > rules->region) | |
935 | region = rules->region - rules->length; | |
936 | else | |
937 | region = buflength - rules->length; | |
938 | ||
939 | for (n = 0; n < region; n ++) | |
940 | if ((result = (memcmp(buffer + rules->offset - bufoffset + n, | |
941 | rules->value.stringv, rules->length) == 0)) != 0) | |
942 | break; | |
943 | } | |
944 | break; | |
945 | ||
0b74af7d | 946 | default : |
947 | if (rules->child != NULL) | |
948 | result = checkrules(filename, fp, rules->child); | |
949 | else | |
950 | result = 0; | |
951 | break; | |
e8fda7b9 | 952 | } |
0b74af7d | 953 | |
954 | /* | |
955 | * If the logic is inverted, invert the result... | |
956 | */ | |
957 | ||
958 | if (rules->invert) | |
959 | result = !result; | |
960 | ||
961 | /* | |
962 | * OK, now if the current logic is OR and this result is true, the this | |
963 | * rule set is true. If the current logic is AND and this result is false, | |
964 | * the the rule set is false... | |
965 | */ | |
966 | ||
8cf6bc6e | 967 | DEBUG_printf(("result of test %p is %d\n", rules, result)); |
968 | ||
0b74af7d | 969 | if ((result && logic == MIME_MAGIC_OR) || |
970 | (!result && logic == MIME_MAGIC_AND)) | |
971 | return (result); | |
972 | ||
973 | /* | |
974 | * Otherwise the jury is still out on this one, so move to the next rule. | |
975 | */ | |
976 | ||
977 | rules = rules->next; | |
e8fda7b9 | 978 | } |
0b74af7d | 979 | |
980 | return (result); | |
981 | } | |
982 | ||
983 | ||
984 | /* | |
985 | * 'patmatch()' - Pattern matching... | |
986 | */ | |
987 | ||
063e1ac7 | 988 | static int /* O - 1 if match, 0 if no match */ |
989 | patmatch(const char *s, /* I - String to match against */ | |
990 | const char *pat) /* I - Pattern to match against */ | |
0b74af7d | 991 | { |
992 | /* | |
993 | * Range check the input... | |
994 | */ | |
995 | ||
996 | if (s == NULL || pat == NULL) | |
997 | return (0); | |
998 | ||
999 | /* | |
1000 | * Loop through the pattern and match strings, and stop if we come to a | |
1001 | * point where the strings don't match or we find a complete match. | |
1002 | */ | |
1003 | ||
1004 | while (*s != '\0' && *pat != '\0') | |
1005 | { | |
1006 | if (*pat == '*') | |
1007 | { | |
1008 | /* | |
1009 | * Wildcard - 0 or more characters... | |
1010 | */ | |
1011 | ||
1012 | pat ++; | |
1013 | if (*pat == '\0') | |
1014 | return (1); /* Last pattern char is *, so everything matches now... */ | |
1015 | ||
1016 | /* | |
1017 | * Test all remaining combinations until we get to the end of the string. | |
1018 | */ | |
1019 | ||
1020 | while (*s != '\0') | |
1021 | { | |
1022 | if (patmatch(s, pat)) | |
1023 | return (1); | |
1024 | ||
1025 | s ++; | |
e8fda7b9 | 1026 | } |
0b74af7d | 1027 | } |
1028 | else if (*pat == '?') | |
1029 | { | |
1030 | /* | |
1031 | * Wildcard - 1 character... | |
1032 | */ | |
1033 | ||
1034 | pat ++; | |
1035 | s ++; | |
1036 | continue; | |
1037 | } | |
1038 | else if (*pat == '[') | |
1039 | { | |
1040 | /* | |
1041 | * Match a character from the input set [chars]... | |
1042 | */ | |
1043 | ||
1044 | pat ++; | |
1045 | while (*pat != ']' && *pat != '\0') | |
1046 | if (*s == *pat) | |
1047 | break; | |
1048 | else | |
1049 | pat ++; | |
1050 | ||
1051 | if (*pat == ']' || *pat == '\0') | |
1052 | return (0); | |
1053 | ||
1054 | while (*pat != ']' && *pat != '\0') | |
1055 | pat ++; | |
1056 | ||
1057 | if (*pat == ']') | |
1058 | pat ++; | |
1059 | ||
1060 | continue; | |
1061 | } | |
1062 | else if (*pat == '\\') | |
1063 | { | |
1064 | /* | |
1065 | * Handle quoted characters... | |
1066 | */ | |
1067 | ||
1068 | pat ++; | |
e8fda7b9 | 1069 | } |
0b74af7d | 1070 | |
1071 | /* | |
1072 | * Stop if the pattern and string don't match... | |
1073 | */ | |
1074 | ||
1075 | if (*pat++ != *s++) | |
1076 | return (0); | |
e8fda7b9 | 1077 | } |
0b74af7d | 1078 | |
1079 | /* | |
1080 | * Done parsing the pattern and string; return 1 if the last character matches | |
1081 | * and 0 otherwise... | |
1082 | */ | |
1083 | ||
1084 | return (*s == *pat); | |
1085 | } | |
79df52e3 | 1086 | |
1087 | ||
1088 | /* | |
839c43aa | 1089 | * End of "$Id: type.c,v 1.11.2.2 2002/01/02 18:05:06 mike Exp $". |
79df52e3 | 1090 | */ |