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