• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * File type conversion routines for CUPS.
3  *
4  * Copyright © 2020-2024 by OpenPrinting.
5  * Copyright 2007-2011 by Apple Inc.
6  * Copyright 1997-2007 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 "mime.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_typelist_s		/**** List of source types ****/
32 {
33   struct _mime_typelist_s *next;	/* Next source type */
34   mime_type_t		*src;		/* Source type */
35 } _mime_typelist_t;
36 
37 
38 /*
39  * Local functions...
40  */
41 
42 static int		mime_compare_filters(mime_filter_t *, mime_filter_t *);
43 static int		mime_compare_srcs(mime_filter_t *, mime_filter_t *);
44 static cups_array_t	*mime_find_filters(mime_t *mime, mime_type_t *src,
45 				      size_t srcsize, mime_type_t *dst,
46 				      int *cost, _mime_typelist_t *visited);
47 
48 
49 /*
50  * 'mimeAddFilter()' - Add a filter to the current MIME database.
51  */
52 
53 mime_filter_t *				/* O - New filter */
mimeAddFilter(mime_t * mime,mime_type_t * src,mime_type_t * dst,int cost,const char * filter)54 mimeAddFilter(mime_t      *mime,	/* I - MIME database */
55               mime_type_t *src,		/* I - Source type */
56 	      mime_type_t *dst,		/* I - Destination type */
57               int         cost,		/* I - Relative time/resource cost */
58 	      const char  *filter)	/* I - Filter program to run */
59 {
60   mime_filter_t	*temp;			/* New filter */
61 
62 
63   DEBUG_printf(("mimeAddFilter(mime=%p, src=%p(%s/%s), dst=%p(%s/%s), cost=%d, "
64                 "filter=\"%s\")", mime,
65 		src, src ? src->super : "???", src ? src->type : "???",
66 		dst, dst ? dst->super : "???", dst ? dst->type : "???",
67 		cost, filter));
68 
69  /*
70   * Range-check the input...
71   */
72 
73   if (!mime || !src || !dst || !filter)
74   {
75     DEBUG_puts("1mimeAddFilter: Returning NULL.");
76     return (NULL);
77   }
78 
79  /*
80   * See if we already have an existing filter for the given source and
81   * destination...
82   */
83 
84   if ((temp = mimeFilterLookup(mime, src, dst)) != NULL)
85   {
86    /*
87     * Yup, does the existing filter have a higher cost?  If so, copy the
88     * filter and cost to the existing filter entry and return it...
89     */
90 
91     if (temp->cost > cost)
92     {
93       DEBUG_printf(("1mimeAddFilter: Replacing filter \"%s\", cost %d.",
94                     temp->filter, temp->cost));
95       temp->cost = cost;
96       strlcpy(temp->filter, filter, sizeof(temp->filter));
97     }
98   }
99   else
100   {
101    /*
102     * Nope, add a new one...
103     */
104 
105     if (!mime->filters)
106       mime->filters = cupsArrayNew((cups_array_func_t)mime_compare_filters, NULL);
107 
108     if (!mime->filters)
109       return (NULL);
110 
111     if ((temp = calloc(1, sizeof(mime_filter_t))) == NULL)
112       return (NULL);
113 
114    /*
115     * Copy the information over and sort if necessary...
116     */
117 
118     temp->src  = src;
119     temp->dst  = dst;
120     temp->cost = cost;
121     strlcpy(temp->filter, filter, sizeof(temp->filter));
122 
123     DEBUG_puts("1mimeAddFilter: Adding new filter.");
124     cupsArrayAdd(mime->filters, temp);
125     cupsArrayAdd(mime->srcs, temp);
126   }
127 
128  /*
129   * Return the new/updated filter...
130   */
131 
132   DEBUG_printf(("1mimeAddFilter: Returning %p.", temp));
133 
134   return (temp);
135 }
136 
137 
138 /*
139  * 'mimeFilter()' - Find the fastest way to convert from one type to another.
140  */
141 
142 cups_array_t *				/* O - Array of filters to run */
mimeFilter(mime_t * mime,mime_type_t * src,mime_type_t * dst,int * cost)143 mimeFilter(mime_t      *mime,		/* I - MIME database */
144            mime_type_t *src,		/* I - Source file type */
145 	   mime_type_t *dst,		/* I - Destination file type */
146 	   int         *cost)		/* O - Cost of filters */
147 {
148   DEBUG_printf(("mimeFilter(mime=%p, src=%p(%s/%s), dst=%p(%s/%s), "
149                 "cost=%p(%d))", mime,
150         	src, src ? src->super : "???", src ? src->type : "???",
151 		dst, dst ? dst->super : "???", dst ? dst->type : "???",
152 		cost, cost ? *cost : 0));
153 
154   return (mimeFilter2(mime, src, 0, dst, cost));
155 }
156 
157 
158 /*
159  * 'mimeFilter2()' - Find the fastest way to convert from one type to another,
160  *                   including file size.
161  */
162 
163 cups_array_t *				/* O - Array of filters to run */
mimeFilter2(mime_t * mime,mime_type_t * src,size_t srcsize,mime_type_t * dst,int * cost)164 mimeFilter2(mime_t      *mime,		/* I - MIME database */
165             mime_type_t *src,		/* I - Source file type */
166 	    size_t      srcsize,	/* I - Size of source file */
167 	    mime_type_t *dst,		/* I - Destination file type */
168 	    int         *cost)		/* O - Cost of filters */
169 {
170   cups_array_t	*filters;		/* Array of filters to run */
171 
172 
173  /*
174   * Range-check the input...
175   */
176 
177   DEBUG_printf(("mimeFilter2(mime=%p, src=%p(%s/%s), srcsize=" CUPS_LLFMT
178                 ", dst=%p(%s/%s), cost=%p(%d))", mime,
179         	src, src ? src->super : "???", src ? src->type : "???",
180 		CUPS_LLCAST srcsize,
181 		dst, dst ? dst->super : "???", dst ? dst->type : "???",
182 		cost, cost ? *cost : 0));
183 
184   if (cost)
185     *cost = 0;
186 
187   if (!mime || !src || !dst)
188     return (NULL);
189 
190  /*
191   * (Re)build the source lookup array as needed...
192   */
193 
194   if (!mime->srcs)
195   {
196     mime_filter_t	*current;	/* Current filter */
197 
198     mime->srcs = cupsArrayNew((cups_array_func_t)mime_compare_srcs, NULL);
199 
200     for (current = mimeFirstFilter(mime);
201          current;
202 	 current = mimeNextFilter(mime))
203       cupsArrayAdd(mime->srcs, current);
204   }
205 
206  /*
207   * Find the filters...
208   */
209 
210   filters = mime_find_filters(mime, src, srcsize, dst, cost, NULL);
211 
212   DEBUG_printf(("1mimeFilter2: Returning %d filter(s), cost %d:",
213                 cupsArrayCount(filters), cost ? *cost : -1));
214 #ifdef DEBUG
215   {
216     mime_filter_t	*filter;	/* Current filter */
217 
218     for (filter = (mime_filter_t *)cupsArrayFirst(filters);
219          filter;
220 	 filter = (mime_filter_t *)cupsArrayNext(filters))
221       DEBUG_printf(("1mimeFilter2: %s/%s %s/%s %d %s", filter->src->super,
222                     filter->src->type, filter->dst->super, filter->dst->type,
223 		    filter->cost, filter->filter));
224   }
225 #endif /* DEBUG */
226 
227   return (filters);
228 }
229 
230 
231 /*
232  * 'mimeFilterLookup()' - Lookup a filter.
233  */
234 
235 mime_filter_t *				/* O - Filter for src->dst */
mimeFilterLookup(mime_t * mime,mime_type_t * src,mime_type_t * dst)236 mimeFilterLookup(mime_t      *mime,	/* I - MIME database */
237                  mime_type_t *src,	/* I - Source type */
238                  mime_type_t *dst)	/* I - Destination type */
239 {
240   mime_filter_t	key,			/* Key record for filter search */
241 		*filter;		/* Matching filter */
242 
243 
244   DEBUG_printf(("2mimeFilterLookup(mime=%p, src=%p(%s/%s), dst=%p(%s/%s))", mime,
245                 src, src ? src->super : "???", src ? src->type : "???",
246 		dst, dst ? dst->super : "???", dst ? dst->type : "???"));
247 
248   key.src = src;
249   key.dst = dst;
250 
251   filter = (mime_filter_t *)cupsArrayFind(mime->filters, &key);
252   DEBUG_printf(("3mimeFilterLookup: Returning %p(%s).", filter,
253                 filter ? filter->filter : "???"));
254   return (filter);
255 }
256 
257 
258 /*
259  * 'mime_compare_filters()' - Compare two filters.
260  */
261 
262 static int				/* O - Comparison result */
mime_compare_filters(mime_filter_t * f0,mime_filter_t * f1)263 mime_compare_filters(mime_filter_t *f0,	/* I - First filter */
264                      mime_filter_t *f1)	/* I - Second filter */
265 {
266   int	i;				/* Result of comparison */
267 
268 
269   if ((i = strcmp(f0->src->super, f1->src->super)) == 0)
270     if ((i = strcmp(f0->src->type, f1->src->type)) == 0)
271       if ((i = strcmp(f0->dst->super, f1->dst->super)) == 0)
272         i = strcmp(f0->dst->type, f1->dst->type);
273 
274   return (i);
275 }
276 
277 
278 /*
279  * 'mime_compare_srcs()' - Compare two filter source types.
280  */
281 
282 static int				/* O - Comparison result */
mime_compare_srcs(mime_filter_t * f0,mime_filter_t * f1)283 mime_compare_srcs(mime_filter_t *f0,	/* I - First filter */
284                   mime_filter_t *f1)	/* I - Second filter */
285 {
286   int	i;				/* Result of comparison */
287 
288 
289   if ((i = strcmp(f0->src->super, f1->src->super)) == 0)
290     i = strcmp(f0->src->type, f1->src->type);
291 
292   return (i);
293 }
294 
295 
296 /*
297  * 'mime_find_filters()' - Find the filters to convert from one type to another.
298  */
299 
300 static cups_array_t *			/* O - Array of filters to run */
mime_find_filters(mime_t * mime,mime_type_t * src,size_t srcsize,mime_type_t * dst,int * cost,_mime_typelist_t * list)301 mime_find_filters(
302     mime_t           *mime,		/* I - MIME database */
303     mime_type_t      *src,		/* I - Source file type */
304     size_t           srcsize,		/* I - Size of source file */
305     mime_type_t      *dst,		/* I - Destination file type */
306     int              *cost,		/* O - Cost of filters */
307     _mime_typelist_t *list)		/* I - Source types we've used */
308 {
309   int			tempcost,	/* Temporary cost */
310 			mincost;	/* Current minimum */
311   cups_array_t		*temp,		/* Temporary filter */
312 			*mintemp;	/* Current minimum */
313   mime_filter_t		*current,	/* Current filter */
314 			srckey;		/* Source type key */
315   _mime_typelist_t	listnode,	/* New list node */
316 			*listptr;	/* Pointer in list */
317 
318 
319   DEBUG_printf(("2mime_find_filters(mime=%p, src=%p(%s/%s), srcsize=" CUPS_LLFMT
320                 ", dst=%p(%s/%s), cost=%p, list=%p)", mime, src, src->super,
321 		src->type, CUPS_LLCAST srcsize, dst, dst->super, dst->type,
322 		cost, list));
323 
324  /*
325   * See if there is a filter that can convert the files directly...
326   */
327 
328   if ((current = mimeFilterLookup(mime, src, dst)) != NULL &&
329       (current->maxsize == 0 || srcsize <= current->maxsize))
330   {
331    /*
332     * Got a direct filter!
333     */
334 
335     DEBUG_puts("3mime_find_filters: Direct filter found.");
336 
337     if ((mintemp = cupsArrayNew(NULL, NULL)) == NULL)
338     {
339       DEBUG_puts("3mime_find_filters: Returning NULL (out of memory).");
340       return (NULL);
341     }
342 
343     cupsArrayAdd(mintemp, current);
344 
345     mincost = current->cost;
346 
347     if (!cost)
348     {
349       DEBUG_printf(("3mime_find_filters: Returning 1 filter, cost %d:",
350                     mincost));
351       DEBUG_printf(("3mime_find_filters: %s/%s %s/%s %d %s",
352                     current->src->super, current->src->type,
353                     current->dst->super, current->dst->type,
354 		    current->cost, current->filter));
355       return (mintemp);
356     }
357   }
358   else
359   {
360    /*
361     * No direct filter...
362     */
363 
364     mintemp = NULL;
365     mincost = 9999999;
366   }
367 
368  /*
369   * Initialize this node in the type list...
370   */
371 
372   listnode.next = list;
373 
374  /*
375   * OK, now look for filters from the source type to any other type...
376   */
377 
378   srckey.src = src;
379 
380   for (current = (mime_filter_t *)cupsArrayFind(mime->srcs, &srckey);
381        current && current->src == src;
382        current = (mime_filter_t *)cupsArrayNext(mime->srcs))
383   {
384    /*
385     * See if we have already tried the destination type as a source
386     * type (this avoids extra filter looping...)
387     */
388 
389     mime_type_t *current_dst;		/* Current destination type */
390 
391     if (current->maxsize > 0 && srcsize > current->maxsize)
392       continue;
393 
394     for (listptr = list, current_dst = current->dst;
395 	 listptr;
396 	 listptr = listptr->next)
397       if (current_dst == listptr->src)
398 	break;
399 
400     if (listptr)
401       continue;
402 
403    /*
404     * See if we have any filters that can convert from the destination type
405     * of this filter to the final type...
406     */
407 
408     listnode.src = current->src;
409 
410     cupsArraySave(mime->srcs);
411     temp = mime_find_filters(mime, current->dst, srcsize, dst, &tempcost,
412                              &listnode);
413     cupsArrayRestore(mime->srcs);
414 
415     if (!temp)
416       continue;
417 
418     if (!cost)
419     {
420       DEBUG_printf(("3mime_find_filters: Returning %d filter(s), cost %d:",
421 		    cupsArrayCount(temp), tempcost));
422 
423 #ifdef DEBUG
424       for (current = (mime_filter_t *)cupsArrayFirst(temp);
425 	   current;
426 	   current = (mime_filter_t *)cupsArrayNext(temp))
427 	DEBUG_printf(("3mime_find_filters: %s/%s %s/%s %d %s",
428 		      current->src->super, current->src->type,
429 		      current->dst->super, current->dst->type,
430 		      current->cost, current->filter));
431 #endif /* DEBUG */
432 
433       return (temp);
434     }
435 
436    /*
437     * Found a match; see if this one is less costly than the last (if
438     * any...)
439     */
440 
441     tempcost += current->cost;
442 
443     if (tempcost < mincost)
444     {
445       cupsArrayDelete(mintemp);
446 
447      /*
448       * Hey, we got a match!  Add the current filter to the beginning of the
449       * filter list...
450       */
451 
452       mintemp = temp;
453       mincost = tempcost;
454       cupsArrayInsert(mintemp, current);
455     }
456     else
457       cupsArrayDelete(temp);
458   }
459 
460   if (mintemp)
461   {
462    /*
463     * Hey, we got a match!
464     */
465 
466     DEBUG_printf(("3mime_find_filters: Returning %d filter(s), cost %d:",
467                   cupsArrayCount(mintemp), mincost));
468 
469 #ifdef DEBUG
470     for (current = (mime_filter_t *)cupsArrayFirst(mintemp);
471          current;
472 	 current = (mime_filter_t *)cupsArrayNext(mintemp))
473       DEBUG_printf(("3mime_find_filters: %s/%s %s/%s %d %s",
474                     current->src->super, current->src->type,
475                     current->dst->super, current->dst->type,
476 		    current->cost, current->filter));
477 #endif /* DEBUG */
478 
479     if (cost)
480       *cost = mincost;
481 
482     return (mintemp);
483   }
484 
485   DEBUG_puts("3mime_find_filters: Returning NULL (no matches).");
486 
487   return (NULL);
488 }
489