• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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