1 /*
2 * MIME test program 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 <cups/debug-private.h>
18 #include <cups/ppd-private.h>
19 #include "mime.h"
20
21
22 /*
23 * Local functions...
24 */
25
26 static void add_ppd_filter(mime_t *mime, mime_type_t *filtertype,
27 const char *filter);
28 static void add_ppd_filters(mime_t *mime, ppd_file_t *ppd);
29 static void print_rules(mime_magic_t *rules);
30 static void type_dir(mime_t *mime, const char *dirname);
31
32
33 /*
34 * 'main()' - Main entry for the test program.
35 */
36
37 int /* O - Exit status */
main(int argc,char * argv[])38 main(int argc, /* I - Number of command-line args */
39 char *argv[]) /* I - Command-line arguments */
40 {
41 int i; /* Looping vars */
42 const char *filter_path; /* Filter path */
43 char super[MIME_MAX_SUPER], /* Super-type name */
44 type[MIME_MAX_TYPE]; /* Type name */
45 int compression; /* Compression of file */
46 int cost; /* Cost of filters */
47 mime_t *mime; /* MIME database */
48 mime_type_t *src, /* Source type */
49 *dst; /* Destination type */
50 struct stat srcinfo; /* Source information */
51 ppd_file_t *ppd; /* PPD file */
52 cups_array_t *filters; /* Filters for the file */
53 mime_filter_t *filter; /* Current filter */
54
55
56 mime = NULL;
57 src = NULL;
58 dst = NULL;
59 ppd = NULL;
60 filter_path = "../filter:" CUPS_SERVERBIN "/filter";
61
62 srcinfo.st_size = 0;
63
64 for (i = 1; i < argc; i ++)
65 if (!strcmp(argv[i], "-d"))
66 {
67 i ++;
68
69 if (i < argc)
70 {
71 mime = mimeLoad(argv[i], filter_path);
72
73 if (ppd)
74 add_ppd_filters(mime, ppd);
75 }
76 }
77 else if (!strcmp(argv[i], "-f"))
78 {
79 i ++;
80
81 if (i < argc)
82 filter_path = argv[i];
83 }
84 else if (!strcmp(argv[i], "-p"))
85 {
86 i ++;
87
88 if (i < argc)
89 {
90 ppd = ppdOpenFile(argv[i]);
91
92 if (mime)
93 add_ppd_filters(mime, ppd);
94 }
95 }
96 else if (!src)
97 {
98 if (!mime)
99 mime = mimeLoad("../conf", filter_path);
100
101 if (ppd)
102 add_ppd_filters(mime, ppd);
103
104 src = mimeFileType(mime, argv[i], NULL, &compression);
105 stat(argv[i], &srcinfo);
106
107 if (src)
108 printf("%s: %s/%s%s\n", argv[i], src->super, src->type,
109 compression ? " (gzipped)" : "");
110 else if ((src = mimeType(mime, "application", "octet-stream")) != NULL)
111 printf("%s: application/octet-stream\n", argv[i]);
112 else
113 {
114 printf("%s: unknown\n", argv[i]);
115 if (mime)
116 mimeDelete(mime);
117 return (1);
118 }
119 }
120 else
121 {
122 sscanf(argv[i], "%15[^/]/%255s", super, type);
123 dst = mimeType(mime, super, type);
124
125 filters = mimeFilter2(mime, src, (size_t)srcinfo.st_size, dst, &cost);
126
127 if (!filters)
128 {
129 printf("No filters to convert from %s/%s to %s.\n", src->super,
130 src->type, argv[i]);
131 }
132 else
133 {
134 int first = 1; /* First filter shown? */
135
136 printf("Filter cost = %d\n", cost);
137
138 for (filter = (mime_filter_t *)cupsArrayFirst(filters);
139 filter;
140 filter = (mime_filter_t *)cupsArrayNext(filters))
141 {
142 if (!strcmp(filter->filter, "-"))
143 continue;
144
145 if (first)
146 {
147 first = 0;
148 fputs(filter->filter, stdout);
149 }
150 else
151 printf(" | %s", filter->filter);
152 }
153
154 putchar('\n');
155
156 cupsArrayDelete(filters);
157 }
158 }
159
160 if (!mime)
161 {
162 mime = mimeLoad("../conf", filter_path);
163 if (ppd)
164 add_ppd_filters(mime, ppd);
165 }
166
167 if (!src)
168 {
169 puts("MIME database types:");
170 for (src = mimeFirstType(mime); src; src = mimeNextType(mime))
171 {
172 printf("\t%s/%s (%d):\n", src->super, src->type, src->priority);
173 print_rules(src->rules);
174 puts("");
175 }
176
177 puts("");
178
179 puts("MIME database filters:");
180 for (filter = mimeFirstFilter(mime); filter; filter = mimeNextFilter(mime))
181 printf("\t%s/%s to %s/%s: %s (%d)\n",
182 filter->src->super, filter->src->type,
183 filter->dst->super, filter->dst->type,
184 filter->filter, filter->cost);
185
186 type_dir(mime, "../doc");
187 }
188
189 return (0);
190 }
191
192
193 /*
194 * 'add_printer_filter()' - Add a printer filter from a PPD.
195 */
196
197 static void
add_ppd_filter(mime_t * mime,mime_type_t * filtertype,const char * filter)198 add_ppd_filter(mime_t *mime, /* I - MIME database */
199 mime_type_t *filtertype, /* I - Filter or prefilter MIME type */
200 const char *filter) /* I - Filter to add */
201 {
202 char super[MIME_MAX_SUPER], /* Super-type for filter */
203 type[MIME_MAX_TYPE], /* Type for filter */
204 dsuper[MIME_MAX_SUPER], /* Destination super-type for filter */
205 dtype[MIME_MAX_TYPE], /* Destination type for filter */
206 dest[MIME_MAX_SUPER + MIME_MAX_TYPE + 2],
207 /* Destination super/type */
208 program[1024]; /* Program/filter name */
209 int cost; /* Cost of filter */
210 size_t maxsize = 0; /* Maximum supported file size */
211 mime_type_t *temptype, /* MIME type looping var */
212 *desttype; /* Destination MIME type */
213 mime_filter_t *filterptr; /* MIME filter */
214
215
216 /*
217 * Parse the filter string; it should be in one of the following formats:
218 *
219 * source/type cost program
220 * source/type cost maxsize(nnnn) program
221 * source/type dest/type cost program
222 * source/type dest/type cost maxsize(nnnn) program
223 */
224
225 if (sscanf(filter, "%15[^/]/%255s%*[ \t]%15[^/]/%255s%d%*[ \t]%1023[^\n]",
226 super, type, dsuper, dtype, &cost, program) == 6)
227 {
228 snprintf(dest, sizeof(dest), "test/%s/%s", dsuper, dtype);
229
230 if ((desttype = mimeType(mime, "printer", dest)) == NULL)
231 desttype = mimeAddType(mime, "printer", dest);
232 }
233 else
234 {
235 if (sscanf(filter, "%15[^/]/%255s%d%*[ \t]%1023[^\n]", super, type, &cost,
236 program) == 4)
237 {
238 desttype = filtertype;
239 }
240 else
241 {
242 printf("testmime: Invalid filter string \"%s\".\n", filter);
243 return;
244 }
245 }
246
247 if (!strncmp(program, "maxsize(", 8))
248 {
249 char *ptr; /* Pointer into maxsize(nnnn) program */
250
251 maxsize = (size_t)strtoll(program + 8, &ptr, 10);
252
253 if (*ptr != ')')
254 {
255 printf("testmime: Invalid filter string \"%s\".\n", filter);
256 return;
257 }
258
259 ptr ++;
260 while (_cups_isspace(*ptr))
261 ptr ++;
262
263 _cups_strcpy(program, ptr);
264 }
265
266 /*
267 * Add the filter to the MIME database, supporting wildcards as needed...
268 */
269
270 for (temptype = mimeFirstType(mime);
271 temptype;
272 temptype = mimeNextType(mime))
273 if (((super[0] == '*' && _cups_strcasecmp(temptype->super, "printer")) ||
274 !_cups_strcasecmp(temptype->super, super)) &&
275 (type[0] == '*' || !_cups_strcasecmp(temptype->type, type)))
276 {
277 if (desttype != filtertype)
278 {
279 filterptr = mimeAddFilter(mime, temptype, desttype, cost, program);
280
281 if (!mimeFilterLookup(mime, desttype, filtertype))
282 mimeAddFilter(mime, desttype, filtertype, 0, "-");
283 }
284 else
285 filterptr = mimeAddFilter(mime, temptype, filtertype, cost, program);
286
287 if (filterptr)
288 filterptr->maxsize = maxsize;
289 }
290 }
291
292
293 /*
294 * 'add_ppd_filters()' - Add all filters from a PPD.
295 */
296
297 static void
add_ppd_filters(mime_t * mime,ppd_file_t * ppd)298 add_ppd_filters(mime_t *mime, /* I - MIME database */
299 ppd_file_t *ppd) /* I - PPD file */
300 {
301 _ppd_cache_t *pc; /* Cache data for PPD */
302 const char *value; /* Filter definition value */
303 mime_type_t *filter, /* Filter type */
304 *prefilter; /* Pre-filter type */
305
306
307 pc = _ppdCacheCreateWithPPD(ppd);
308 if (!pc)
309 return;
310
311 filter = mimeAddType(mime, "printer", "test");
312
313 if (pc->filters)
314 {
315 for (value = (const char *)cupsArrayFirst(pc->filters);
316 value;
317 value = (const char *)cupsArrayNext(pc->filters))
318 add_ppd_filter(mime, filter, value);
319 }
320 else
321 {
322 add_ppd_filter(mime, filter, "application/vnd.cups-raw 0 -");
323 add_ppd_filter(mime, filter, "application/vnd.cups-postscript 0 -");
324 }
325
326 if (pc->prefilters)
327 {
328 prefilter = mimeAddType(mime, "prefilter", "test");
329
330 for (value = (const char *)cupsArrayFirst(pc->prefilters);
331 value;
332 value = (const char *)cupsArrayNext(pc->prefilters))
333 add_ppd_filter(mime, prefilter, value);
334 }
335 }
336
337
338 /*
339 * 'print_rules()' - Print the rules for a file type...
340 */
341
342 static void
print_rules(mime_magic_t * rules)343 print_rules(mime_magic_t *rules) /* I - Rules to print */
344 {
345 int i; /* Looping var */
346 static char indent[255] = "\t"; /* Indentation for rules */
347
348
349 if (rules == NULL)
350 return;
351
352 while (rules != NULL)
353 {
354 printf("%s[%p] ", indent, rules);
355
356 if (rules->invert)
357 printf("NOT ");
358
359 switch (rules->op)
360 {
361 case MIME_MAGIC_MATCH :
362 printf("match(%s)", rules->value.matchv);
363 break;
364 case MIME_MAGIC_LOCALE :
365 printf("locale(%s)", rules->value.localev);
366 break;
367 case MIME_MAGIC_ASCII :
368 printf("ascii(%d,%d)", rules->offset, rules->length);
369 break;
370 case MIME_MAGIC_PRINTABLE :
371 printf("printable(%d,%d)", rules->offset, rules->length);
372 break;
373 case MIME_MAGIC_STRING :
374 printf("string(%d,", rules->offset);
375 for (i = 0; i < rules->length; i ++)
376 if (rules->value.stringv[i] < ' ' ||
377 rules->value.stringv[i] > 126)
378 printf("<%02X>", rules->value.stringv[i]);
379 else
380 putchar(rules->value.stringv[i]);
381 putchar(')');
382 break;
383 case MIME_MAGIC_CHAR :
384 printf("char(%d,%d)", rules->offset, rules->value.charv);
385 break;
386 case MIME_MAGIC_SHORT :
387 printf("short(%d,%d)", rules->offset, rules->value.shortv);
388 break;
389 case MIME_MAGIC_INT :
390 printf("int(%d,%d)", rules->offset, rules->value.intv);
391 break;
392 case MIME_MAGIC_CONTAINS :
393 printf("contains(%d,%d,", rules->offset, rules->region);
394 for (i = 0; i < rules->length; i ++)
395 if (rules->value.stringv[i] < ' ' ||
396 rules->value.stringv[i] > 126)
397 printf("<%02X>", rules->value.stringv[i]);
398 else
399 putchar(rules->value.stringv[i]);
400 putchar(')');
401 break;
402 default :
403 break;
404 }
405
406 if (rules->child != NULL)
407 {
408 if (rules->op == MIME_MAGIC_OR)
409 puts("OR (");
410 else
411 puts("AND (");
412
413 strcat(indent, "\t");
414 print_rules(rules->child);
415 indent[strlen(indent) - 1] = '\0';
416 printf("%s)\n", indent);
417 }
418 else
419 putchar('\n');
420
421 rules = rules->next;
422 }
423 }
424
425
426 /*
427 * 'type_dir()' - Show the MIME types for a given directory.
428 */
429
430 static void
type_dir(mime_t * mime,const char * dirname)431 type_dir(mime_t *mime, /* I - MIME database */
432 const char *dirname) /* I - Directory */
433 {
434 cups_dir_t *dir; /* Directory */
435 cups_dentry_t *dent; /* Directory entry */
436 char filename[1024]; /* File to type */
437 mime_type_t *filetype; /* File type */
438 int compression; /* Compressed file? */
439 mime_type_t *pstype; /* application/vnd.cups-postscript */
440 cups_array_t *filters; /* Filters to pstype */
441 mime_filter_t *filter; /* Current filter */
442 int cost; /* Filter cost */
443
444
445 dir = cupsDirOpen(dirname);
446 if (!dir)
447 return;
448
449 pstype = mimeType(mime, "application", "vnd.cups-postscript");
450
451 while ((dent = cupsDirRead(dir)) != NULL)
452 {
453 if (dent->filename[0] == '.')
454 continue;
455
456 snprintf(filename, sizeof(filename), "%s/%s", dirname, dent->filename);
457
458 if (S_ISDIR(dent->fileinfo.st_mode))
459 type_dir(mime, filename);
460
461 if (!S_ISREG(dent->fileinfo.st_mode))
462 continue;
463
464 filetype = mimeFileType(mime, filename, NULL, &compression);
465
466 if (filetype)
467 {
468 printf("%s: %s/%s%s\n", filename, filetype->super, filetype->type,
469 compression ? " (compressed)" : "");
470
471 filters = mimeFilter(mime, filetype, pstype, &cost);
472
473 if (!filters)
474 puts(" No filters to convert application/vnd.cups-postscript.");
475 else
476 {
477 printf(" Filter cost = %d\n", cost);
478
479 filter = (mime_filter_t *)cupsArrayFirst(filters);
480 printf(" %s", filter->filter);
481
482 for (filter = (mime_filter_t *)cupsArrayNext(filters);
483 filter;
484 filter = (mime_filter_t *)cupsArrayNext(filters))
485 printf(" | %s", filter->filter);
486
487 putchar('\n');
488
489 cupsArrayDelete(filters);
490 }
491 }
492 else
493 printf("%s: unknown%s\n", filename, compression ? " (compressed)" : "");
494 }
495
496 cupsDirClose(dir);
497 }
498