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