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