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