1 /*
2 * MIME database file routines for CUPS.
3 *
4 * Copyright © 2020-2024 by OpenPrinting.
5 * Copyright 2007-2014 by Apple Inc.
6 * Copyright 1997-2006 by Easy Software Products, all rights reserved.
7 *
8 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
9 */
10
11 /*
12 * Include necessary headers...
13 */
14
15 #include <cups/string-private.h>
16 #include <cups/dir.h>
17 #include "mime-private.h"
18
19
20 /*
21 * Debug macros that used to be private API...
22 */
23
24 #define DEBUG_puts(x)
25 #define DEBUG_printf(...)
26
27
28 /*
29 * Local types...
30 */
31
32 typedef struct _mime_fcache_s /**** Filter cache structure ****/
33 {
34 char *name, /* Filter name */
35 *path; /* Full path to filter if available */
36 } _mime_fcache_t;
37
38
39 /*
40 * Local functions...
41 */
42
43 static const char *mime_add_fcache(cups_array_t *filtercache, const char *name,
44 const char *filterpath);
45 static int mime_compare_fcache(_mime_fcache_t *a, _mime_fcache_t *b);
46 static void mime_delete_fcache(cups_array_t *filtercache);
47 static void mime_delete_rules(mime_magic_t *rules);
48 static void mime_load_convs(mime_t *mime, const char *filename,
49 const char *filterpath,
50 cups_array_t *filtercache);
51 static void mime_load_types(mime_t *mime, const char *filename);
52
53
54 /*
55 * 'mimeDelete()' - Delete (free) a MIME database.
56 */
57
58 void
mimeDelete(mime_t * mime)59 mimeDelete(mime_t *mime) /* I - MIME database */
60 {
61 mime_type_t *type; /* Current type */
62 mime_filter_t *filter; /* Current filter */
63
64
65 DEBUG_printf(("mimeDelete(mime=%p)", mime));
66
67 if (!mime)
68 return;
69
70 /*
71 * Loop through filters and free them...
72 */
73
74 for (filter = (mime_filter_t *)cupsArrayFirst(mime->filters);
75 filter;
76 filter = (mime_filter_t *)cupsArrayNext(mime->filters))
77 mimeDeleteFilter(mime, filter);
78
79 /*
80 * Loop through the file types and delete any rules...
81 */
82
83 for (type = (mime_type_t *)cupsArrayFirst(mime->types);
84 type;
85 type = (mime_type_t *)cupsArrayNext(mime->types))
86 mimeDeleteType(mime, type);
87
88 /*
89 * Free the types and filters arrays, and then the MIME database structure.
90 */
91
92 cupsArrayDelete(mime->types);
93 cupsArrayDelete(mime->filters);
94 cupsArrayDelete(mime->srcs);
95 free(mime);
96 }
97
98
99 /*
100 * 'mimeDeleteFilter()' - Delete a filter from the MIME database.
101 */
102
103 void
mimeDeleteFilter(mime_t * mime,mime_filter_t * filter)104 mimeDeleteFilter(mime_t *mime, /* I - MIME database */
105 mime_filter_t *filter) /* I - Filter */
106 {
107 DEBUG_printf(("mimeDeleteFilter(mime=%p, filter=%p(%s/%s->%s/%s, cost=%d, "
108 "maxsize=" CUPS_LLFMT "))", mime, filter,
109 filter ? filter->src->super : "???",
110 filter ? filter->src->type : "???",
111 filter ? filter->dst->super : "???",
112 filter ? filter->dst->super : "???",
113 filter ? filter->cost : -1,
114 filter ? CUPS_LLCAST filter->maxsize : CUPS_LLCAST -1));
115
116 if (!mime || !filter)
117 return;
118
119 #ifdef DEBUG
120 if (!cupsArrayFind(mime->filters, filter))
121 DEBUG_puts("1mimeDeleteFilter: Filter not in MIME database.");
122 #endif /* DEBUG */
123
124 cupsArrayRemove(mime->filters, filter);
125 free(filter);
126
127 /*
128 * Deleting a filter invalidates the source lookup cache used by
129 * mimeFilter()...
130 */
131
132 if (mime->srcs)
133 {
134 DEBUG_puts("1mimeDeleteFilter: Deleting source lookup cache.");
135 cupsArrayDelete(mime->srcs);
136 mime->srcs = NULL;
137 }
138 }
139
140
141 /*
142 * 'mimeDeleteType()' - Delete a type from the MIME database.
143 */
144
145 void
mimeDeleteType(mime_t * mime,mime_type_t * mt)146 mimeDeleteType(mime_t *mime, /* I - MIME database */
147 mime_type_t *mt) /* I - Type */
148 {
149 DEBUG_printf(("mimeDeleteType(mime=%p, mt=%p(%s/%s))", mime, mt,
150 mt ? mt->super : "???", mt ? mt->type : "???"));
151
152 if (!mime || !mt)
153 return;
154
155 #ifdef DEBUG
156 if (!cupsArrayFind(mime->types, mt))
157 DEBUG_puts("1mimeDeleteFilter: Type not in MIME database.");
158 #endif /* DEBUG */
159
160 cupsArrayRemove(mime->types, mt);
161
162 mime_delete_rules(mt->rules);
163 free(mt);
164 }
165
166
167 /*
168 * '_mimeError()' - Show an error message.
169 */
170
171 void
_mimeError(mime_t * mime,const char * message,...)172 _mimeError(mime_t *mime, /* I - MIME database */
173 const char *message, /* I - Printf-style message string */
174 ...) /* I - Additional arguments as needed */
175 {
176 va_list ap; /* Argument pointer */
177 char buffer[8192]; /* Message buffer */
178
179
180 if (mime->error_cb)
181 {
182 va_start(ap, message);
183 vsnprintf(buffer, sizeof(buffer), message, ap);
184 va_end(ap);
185
186 (*mime->error_cb)(mime->error_ctx, buffer);
187 }
188 }
189
190
191 /*
192 * 'mimeFirstFilter()' - Get the first filter in the MIME database.
193 */
194
195 mime_filter_t * /* O - Filter or NULL */
mimeFirstFilter(mime_t * mime)196 mimeFirstFilter(mime_t *mime) /* I - MIME database */
197 {
198 DEBUG_printf(("6mimeFirstFilter(mime=%p)", mime));
199
200 if (!mime)
201 {
202 DEBUG_puts("7mimeFirstFilter: Returning NULL.");
203 return (NULL);
204 }
205 else
206 {
207 mime_filter_t *first = (mime_filter_t *)cupsArrayFirst(mime->filters);
208 /* First filter */
209
210 DEBUG_printf(("7mimeFirstFilter: Returning %p.", first));
211 return (first);
212 }
213 }
214
215
216 /*
217 * 'mimeFirstType()' - Get the first type in the MIME database.
218 */
219
220 mime_type_t * /* O - Type or NULL */
mimeFirstType(mime_t * mime)221 mimeFirstType(mime_t *mime) /* I - MIME database */
222 {
223 DEBUG_printf(("6mimeFirstType(mime=%p)", mime));
224
225 if (!mime)
226 {
227 DEBUG_puts("7mimeFirstType: Returning NULL.");
228 return (NULL);
229 }
230 else
231 {
232 mime_type_t *first = (mime_type_t *)cupsArrayFirst(mime->types);
233 /* First type */
234
235 DEBUG_printf(("7mimeFirstType: Returning %p.", first));
236 return (first);
237 }
238 }
239
240
241 /*
242 * 'mimeLoad()' - Create a new MIME database from disk.
243 *
244 * This function uses @link mimeLoadFilters@ and @link mimeLoadTypes@ to
245 * create a MIME database from a single directory.
246 */
247
248 mime_t * /* O - New MIME database */
mimeLoad(const char * pathname,const char * filterpath)249 mimeLoad(const char *pathname, /* I - Directory to load */
250 const char *filterpath) /* I - Directory to load */
251 {
252 mime_t *mime; /* New MIME database */
253
254 DEBUG_printf(("mimeLoad(pathname=\"%s\", filterpath=\"%s\")", pathname,
255 filterpath));
256
257 mime = mimeLoadFilters(mimeLoadTypes(NULL, pathname), pathname, filterpath);
258 DEBUG_printf(("1mimeLoad: Returning %p.", mime));
259
260 return (mime);
261 }
262
263
264 /*
265 * 'mimeLoadFilters()' - Load filter definitions from disk.
266 *
267 * This function loads all of the .convs files from the specified directory.
268 * Use @link mimeLoadTypes@ to load all types before you load the filters.
269 */
270
271 mime_t * /* O - MIME database */
mimeLoadFilters(mime_t * mime,const char * pathname,const char * filterpath)272 mimeLoadFilters(mime_t *mime, /* I - MIME database */
273 const char *pathname, /* I - Directory to load from */
274 const char *filterpath) /* I - Default filter program directory */
275 {
276 cups_dir_t *dir; /* Directory */
277 cups_dentry_t *dent; /* Directory entry */
278 char filename[1024]; /* Full filename of .convs file */
279 cups_array_t *filtercache; /* Filter cache */
280
281
282 DEBUG_printf(("mimeLoadFilters(mime=%p, pathname=\"%s\", filterpath=\"%s\")",
283 mime, pathname, filterpath));
284
285 /*
286 * Range check input...
287 */
288
289 if (!mime || !pathname || !filterpath)
290 {
291 DEBUG_puts("1mimeLoadFilters: Bad arguments.");
292 return (mime);
293 }
294
295 /*
296 * Then open the directory specified by pathname...
297 */
298
299 if ((dir = cupsDirOpen(pathname)) == NULL)
300 {
301 DEBUG_printf(("1mimeLoadFilters: Unable to open \"%s\": %s", pathname,
302 strerror(errno)));
303 _mimeError(mime, "Unable to open \"%s\": %s", pathname, strerror(errno));
304 return (mime);
305 }
306
307 /*
308 * Read all the .convs files...
309 */
310
311 filtercache = cupsArrayNew((cups_array_func_t)mime_compare_fcache, NULL);
312
313 while ((dent = cupsDirRead(dir)) != NULL)
314 {
315 if (strlen(dent->filename) > 6 &&
316 !strcmp(dent->filename + strlen(dent->filename) - 6, ".convs"))
317 {
318 /*
319 * Load a mime.convs file...
320 */
321
322 snprintf(filename, sizeof(filename), "%s/%s", pathname, dent->filename);
323 DEBUG_printf(("1mimeLoadFilters: Loading \"%s\".", filename));
324 mime_load_convs(mime, filename, filterpath, filtercache);
325 }
326 }
327
328 mime_delete_fcache(filtercache);
329
330 cupsDirClose(dir);
331
332 return (mime);
333 }
334
335
336 /*
337 * 'mimeLoadTypes()' - Load type definitions from disk.
338 *
339 * This function loads all of the .types files from the specified directory.
340 * Use @link mimeLoadFilters@ to load all filters after you load the types.
341 */
342
343 mime_t * /* O - MIME database */
mimeLoadTypes(mime_t * mime,const char * pathname)344 mimeLoadTypes(mime_t *mime, /* I - MIME database or @code NULL@ to create a new one */
345 const char *pathname) /* I - Directory to load from */
346 {
347 cups_dir_t *dir; /* Directory */
348 cups_dentry_t *dent; /* Directory entry */
349 char filename[1024]; /* Full filename of .types file */
350
351
352 DEBUG_printf(("mimeLoadTypes(mime=%p, pathname=\"%s\")", mime, pathname));
353
354 /*
355 * First open the directory specified by pathname...
356 */
357
358 if ((dir = cupsDirOpen(pathname)) == NULL)
359 {
360 DEBUG_printf(("1mimeLoadTypes: Unable to open \"%s\": %s", pathname,
361 strerror(errno)));
362 DEBUG_printf(("1mimeLoadTypes: Returning %p.", mime));
363 _mimeError(mime, "Unable to open \"%s\": %s", pathname, strerror(errno));
364 return (mime);
365 }
366
367 /*
368 * If "mime" is NULL, make a new, empty database...
369 */
370
371 if (!mime)
372 mime = mimeNew();
373
374 if (!mime)
375 {
376 cupsDirClose(dir);
377 DEBUG_puts("1mimeLoadTypes: Returning NULL.");
378 return (NULL);
379 }
380
381 /*
382 * Read all the .types files...
383 */
384
385 while ((dent = cupsDirRead(dir)) != NULL)
386 {
387 if (strlen(dent->filename) > 6 &&
388 !strcmp(dent->filename + strlen(dent->filename) - 6, ".types"))
389 {
390 /*
391 * Load a mime.types file...
392 */
393
394 snprintf(filename, sizeof(filename), "%s/%s", pathname, dent->filename);
395 DEBUG_printf(("1mimeLoadTypes: Loading \"%s\".", filename));
396 mime_load_types(mime, filename);
397 }
398 }
399
400 cupsDirClose(dir);
401
402 DEBUG_printf(("1mimeLoadTypes: Returning %p.", mime));
403
404 return (mime);
405 }
406
407
408 /*
409 * 'mimeNew()' - Create a new, empty MIME database.
410 */
411
412 mime_t * /* O - MIME database */
mimeNew(void)413 mimeNew(void)
414 {
415 return ((mime_t *)calloc(1, sizeof(mime_t)));
416 }
417
418
419 /*
420 * 'mimeNextFilter()' - Get the next filter in the MIME database.
421 */
422
423 mime_filter_t * /* O - Filter or NULL */
mimeNextFilter(mime_t * mime)424 mimeNextFilter(mime_t *mime) /* I - MIME database */
425 {
426 DEBUG_printf(("6mimeNextFilter(mime=%p)", mime));
427
428 if (!mime)
429 {
430 DEBUG_puts("7mimeNextFilter: Returning NULL.");
431 return (NULL);
432 }
433 else
434 {
435 mime_filter_t *next = (mime_filter_t *)cupsArrayNext(mime->filters);
436 /* Next filter */
437
438 DEBUG_printf(("7mimeNextFilter: Returning %p.", next));
439 return (next);
440 }
441 }
442
443
444 /*
445 * 'mimeNextType()' - Get the next type in the MIME database.
446 */
447
448 mime_type_t * /* O - Type or NULL */
mimeNextType(mime_t * mime)449 mimeNextType(mime_t *mime) /* I - MIME database */
450 {
451 DEBUG_printf(("6mimeNextType(mime=%p)", mime));
452
453 if (!mime)
454 {
455 DEBUG_puts("7mimeNextType: Returning NULL.");
456 return (NULL);
457 }
458 else
459 {
460 mime_type_t *next = (mime_type_t *)cupsArrayNext(mime->types);
461 /* Next type */
462
463 DEBUG_printf(("7mimeNextType: Returning %p.", next));
464 return (next);
465 }
466 }
467
468
469 /*
470 * 'mimeNumFilters()' - Get the number of filters in a MIME database.
471 */
472
473 int
mimeNumFilters(mime_t * mime)474 mimeNumFilters(mime_t *mime) /* I - MIME database */
475 {
476 DEBUG_printf(("mimeNumFilters(mime=%p)", mime));
477
478 if (!mime)
479 {
480 DEBUG_puts("1mimeNumFilters: Returning 0.");
481 return (0);
482 }
483 else
484 {
485 DEBUG_printf(("1mimeNumFilters: Returning %d.",
486 cupsArrayCount(mime->filters)));
487 return (cupsArrayCount(mime->filters));
488 }
489 }
490
491
492 /*
493 * 'mimeNumTypes()' - Get the number of types in a MIME database.
494 */
495
496 int
mimeNumTypes(mime_t * mime)497 mimeNumTypes(mime_t *mime) /* I - MIME database */
498 {
499 DEBUG_printf(("mimeNumTypes(mime=%p)", mime));
500
501 if (!mime)
502 {
503 DEBUG_puts("1mimeNumTypes: Returning 0.");
504 return (0);
505 }
506 else
507 {
508 DEBUG_printf(("1mimeNumTypes: Returning %d.",
509 cupsArrayCount(mime->types)));
510 return (cupsArrayCount(mime->types));
511 }
512 }
513
514
515 /*
516 * 'mimeSetErrorCallback()' - Set the callback for error messages.
517 */
518
519 void
mimeSetErrorCallback(mime_t * mime,mime_error_cb_t cb,void * ctx)520 mimeSetErrorCallback(
521 mime_t *mime, /* I - MIME database */
522 mime_error_cb_t cb, /* I - Callback function */
523 void *ctx) /* I - Context pointer for callback */
524 {
525 if (mime)
526 {
527 mime->error_cb = cb;
528 mime->error_ctx = ctx;
529 }
530 }
531
532
533 /*
534 * 'mime_add_fcache()' - Add a filter to the filter cache.
535 */
536
537 static const char * /* O - Full path to filter or NULL */
mime_add_fcache(cups_array_t * filtercache,const char * name,const char * filterpath)538 mime_add_fcache(
539 cups_array_t *filtercache, /* I - Filter cache */
540 const char *name, /* I - Filter name */
541 const char *filterpath) /* I - Filter path */
542 {
543 _mime_fcache_t key, /* Search key */
544 *temp; /* New filter cache */
545 char path[1024]; /* Full path to filter */
546
547
548 DEBUG_printf(("2mime_add_fcache(filtercache=%p, name=\"%s\", "
549 "filterpath=\"%s\")", filtercache, name, filterpath));
550
551 key.name = (char *)name;
552 if ((temp = (_mime_fcache_t *)cupsArrayFind(filtercache, &key)) != NULL)
553 {
554 DEBUG_printf(("3mime_add_fcache: Returning \"%s\".", temp->path));
555 return (temp->path);
556 }
557
558 if ((temp = calloc(1, sizeof(_mime_fcache_t))) == NULL)
559 {
560 DEBUG_puts("3mime_add_fcache: Returning NULL.");
561 return (NULL);
562 }
563
564 temp->name = strdup(name);
565
566 if (cupsFileFind(name, filterpath, 1, path, sizeof(path)))
567 temp->path = strdup(path);
568
569 cupsArrayAdd(filtercache, temp);
570
571 DEBUG_printf(("3mime_add_fcache: Returning \"%s\".", temp->path));
572 return (temp->path);
573 }
574
575
576 /*
577 * 'mime_compare_fcache()' - Compare two filter cache entries.
578 */
579
580 static int /* O - Result of comparison */
mime_compare_fcache(_mime_fcache_t * a,_mime_fcache_t * b)581 mime_compare_fcache(_mime_fcache_t *a, /* I - First entry */
582 _mime_fcache_t *b) /* I - Second entry */
583 {
584 return (strcmp(a->name, b->name));
585 }
586
587
588 /*
589 * 'mime_delete_fcache()' - Free all memory used by the filter cache.
590 */
591
592 static void
mime_delete_fcache(cups_array_t * filtercache)593 mime_delete_fcache(
594 cups_array_t *filtercache) /* I - Filter cache */
595 {
596 _mime_fcache_t *current; /* Current cache entry */
597
598
599 DEBUG_printf(("2mime_delete_fcache(filtercache=%p)", filtercache));
600
601 for (current = (_mime_fcache_t *)cupsArrayFirst(filtercache);
602 current;
603 current = (_mime_fcache_t *)cupsArrayNext(filtercache))
604 {
605 free(current->name);
606
607 if (current->path)
608 free(current->path);
609
610 free(current);
611 }
612
613 cupsArrayDelete(filtercache);
614 }
615
616
617 /*
618 * 'mime_delete_rules()' - Free all memory for the given rule tree.
619 */
620
621 static void
mime_delete_rules(mime_magic_t * rules)622 mime_delete_rules(mime_magic_t *rules) /* I - Rules to free */
623 {
624 mime_magic_t *next; /* Next rule to free */
625
626
627 DEBUG_printf(("2mime_delete_rules(rules=%p)", rules));
628
629 /*
630 * Free the rules list, descending recursively to free any child rules.
631 */
632
633 while (rules != NULL)
634 {
635 next = rules->next;
636
637 if (rules->child != NULL)
638 mime_delete_rules(rules->child);
639
640 if (rules->op == MIME_MAGIC_REGEX)
641 regfree(&(rules->value.rev));
642
643 free(rules);
644 rules = next;
645 }
646 }
647
648
649 /*
650 * 'mime_load_convs()' - Load a xyz.convs file.
651 */
652
653 static void
mime_load_convs(mime_t * mime,const char * filename,const char * filterpath,cups_array_t * filtercache)654 mime_load_convs(
655 mime_t *mime, /* I - MIME database */
656 const char *filename, /* I - Convs file to load */
657 const char *filterpath, /* I - Path for filters */
658 cups_array_t *filtercache) /* I - Filter program cache */
659 {
660 cups_file_t *fp; /* Convs file */
661 char line[1024], /* Input line from file */
662 *lineptr, /* Current position in line */
663 super[MIME_MAX_SUPER], /* Super-type name */
664 type[MIME_MAX_TYPE], /* Type name */
665 *temp, /* Temporary pointer */
666 *filter; /* Filter program */
667 mime_type_t *temptype, /* MIME type looping var */
668 *dsttype; /* Destination MIME type */
669 int cost; /* Cost of filter */
670
671
672 DEBUG_printf(("2mime_load_convs(mime=%p, filename=\"%s\", filterpath=\"%s\", "
673 "filtercache=%p)", mime, filename, filterpath, filtercache));
674
675 /*
676 * First try to open the file...
677 */
678
679 if ((fp = cupsFileOpen(filename, "r")) == NULL)
680 {
681 DEBUG_printf(("3mime_load_convs: Unable to open \"%s\": %s", filename,
682 strerror(errno)));
683 _mimeError(mime, "Unable to open \"%s\": %s", filename, strerror(errno));
684 return;
685 }
686
687 /*
688 * Then read each line from the file, skipping any comments in the file...
689 */
690
691 while (cupsFileGets(fp, line, sizeof(line)) != NULL)
692 {
693 /*
694 * Skip blank lines and lines starting with a #...
695 */
696
697 if (!line[0] || line[0] == '#')
698 continue;
699
700 /*
701 * Strip trailing whitespace...
702 */
703
704 for (lineptr = line + strlen(line) - 1;
705 lineptr >= line && isspace(*lineptr & 255);
706 lineptr --)
707 *lineptr = '\0';
708
709 /*
710 * Extract the destination super-type and type names from the middle of
711 * the line.
712 */
713
714 lineptr = line;
715 while (*lineptr != ' ' && *lineptr != '\t' && *lineptr != '\0')
716 lineptr ++;
717
718 while (*lineptr == ' ' || *lineptr == '\t')
719 lineptr ++;
720
721 temp = super;
722
723 while (*lineptr != '/' && *lineptr != '\n' && *lineptr != '\0' &&
724 (temp - super + 1) < MIME_MAX_SUPER)
725 *temp++ = (char)tolower(*lineptr++ & 255);
726
727 *temp = '\0';
728
729 if (*lineptr != '/')
730 continue;
731
732 lineptr ++;
733 temp = type;
734
735 while (*lineptr != ' ' && *lineptr != '\t' && *lineptr != '\n' &&
736 *lineptr != '\0' && (temp - type + 1) < MIME_MAX_TYPE)
737 *temp++ = (char)tolower(*lineptr++ & 255);
738
739 *temp = '\0';
740
741 if (*lineptr == '\0' || *lineptr == '\n')
742 continue;
743
744 if ((dsttype = mimeType(mime, super, type)) == NULL)
745 {
746 DEBUG_printf(("3mime_load_convs: Destination type %s/%s not found.",
747 super, type));
748 continue;
749 }
750
751 /*
752 * Then get the cost and filter program...
753 */
754
755 while (*lineptr == ' ' || *lineptr == '\t')
756 lineptr ++;
757
758 if (*lineptr < '0' || *lineptr > '9')
759 continue;
760
761 cost = atoi(lineptr);
762
763 while (*lineptr != ' ' && *lineptr != '\t' && *lineptr != '\0')
764 lineptr ++;
765 while (*lineptr == ' ' || *lineptr == '\t')
766 lineptr ++;
767
768 if (*lineptr == '\0' || *lineptr == '\n')
769 continue;
770
771 filter = lineptr;
772
773 if (strcmp(filter, "-"))
774 {
775 /*
776 * Verify that the filter exists and is executable...
777 */
778
779 if (!mime_add_fcache(filtercache, filter, filterpath))
780 {
781 DEBUG_printf(("mime_load_convs: Filter %s not found in %s.", filter,
782 filterpath));
783 _mimeError(mime, "Filter \"%s\" not found.", filter);
784 continue;
785 }
786 }
787
788 /*
789 * Finally, get the source super-type and type names from the beginning of
790 * the line. We do it here so we can support wildcards...
791 */
792
793 lineptr = line;
794 temp = super;
795
796 while (*lineptr != '/' && *lineptr != '\n' && *lineptr != '\0' &&
797 (temp - super + 1) < MIME_MAX_SUPER)
798 *temp++ = (char)tolower(*lineptr++ & 255);
799
800 *temp = '\0';
801
802 if (*lineptr != '/')
803 continue;
804
805 lineptr ++;
806 temp = type;
807
808 while (*lineptr != ' ' && *lineptr != '\t' && *lineptr != '\n' &&
809 *lineptr != '\0' && (temp - type + 1) < MIME_MAX_TYPE)
810 *temp++ = (char)tolower(*lineptr++ & 255);
811
812 *temp = '\0';
813
814 if (!strcmp(super, "*") && !strcmp(type, "*"))
815 {
816 /*
817 * Force * / * to be "application/octet-stream"...
818 */
819
820 strlcpy(super, "application", sizeof(super));
821 strlcpy(type, "octet-stream", sizeof(type));
822 }
823
824 /*
825 * Add the filter to the MIME database, supporting wildcards as needed...
826 */
827
828 for (temptype = (mime_type_t *)cupsArrayFirst(mime->types);
829 temptype;
830 temptype = (mime_type_t *)cupsArrayNext(mime->types))
831 if ((super[0] == '*' || !strcmp(temptype->super, super)) &&
832 (type[0] == '*' || !strcmp(temptype->type, type)))
833 mimeAddFilter(mime, temptype, dsttype, cost, filter);
834 }
835
836 cupsFileClose(fp);
837 }
838
839
840 /*
841 * 'mime_load_types()' - Load a xyz.types file.
842 */
843
844 static void
mime_load_types(mime_t * mime,const char * filename)845 mime_load_types(mime_t *mime, /* I - MIME database */
846 const char *filename) /* I - Types file to load */
847 {
848 cups_file_t *fp; /* Types file */
849 size_t linelen; /* Length of line */
850 char line[32768], /* Input line from file */
851 *lineptr, /* Current position in line */
852 super[MIME_MAX_SUPER], /* Super-type name */
853 type[MIME_MAX_TYPE], /* Type name */
854 *temp; /* Temporary pointer */
855 mime_type_t *typeptr; /* New MIME type */
856
857
858 DEBUG_printf(("2mime_load_types(mime=%p, filename=\"%s\")", mime, filename));
859
860 /*
861 * First try to open the file...
862 */
863
864 if ((fp = cupsFileOpen(filename, "r")) == NULL)
865 {
866 DEBUG_printf(("3mime_load_types: Unable to open \"%s\": %s", filename,
867 strerror(errno)));
868 _mimeError(mime, "Unable to open \"%s\": %s", filename, strerror(errno));
869 return;
870 }
871
872 /*
873 * Then read each line from the file, skipping any comments in the file...
874 */
875
876 while (cupsFileGets(fp, line, sizeof(line)) != NULL)
877 {
878 /*
879 * Skip blank lines and lines starting with a #...
880 */
881
882 if (!line[0] || line[0] == '#')
883 continue;
884
885 /*
886 * While the last character in the line is a backslash, continue on to the
887 * next line (and the next, etc.)
888 */
889
890 linelen = strlen(line);
891
892 while (line[linelen - 1] == '\\')
893 {
894 linelen --;
895
896 if (cupsFileGets(fp, line + linelen, sizeof(line) - linelen) == NULL)
897 line[linelen] = '\0';
898 else
899 linelen += strlen(line + linelen);
900 }
901
902 /*
903 * Extract the super-type and type names from the beginning of the line.
904 */
905
906 lineptr = line;
907 temp = super;
908
909 while (*lineptr != '/' && *lineptr != '\n' && *lineptr != '\0' &&
910 (temp - super + 1) < MIME_MAX_SUPER)
911 *temp++ = (char)tolower(*lineptr++ & 255);
912
913 *temp = '\0';
914
915 if (*lineptr != '/')
916 continue;
917
918 lineptr ++;
919 temp = type;
920
921 while (*lineptr != ' ' && *lineptr != '\t' && *lineptr != '\n' &&
922 *lineptr != '\0' && (temp - type + 1) < MIME_MAX_TYPE)
923 *temp++ = (char)tolower(*lineptr++ & 255);
924
925 *temp = '\0';
926
927 /*
928 * Add the type and rules to the MIME database...
929 */
930
931 typeptr = mimeAddType(mime, super, type);
932 mimeAddTypeRule(typeptr, lineptr);
933 }
934
935 cupsFileClose(fp);
936 }
937