• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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