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