/*
 * MIME test program for CUPS.
 *
 * Copyright 2007-2014 by Apple Inc.
 * Copyright 1997-2006 by Easy Software Products, all rights reserved.
 *
 * Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
 */

/*
 * Include necessary headers...
 */

#include <cups/string-private.h>
#include <cups/dir.h>
#include <cups/debug-private.h>
#include <cups/ppd-private.h>
#include "mime.h"


/*
 * Local functions...
 */

static void	add_ppd_filter(mime_t *mime, mime_type_t *filtertype,
		               const char *filter);
static void	add_ppd_filters(mime_t *mime, ppd_file_t *ppd);
static void	print_rules(mime_magic_t *rules);
static void	type_dir(mime_t *mime, const char *dirname);


/*
 * 'main()' - Main entry for the test program.
 */

int					/* O - Exit status */
main(int  argc,				/* I - Number of command-line args */
     char *argv[])			/* I - Command-line arguments */
{
  int		i;			/* Looping vars */
  const char	*filter_path;		/* Filter path */
  char		super[MIME_MAX_SUPER],	/* Super-type name */
		type[MIME_MAX_TYPE];	/* Type name */
  int		compression;		/* Compression of file */
  int		cost;			/* Cost of filters */
  mime_t	*mime;			/* MIME database */
  mime_type_t	*src,			/* Source type */
		*dst;			/* Destination type */
  struct stat	srcinfo;		/* Source information */
  ppd_file_t	*ppd;			/* PPD file */
  cups_array_t	*filters;		/* Filters for the file */
  mime_filter_t	*filter;		/* Current filter */


  mime        = NULL;
  src         = NULL;
  dst         = NULL;
  ppd         = NULL;
  filter_path = "../filter:" CUPS_SERVERBIN "/filter";

  srcinfo.st_size = 0;

  for (i = 1; i < argc; i ++)
    if (!strcmp(argv[i], "-d"))
    {
      i ++;

      if (i < argc)
      {
        mime = mimeLoad(argv[i], filter_path);

	if (ppd)
	  add_ppd_filters(mime, ppd);
      }
    }
    else if (!strcmp(argv[i], "-f"))
    {
      i ++;

      if (i < argc)
        filter_path = argv[i];
    }
    else if (!strcmp(argv[i], "-p"))
    {
      i ++;

      if (i < argc)
      {
        ppd = ppdOpenFile(argv[i]);

	if (mime)
	  add_ppd_filters(mime, ppd);
      }
    }
    else if (!src)
    {
      if (!mime)
	mime = mimeLoad("../conf", filter_path);

      if (ppd)
        add_ppd_filters(mime, ppd);

      src = mimeFileType(mime, argv[i], NULL, &compression);
      stat(argv[i], &srcinfo);

      if (src)
	printf("%s: %s/%s%s\n", argv[i], src->super, src->type,
	       compression ? " (gzipped)" : "");
      else if ((src = mimeType(mime, "application", "octet-stream")) != NULL)
	printf("%s: application/octet-stream\n", argv[i]);
      else
      {
	printf("%s: unknown\n", argv[i]);
	if (mime)
	  mimeDelete(mime);
	return (1);
      }
    }
    else
    {
      sscanf(argv[i], "%15[^/]/%255s", super, type);
      dst = mimeType(mime, super, type);

      filters = mimeFilter2(mime, src, (size_t)srcinfo.st_size, dst, &cost);

      if (!filters)
      {
	printf("No filters to convert from %s/%s to %s.\n", src->super,
	       src->type, argv[i]);
      }
      else
      {
        int first = 1;			/* First filter shown? */

        printf("Filter cost = %d\n", cost);

        for (filter = (mime_filter_t *)cupsArrayFirst(filters);
	     filter;
	     filter = (mime_filter_t *)cupsArrayNext(filters))
	{
	  if (!strcmp(filter->filter, "-"))
	    continue;

          if (first)
	  {
	    first = 0;
	    fputs(filter->filter, stdout);
	  }
	  else
	    printf(" | %s", filter->filter);
	}

        putchar('\n');

        cupsArrayDelete(filters);
      }
    }

  if (!mime)
  {
    mime = mimeLoad("../conf", filter_path);
    if (ppd)
      add_ppd_filters(mime, ppd);
  }

  if (!src)
  {
    puts("MIME database types:");
    for (src = mimeFirstType(mime); src; src = mimeNextType(mime))
    {
      printf("\t%s/%s (%d):\n", src->super, src->type, src->priority);
      print_rules(src->rules);
      puts("");
    }

    puts("");

    puts("MIME database filters:");
    for (filter = mimeFirstFilter(mime); filter; filter = mimeNextFilter(mime))
      printf("\t%s/%s to %s/%s: %s (%d)\n",
             filter->src->super, filter->src->type,
	     filter->dst->super, filter->dst->type,
	     filter->filter, filter->cost);

    type_dir(mime, "../doc");
  }

  return (0);
}


/*
 * 'add_printer_filter()' - Add a printer filter from a PPD.
 */

static void
add_ppd_filter(mime_t      *mime,	/* I - MIME database */
               mime_type_t *filtertype,	/* I - Filter or prefilter MIME type */
	       const char  *filter)	/* I - Filter to add */
{
  char		super[MIME_MAX_SUPER],	/* Super-type for filter */
		type[MIME_MAX_TYPE],	/* Type for filter */
		dsuper[MIME_MAX_SUPER],	/* Destination super-type for filter */
		dtype[MIME_MAX_TYPE],	/* Destination type for filter */
		dest[MIME_MAX_SUPER + MIME_MAX_TYPE + 2],
					/* Destination super/type */
		program[1024];		/* Program/filter name */
  int		cost;			/* Cost of filter */
  size_t	maxsize = 0;		/* Maximum supported file size */
  mime_type_t	*temptype,		/* MIME type looping var */
		*desttype;		/* Destination MIME type */
  mime_filter_t	*filterptr;		/* MIME filter */


 /*
  * Parse the filter string; it should be in one of the following formats:
  *
  *     source/type cost program
  *     source/type cost maxsize(nnnn) program
  *     source/type dest/type cost program
  *     source/type dest/type cost maxsize(nnnn) program
  */

  if (sscanf(filter, "%15[^/]/%255s%*[ \t]%15[^/]/%255s%d%*[ \t]%1023[^\n]",
             super, type, dsuper, dtype, &cost, program) == 6)
  {
    snprintf(dest, sizeof(dest), "test/%s/%s", dsuper, dtype);

    if ((desttype = mimeType(mime, "printer", dest)) == NULL)
      desttype = mimeAddType(mime, "printer", dest);
  }
  else
  {
    if (sscanf(filter, "%15[^/]/%255s%d%*[ \t]%1023[^\n]", super, type, &cost,
               program) == 4)
    {
      desttype = filtertype;
    }
    else
    {
      printf("testmime: Invalid filter string \"%s\".\n", filter);
      return;
    }
  }

  if (!strncmp(program, "maxsize(", 8))
  {
    char	*ptr;			/* Pointer into maxsize(nnnn) program */

    maxsize = (size_t)strtoll(program + 8, &ptr, 10);

    if (*ptr != ')')
    {
      printf("testmime: Invalid filter string \"%s\".\n", filter);
      return;
    }

    ptr ++;
    while (_cups_isspace(*ptr))
      ptr ++;

    _cups_strcpy(program, ptr);
  }

 /*
  * Add the filter to the MIME database, supporting wildcards as needed...
  */

  for (temptype = mimeFirstType(mime);
       temptype;
       temptype = mimeNextType(mime))
    if (((super[0] == '*' && _cups_strcasecmp(temptype->super, "printer")) ||
         !_cups_strcasecmp(temptype->super, super)) &&
        (type[0] == '*' || !_cups_strcasecmp(temptype->type, type)))
    {
      if (desttype != filtertype)
      {
        filterptr = mimeAddFilter(mime, temptype, desttype, cost, program);

        if (!mimeFilterLookup(mime, desttype, filtertype))
          mimeAddFilter(mime, desttype, filtertype, 0, "-");
      }
      else
        filterptr = mimeAddFilter(mime, temptype, filtertype, cost, program);

      if (filterptr)
	filterptr->maxsize = maxsize;
    }
}


/*
 * 'add_ppd_filters()' - Add all filters from a PPD.
 */

static void
add_ppd_filters(mime_t     *mime,	/* I - MIME database */
                ppd_file_t *ppd)	/* I - PPD file */
{
  _ppd_cache_t	*pc;			/* Cache data for PPD */
  const char	*value;			/* Filter definition value */
  mime_type_t	*filter,		/* Filter type */
		*prefilter;		/* Pre-filter type */


  pc = _ppdCacheCreateWithPPD(ppd);
  if (!pc)
    return;

  filter = mimeAddType(mime, "printer", "test");

  if (pc->filters)
  {
    for (value = (const char *)cupsArrayFirst(pc->filters);
         value;
         value = (const char *)cupsArrayNext(pc->filters))
      add_ppd_filter(mime, filter, value);
  }
  else
  {
    add_ppd_filter(mime, filter, "application/vnd.cups-raw 0 -");
    add_ppd_filter(mime, filter, "application/vnd.cups-postscript 0 -");
  }

  if (pc->prefilters)
  {
    prefilter = mimeAddType(mime, "prefilter", "test");

    for (value = (const char *)cupsArrayFirst(pc->prefilters);
         value;
         value = (const char *)cupsArrayNext(pc->prefilters))
      add_ppd_filter(mime, prefilter, value);
  }
}


/*
 * 'print_rules()' - Print the rules for a file type...
 */

static void
print_rules(mime_magic_t *rules)	/* I - Rules to print */
{
  int	i;				/* Looping var */
  static char	indent[255] = "\t";	/* Indentation for rules */


  if (rules == NULL)
    return;

  while (rules != NULL)
  {
    printf("%s[%p] ", indent, rules);

    if (rules->invert)
      printf("NOT ");

    switch (rules->op)
    {
      case MIME_MAGIC_MATCH :
          printf("match(%s)", rules->value.matchv);
	  break;
      case MIME_MAGIC_LOCALE :
          printf("locale(%s)", rules->value.localev);
	  break;
      case MIME_MAGIC_ASCII :
          printf("ascii(%d,%d)", rules->offset, rules->length);
	  break;
      case MIME_MAGIC_PRINTABLE :
          printf("printable(%d,%d)", rules->offset, rules->length);
	  break;
      case MIME_MAGIC_STRING :
          printf("string(%d,", rules->offset);
	  for (i = 0; i < rules->length; i ++)
	    if (rules->value.stringv[i] < ' ' ||
	        rules->value.stringv[i] > 126)
	      printf("<%02X>", rules->value.stringv[i]);
	    else
	      putchar(rules->value.stringv[i]);
          putchar(')');
	  break;
      case MIME_MAGIC_CHAR :
          printf("char(%d,%d)", rules->offset, rules->value.charv);
	  break;
      case MIME_MAGIC_SHORT :
          printf("short(%d,%d)", rules->offset, rules->value.shortv);
	  break;
      case MIME_MAGIC_INT :
          printf("int(%d,%d)", rules->offset, rules->value.intv);
	  break;
      case MIME_MAGIC_CONTAINS :
          printf("contains(%d,%d,", rules->offset, rules->region);
	  for (i = 0; i < rules->length; i ++)
	    if (rules->value.stringv[i] < ' ' ||
	        rules->value.stringv[i] > 126)
	      printf("<%02X>", rules->value.stringv[i]);
	    else
	      putchar(rules->value.stringv[i]);
          putchar(')');
	  break;
      default :
	  break;
    }

    if (rules->child != NULL)
    {
      if (rules->op == MIME_MAGIC_OR)
	puts("OR (");
      else
	puts("AND (");

      strcat(indent, "\t");
      print_rules(rules->child);
      indent[strlen(indent) - 1] = '\0';
      printf("%s)\n", indent);
    }
    else
      putchar('\n');

    rules = rules->next;
  }
}


/*
 * 'type_dir()' - Show the MIME types for a given directory.
 */

static void
type_dir(mime_t     *mime,		/* I - MIME database */
         const char *dirname)		/* I - Directory */
{
  cups_dir_t	*dir;			/* Directory */
  cups_dentry_t	*dent;			/* Directory entry */
  char		filename[1024];		/* File to type */
  mime_type_t	*filetype;		/* File type */
  int		compression;		/* Compressed file? */
  mime_type_t	*pstype;		/* application/vnd.cups-postscript */
  cups_array_t	*filters;		/* Filters to pstype */
  mime_filter_t	*filter;		/* Current filter */
  int		cost;			/* Filter cost */


  dir = cupsDirOpen(dirname);
  if (!dir)
    return;

  pstype = mimeType(mime, "application", "vnd.cups-postscript");

  while ((dent = cupsDirRead(dir)) != NULL)
  {
    if (dent->filename[0] == '.')
      continue;

    snprintf(filename, sizeof(filename), "%s/%s", dirname, dent->filename);

    if (S_ISDIR(dent->fileinfo.st_mode))
      type_dir(mime, filename);

    if (!S_ISREG(dent->fileinfo.st_mode))
      continue;

    filetype = mimeFileType(mime, filename, NULL, &compression);

    if (filetype)
    {
      printf("%s: %s/%s%s\n", filename, filetype->super, filetype->type,
             compression ? " (compressed)" : "");

      filters = mimeFilter(mime, filetype, pstype, &cost);

      if (!filters)
	puts("    No filters to convert application/vnd.cups-postscript.");
      else
      {
        printf("    Filter cost = %d\n", cost);

        filter = (mime_filter_t *)cupsArrayFirst(filters);
	printf("    %s", filter->filter);

	for (filter = (mime_filter_t *)cupsArrayNext(filters);
	     filter;
	     filter = (mime_filter_t *)cupsArrayNext(filters))
	  printf(" | %s", filter->filter);

        putchar('\n');

        cupsArrayDelete(filters);
      }
    }
    else
      printf("%s: unknown%s\n", filename, compression ? " (compressed)" : "");
  }

  cupsDirClose(dir);
}
