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