]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/printers.c
Merge changes from CUPS 1.5svn-r9641
[thirdparty/cups.git] / scheduler / printers.c
index 003210aa49e9c9cc0a07a5aadf619240f6abd70f..5331ca7bbc891894337de24e90e33730ea425fe0 100644 (file)
@@ -1,9 +1,9 @@
 /*
  * "$Id: printers.c 7968 2008-09-19 23:03:01Z mike $"
  *
- *   Printer routines for the Common UNIX Printing System (CUPS).
+ *   Printer routines for the CUPS scheduler.
  *
- *   Copyright 2007-2009 by Apple Inc.
+ *   Copyright 2007-2011 by Apple Inc.
  *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  *   These coded instructions, statements, and computer programs are the
  * Contents:
  *
  *   cupsdAddPrinter()          - Add a printer to the system.
- *   cupsdAddPrinterHistory()   - Add the current printer state to the history.
- *   cupsdAddPrinterUser()      - Add a user to the ACL.
  *   cupsdCreateCommonData()    - Create the common printer data.
  *   cupsdDeleteAllPrinters()   - Delete all printers from the system.
  *   cupsdDeletePrinter()       - Delete a printer from the system.
  *   cupsdFindDest()            - Find a destination in the list.
  *   cupsdFindPrinter()         - Find a printer in the list.
- *   cupsdFreePrinterUsers()    - Free allow/deny users.
  *   cupsdLoadAllPrinters()     - Load printers from the printers.conf file.
  *   cupsdRenamePrinter()       - Rename a printer.
  *   cupsdSaveAllPrinters()     - Save all printer definitions to the
  *   add_printer_filter()       - Add a MIME filter for a printer.
  *   add_printer_formats()      - Add document-format-supported values for a
  *                                printer.
- *   add_string_array()         - Add a string to an array of CUPS strings.
  *   compare_printers()         - Compare two printers.
  *   delete_printer_filters()   - Delete all MIME filters for a printer.
- *   delete_string_array()      - Delete an array of CUPS strings.
+ *   dirty_printer()            - Mark config and state files dirty for the
+ *                                specified printer.
  *   load_ppd()                 - Load a cached PPD file, updating the cache as
  *                                needed.
+ *   new_media_col()            - Create a media-col collection value.
  *   write_irix_config()        - Update the config files used by the IRIX
  *                                desktop tools.
  *   write_irix_state()         - Update the status files used by IRIX printing
 
 #include "cupsd.h"
 #include <cups/dir.h>
-#include <cups/pwgmedia.h>
+#ifdef HAVE_APPLICATIONSERVICES_H
+#  include <ApplicationServices/ApplicationServices.h>
+#endif /* HAVE_APPLICATIONSERVICES_H */
+#ifdef HAVE_SYS_MOUNT_H
+#  include <sys/mount.h>
+#endif /* HAVE_SYS_MOUNT_H */
+#ifdef HAVE_SYS_STATVFS_H
+#  include <sys/statvfs.h>
+#elif defined(HAVE_SYS_STATFS_H)
+#  include <sys/statfs.h>
+#endif /* HAVE_SYS_STATVFS_H */
+#ifdef HAVE_SYS_VFS_H
+#  include <sys/vfs.h>
+#endif /* HAVE_SYS_VFS_H */
 
 
 /*
@@ -75,11 +86,12 @@ static void add_printer_defaults(cupsd_printer_t *p);
 static void    add_printer_filter(cupsd_printer_t *p, mime_type_t *type,
                                   const char *filter);
 static void    add_printer_formats(cupsd_printer_t *p);
-static void    add_string_array(cups_array_t **a, const char *s);
 static int     compare_printers(void *first, void *second, void *data);
 static void    delete_printer_filters(cupsd_printer_t *p);
-static void    delete_string_array(cups_array_t **a);
+static void    dirty_printer(cupsd_printer_t *p);
 static void    load_ppd(cupsd_printer_t *p);
+static ipp_t   *new_media_col(_pwg_size_t *size, const char *source,
+                              const char *type);
 #ifdef __sgi
 static void    write_irix_config(cupsd_printer_t *p);
 static void    write_irix_state(cupsd_printer_t *p);
@@ -95,6 +107,8 @@ cupsd_printer_t *                    /* O - New printer */
 cupsdAddPrinter(const char *name)      /* I - Name of printer */
 {
   cupsd_printer_t      *p;             /* New printer */
+  char                 uri[1024],      /* Printer URI */
+                       uuid[64];       /* Printer UUID */
 
 
  /*
@@ -118,7 +132,10 @@ cupsdAddPrinter(const char *name)  /* I - Name of printer */
   cupsdSetString(&p->info, name);
   cupsdSetString(&p->hostname, ServerName);
 
-  cupsdSetStringf(&p->uri, "ipp://%s:%d/printers/%s", ServerName, LocalPort, name);
+  httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
+                  ServerName, RemotePort, "/printers/%s", name);
+  cupsdSetString(&p->uri, uri);
+  cupsdSetString(&p->uuid, cupsdMakeUUID(name, 0, uuid, sizeof(uuid)));
   cupsdSetDeviceURI(p, "file:///dev/null");
 
   p->state      = IPP_PRINTER_STOPPED;
@@ -135,9 +152,6 @@ cupsdAddPrinter(const char *name)   /* I - Name of printer */
 
   p->op_policy_ptr = DefaultPolicyPtr;
 
-  if (MaxPrinterHistory)
-    p->history = calloc(MaxPrinterHistory, sizeof(ipp_t *));
-
  /*
   * Insert the printer in the printer list alphabetically...
   */
@@ -160,103 +174,6 @@ cupsdAddPrinter(const char *name) /* I - Name of printer */
 }
 
 
-/*
- * 'cupsdAddPrinterHistory()' - Add the current printer state to the history.
- */
-
-void
-cupsdAddPrinterHistory(
-    cupsd_printer_t *p)                        /* I - Printer */
-{
-  ipp_t        *history;                       /* History collection */
-
-
- /*
-  * Stop early if we aren't keeping history data...
-  */
-
-  if (MaxPrinterHistory <= 0)
-    return;
-
- /*
-  * Retire old history data as needed...
-  */
-
-  p->sequence_number ++;
-
-  if (p->num_history >= MaxPrinterHistory)
-  {
-    p->num_history --;
-    ippDelete(p->history[0]);
-    memmove(p->history, p->history + 1, p->num_history * sizeof(ipp_t *));
-  }
-
- /*
-  * Create a collection containing the current printer-state, printer-up-time,
-  * printer-state-message, and printer-state-reasons attributes.
-  */
-
-  history = ippNew();
-  ippAddInteger(history, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
-                p->state);
-  ippAddBoolean(history, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
-                p->accepting);
-  ippAddBoolean(history, IPP_TAG_PRINTER, "printer-is-shared", p->shared);
-  ippAddString(history, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-state-message",
-               NULL, p->state_message);
-#ifdef __APPLE__
-  if (p->recoverable)
-    ippAddString(history, IPP_TAG_PRINTER, IPP_TAG_TEXT,
-                 "com.apple.print.recoverable-message", NULL, p->recoverable);
-#endif /* __APPLE__ */
-  if (p->num_reasons == 0)
-    ippAddString(history, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-                 "printer-state-reasons", NULL, "none");
-  else
-    ippAddStrings(history, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-                  "printer-state-reasons", p->num_reasons, NULL,
-                 (const char * const *)p->reasons);
-  ippAddInteger(history, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
-                "printer-state-change-time", p->state_time);
-  ippAddInteger(history, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
-                "printer-state-sequence-number", p->sequence_number);
-
-  p->history[p->num_history] = history;
-  p->num_history ++;
-}
-
-
-/*
- * 'cupsdAddPrinterUser()' - Add a user to the ACL.
- */
-
-void
-cupsdAddPrinterUser(
-    cupsd_printer_t *p,                        /* I - Printer */
-    const char      *username)         /* I - User */
-{
-  const char   **temp;                 /* Temporary array pointer */
-
-
-  if (!p || !username)
-    return;
-
-  if (p->num_users == 0)
-    temp = malloc(sizeof(char **));
-  else
-    temp = realloc(p->users, sizeof(char **) * (p->num_users + 1));
-
-  if (!temp)
-    return;
-
-  p->users = temp;
-  temp     += p->num_users;
-
-  if ((*temp = strdup(username)) != NULL)
-    p->num_users ++;
-}
-
-
 /*
  * 'cupsdCreateCommonData()' - Create the common printer data.
  */
@@ -272,6 +189,14 @@ cupsdCreateCommonData(void)
   char                 filename[1024], /* Filename */
                        *notifier;      /* Current notifier */
   cupsd_policy_t       *p;             /* Current policy */
+  int                  k_supported;    /* Maximum file size supported */
+#ifdef HAVE_STATVFS
+  struct statvfs       spoolinfo;      /* FS info for spool directory */
+  double               spoolsize;      /* FS size */
+#elif defined(HAVE_STATFS)
+  struct statfs                spoolinfo;      /* FS info for spool directory */
+  double               spoolsize;      /* FS size */
+#endif /* HAVE_STATVFS */
   static const int nups[] =            /* number-up-supported values */
                { 1, 2, 4, 6, 9, 16 };
   static const int orients[4] =/* orientation-requested-supported values */
@@ -311,6 +236,7 @@ cupsdCreateCommonData(void)
                  IPP_GET_PRINTER_ATTRIBUTES,
                  IPP_HOLD_JOB,
                  IPP_RELEASE_JOB,
+                 IPP_RESTART_JOB,
                  IPP_PAUSE_PRINTER,
                  IPP_RESUME_PRINTER,
                  IPP_PURGE_JOBS,
@@ -328,6 +254,9 @@ cupsdCreateCommonData(void)
                  IPP_DISABLE_PRINTER,
                  IPP_HOLD_NEW_JOBS,
                  IPP_RELEASE_HELD_NEW_JOBS,
+                 IPP_CANCEL_JOBS,
+                 IPP_CANCEL_MY_JOBS,
+                 IPP_CLOSE_JOB,
                  CUPS_GET_DEFAULT,
                  CUPS_GET_PRINTERS,
                  CUPS_ADD_PRINTER,
@@ -360,9 +289,12 @@ cupsdCreateCommonData(void)
                };
   static const char * const media_col_supported[] =
                {                       /* media-col-supported values */
-                 "media-color",
-                 "media-key",
+                 "media-bottom-margin",
+                 "media-left-margin",
+                 "media-right-margin",
                  "media-size",
+                 "media-source",
+                 "media-top-margin",
                  "media-type"
                };
   static const char * const multiple_document_handling[] =
@@ -370,13 +302,6 @@ cupsdCreateCommonData(void)
                  "separate-documents-uncollated-copies",
                  "separate-documents-collated-copies"
                };
-  static const char * const errors[] = /* printer-error-policy-supported values */
-               {
-                 "abort-job",
-                 "retry-current-job",
-                 "retry-job",
-                 "stop-printer"
-               };
   static const char * const notify_attrs[] =
                {                       /* notify-attributes-supported values */
                  "printer-state-change-time",
@@ -407,26 +332,75 @@ cupsdCreateCommonData(void)
                  "server-started",
                  "server-stopped"
                };
+  static const char * const job_creation[] =
+               {                       /* job-creation-attributes-supported */
+                 "copies",
+                 "finishings",
+                 "ipp-attribute-fidelity",
+                 "job-hold-until",
+                 "job-name",
+                 "job-priority",
+                 "job-sheets",
+                 "media",
+                 "media-col",
+                 "multiple-document-handling",
+                 "number-up",
+                 "output-bin",
+                 "output-mode",
+                 "orientation-requested",
+                 "page-ranges",
+                 "print-quality",
+                 "printer-resolution",
+                 "sides"
+               };
   static const char * const job_settable[] =
                {                       /* job-settable-attributes-supported */
                  "copies",
                  "finishings",
                  "job-hold-until",
+                 "job-name",
                  "job-priority",
                  "media",
+                 "media-col",
                  "multiple-document-handling",
                  "number-up",
+                 "output-bin",
+                 "output-mode",
                  "orientation-requested",
                  "page-ranges",
                  "print-quality",
                  "printer-resolution",
                  "sides"
                };
+  static const char * const pdf_versions[] =
+               {                       /* pdf-versions-supported */
+                 "adobe-1.2",
+                 "adobe-1.3",
+                 "adobe-1.4",
+                 "adobe-1.5",
+                 "adobe-1.6",
+                 "adobe-1.7",
+                 "iso-19005-1_2005",
+                 "iso-32000-1_2008",
+                 "pwg-5102.3"
+               };
   static const char * const printer_settable[] =
                {                       /* printer-settable-attributes-supported */
                  "printer-info",
                  "printer-location"
                };
+  static const char * const which_jobs[] =
+               {                       /* which-jobs-supported values */
+                 "completed",
+                 "not-completed",
+                 "aborted",
+                 "all",
+                 "canceled",
+                 "pending",
+                 "pending-held",
+                 "processing",
+                 "processing-stopped"
+               };
 
 
   if (CommonData)
@@ -434,6 +408,34 @@ cupsdCreateCommonData(void)
 
   CommonData = ippNew();
 
+ /*
+  * Get the maximum spool size based on the size of the filesystem used for
+  * the RequestRoot directory.  If the host OS doesn't support the statfs call
+  * or the filesystem is larger than 2TiB, always report INT_MAX.
+  */
+
+#ifdef HAVE_STATVFS
+  if (statvfs(RequestRoot, &spoolinfo))
+    k_supported = INT_MAX;
+  else if ((spoolsize = (double)spoolinfo.f_frsize * spoolinfo.f_blocks / 1024) >
+               INT_MAX)
+    k_supported = INT_MAX;
+  else
+    k_supported = (int)spoolsize;
+
+#elif defined(HAVE_STATFS)
+  if (statfs(RequestRoot, &spoolinfo))
+    k_supported = INT_MAX;
+  else if ((spoolsize = (double)spoolinfo.f_bsize * spoolinfo.f_blocks / 1024) >
+               INT_MAX)
+    k_supported = INT_MAX;
+  else
+    k_supported = (int)spoolsize;
+
+#else
+  k_supported = INT_MAX;
+#endif /* HAVE_STATVFS */
+
  /*
   * This list of attributes is sorted to improve performance when the
   * client provides a requested-attributes attribute...
@@ -441,7 +443,7 @@ cupsdCreateCommonData(void)
 
   /* charset-configured */
   ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_CHARSET | IPP_TAG_COPY,
-               "charset-configured", NULL, DefaultCharset);
+               "charset-configured", NULL, "utf-8");
 
   /* charset-supported */
   ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_CHARSET | IPP_TAG_COPY,
@@ -461,8 +463,8 @@ cupsdCreateCommonData(void)
   ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_TEXT | IPP_TAG_COPY,
                "cups-version", NULL, CUPS_SVERSION + 6);
 
-  /* generated-natural-language-supported */
-  ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE | IPP_TAG_COPY,
+  /* generated-natural-language-supported (no IPP_TAG_COPY) */
+  ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE,
                "generated-natural-language-supported", NULL, DefaultLanguage);
 
   /* ipp-versions-supported */
@@ -470,11 +472,28 @@ cupsdCreateCommonData(void)
                 "ipp-versions-supported", sizeof(versions) / sizeof(versions[0]),
                NULL, versions);
 
+  /* ippget-event-life */
+  ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                "ippget-event-life", 15);
+
+  /* job-creation-attributes-supported */
+  ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
+                "job-creation-attributes-supported",
+               sizeof(job_creation) / sizeof(job_creation[0]),
+               NULL, job_creation);
+
   /* job-hold-until-supported */
   ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
                 "job-hold-until-supported", sizeof(holds) / sizeof(holds[0]),
                NULL, holds);
 
+  /* job-ids-supported */
+  ippAddBoolean(CommonData, IPP_TAG_PRINTER, "job-ids-supported", 1);
+
+  /* job-k-octets-supported */
+  ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                "job-k-octets-supported", k_supported);
+
   /* job-priority-supported */
   ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
                 "job-priority-supported", 100);
@@ -523,6 +542,18 @@ cupsdCreateCommonData(void)
     ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
                  "job-sheets-supported", NULL, "none");
 
+  /* jpeg-k-octets-supported */
+  ippAddRange(CommonData, IPP_TAG_PRINTER, "jpeg-k-octets-supported", 0,
+              k_supported);
+
+  /* jpeg-x-dimension-supported */
+  ippAddRange(CommonData, IPP_TAG_PRINTER, "jpeg-x-dimension-supported", 0,
+              65535);
+
+  /* jpeg-y-dimension-supported */
+  ippAddRange(CommonData, IPP_TAG_PRINTER, "jpeg-y-dimension-supported", 1,
+              65535);
+
   /* media-col-supported */
   ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
                 "media-col-supported",
@@ -545,8 +576,8 @@ cupsdCreateCommonData(void)
   ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
                 "multiple-operation-time-out", MultipleOperationTimeout);
 
-  /* natural-language-configured */
-  ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE | IPP_TAG_COPY,
+  /* natural-language-configured (no IPP_TAG_COPY) */
+  ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE,
                "natural-language-configured", NULL, DefaultLanguage);
 
   /* notify-attributes-supported */
@@ -617,14 +648,19 @@ cupsdCreateCommonData(void)
   /* page-ranges-supported */
   ippAddBoolean(CommonData, IPP_TAG_PRINTER, "page-ranges-supported", 1);
 
-  /* pdf-override-supported */
-  ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
-               "pdl-override-supported", NULL, "not-attempted");
+  /* pdf-k-octets-supported */
+  ippAddRange(CommonData, IPP_TAG_PRINTER, "pdf-k-octets-supported", 0,
+              k_supported);
 
-  /* printer-error-policy-supported */
-  ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
-                "printer-error-policy-supported",
-               sizeof(errors) / sizeof(errors[0]), NULL, errors);
+  /* pdf-versions-supported */
+  ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
+                "pdf-versions-supported",
+                sizeof(pdf_versions) / sizeof(pdf_versions[0]), NULL,
+                pdf_versions);
+
+  /* pdl-override-supported */
+  ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
+               "pdl-override-supported", NULL, "attempted");
 
   /* printer-op-policy-supported */
   attr = ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
@@ -644,6 +680,11 @@ cupsdCreateCommonData(void)
   /* server-is-sharing-printers */
   ippAddBoolean(CommonData, IPP_TAG_PRINTER, "server-is-sharing-printers",
                 BrowseLocalProtocols != 0 && Browsing);
+
+  /* which-jobs-supported */
+  ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
+                "which-jobs-supported",
+                sizeof(which_jobs) / sizeof(which_jobs[0]), NULL, which_jobs);
 }
 
 
@@ -660,7 +701,10 @@ cupsdDeleteAllPrinters(void)
   for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
        p;
        p = (cupsd_printer_t *)cupsArrayNext(Printers))
+  {
+    p->op_policy_ptr = DefaultPolicyPtr;
     cupsdDeletePrinter(p, 0);
+  }
 }
 
 
@@ -668,12 +712,13 @@ cupsdDeleteAllPrinters(void)
  * 'cupsdDeletePrinter()' - Delete a printer from the system.
  */
 
-void
+int                                    /* O - 1 if classes affected, 0 otherwise */
 cupsdDeletePrinter(
     cupsd_printer_t *p,                        /* I - Printer to delete */
     int             update)            /* I - Update printers.conf? */
 {
-  int  i;                              /* Looping var */
+  int  i,                              /* Looping var */
+       changed = 0;                    /* Class changed? */
 #ifdef __sgi
   char filename[1024];                 /* Interface script filename */
 #endif /* __sgi */
@@ -692,7 +737,14 @@ cupsdDeletePrinter(
   * Stop printing on this printer...
   */
 
-  cupsdStopPrinter(p, update);
+  cupsdSetPrinterState(p, IPP_PRINTER_STOPPED, update);
+
+  p->state = IPP_PRINTER_STOPPED;      /* Force for browsed printers */
+
+  if (p->job)
+    cupsdSetJobState(p->job, IPP_JOB_PENDING, CUPSD_JOB_FORCE,
+                     update ? "Job stopped due to printer being deleted." :
+                             "Job stopped.");
 
  /*
   * If this printer is the next for browsing, point to the next one...
@@ -779,7 +831,7 @@ cupsdDeletePrinter(
 
   if (!(p->type & CUPS_PRINTER_IMPLICIT))
   {
-    cupsdDeletePrinterFromClasses(p);
+    changed = cupsdDeletePrinterFromClasses(p);
 
    /*
     * Deregister from any browse protocols...
@@ -795,13 +847,7 @@ cupsdDeletePrinter(
   if (p->printers != NULL)
     free(p->printers);
 
-  if (MaxPrinterHistory)
-  {
-    for (i = 0; i < p->num_history; i ++)
-      ippDelete(p->history[i]);
-
-    free(p->history);
-  }
+  delete_printer_filters(p);
 
   for (i = 0; i < p->num_reasons; i ++)
     _cupsStrFree(p->reasons[i]);
@@ -809,15 +855,10 @@ cupsdDeletePrinter(
   ippDelete(p->attrs);
   ippDelete(p->ppd_attrs);
 
-  delete_printer_filters(p);
-
   mimeDeleteType(MimeDatabase, p->filetype);
   mimeDeleteType(MimeDatabase, p->prefiltertype);
 
-  delete_string_array(&(p->filters));
-  delete_string_array(&(p->pre_filters));
-
-  cupsdFreePrinterUsers(p);
+  cupsdFreeStrings(&(p->users));
   cupsdFreeQuotas(p);
 
   cupsdClearString(&p->uri);
@@ -838,7 +879,6 @@ cupsdDeletePrinter(
   cupsdClearString(&p->alert_description);
 
 #ifdef HAVE_DNSSD
-  cupsdClearString(&p->product);
   cupsdClearString(&p->pdl);
 #endif /* HAVE_DNSSD */
 
@@ -847,10 +887,6 @@ cupsdDeletePrinter(
   if (p->browse_attrs)
     free(p->browse_attrs);
 
-#ifdef __APPLE__
-  cupsdClearString(&p->recoverable);
-#endif /* __APPLE__ */
-
   cupsFreeOptions(p->num_options, p->options);
 
   free(p);
@@ -860,6 +896,8 @@ cupsdDeletePrinter(
   */
 
   cupsArrayRestore(Printers);
+
+  return (changed);
 }
 
 
@@ -895,30 +933,6 @@ cupsdFindPrinter(const char *name) /* I - Name of printer to find */
 }
 
 
-/*
- * 'cupsdFreePrinterUsers()' - Free allow/deny users.
- */
-
-void
-cupsdFreePrinterUsers(
-    cupsd_printer_t *p)                        /* I - Printer */
-{
-  int  i;                              /* Looping var */
-
-
-  if (!p || !p->num_users)
-    return;
-
-  for (i = 0; i < p->num_users; i ++)
-    free((void *)p->users[i]);
-
-  free(p->users);
-
-  p->num_users = 0;
-  p->users     = NULL;
-}
-
-
 /*
  * 'cupsdLoadAllPrinters()' - Load printers from the printers.conf file.
  */
@@ -1000,7 +1014,6 @@ cupsdLoadAllPrinters(void)
        */
 
         cupsdSetPrinterAttrs(p);
-       cupsdAddPrinterHistory(p);
 
         if (strncmp(p->device_uri, "file:", 5) &&
            p->state != IPP_PRINTER_STOPPED)
@@ -1038,6 +1051,14 @@ cupsdLoadAllPrinters(void)
       cupsdLogMessage(CUPSD_LOG_ERROR,
                       "Syntax error on line %d of printers.conf.", linenum);
     }
+    else if (!strcasecmp(line, "UUID"))
+    {
+      if (value && !strncmp(value, "urn:uuid:", 9))
+        cupsdSetString(&(p->uuid), value);
+      else
+        cupsdLogMessage(CUPSD_LOG_ERROR,
+                       "Bad UUID on line %d of printers.conf.", linenum);
+    }
     else if (!strcasecmp(line, "AuthInfoRequired"))
     {
       if (!cupsdSetAuthInfoRequired(p, value, NULL))
@@ -1099,7 +1120,10 @@ cupsdLoadAllPrinters(void)
     }
     else if (!strcasecmp(line, "Reason"))
     {
-      if (value)
+      if (value &&
+          strcmp(value, "connecting-to-device") &&
+          strcmp(value, "cups-insecure-filter-warning") &&
+          strcmp(value, "cups-missing-filter-warning"))
       {
         for (i = 0 ; i < p->num_reasons; i ++)
          if (!strcmp(value, p->reasons[i]))
@@ -1127,7 +1151,17 @@ cupsdLoadAllPrinters(void)
       else if (value && !strcasecmp(value, "stopped"))
       {
         p->state = IPP_PRINTER_STOPPED;
-       cupsdSetPrinterReasons(p, "+paused");
+
+        for (i = 0 ; i < p->num_reasons; i ++)
+         if (!strcmp("paused", p->reasons[i]))
+           break;
+
+        if (i >= p->num_reasons &&
+           p->num_reasons < (int)(sizeof(p->reasons) / sizeof(p->reasons[0])))
+       {
+         p->reasons[p->num_reasons] = _cupsStrAlloc("paused");
+         p->num_reasons ++;
+       }
       }
       else
        cupsdLogMessage(CUPSD_LOG_ERROR,
@@ -1179,44 +1213,6 @@ cupsdLoadAllPrinters(void)
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
     }
-    else if (!strcasecmp(line, "Product"))
-    {
-      if (value)
-      {
-#ifdef HAVE_DNSSD
-        p->product = _cupsStrAlloc(value);
-#endif /* HAVE_DNSSD */
-      }
-      else
-       cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "Syntax error on line %d of printers.conf.", linenum);
-    }
-    else if (!strcasecmp(line, "Filter"))
-    {
-      if (value)
-      {
-        if (!p->filters)
-         p->filters = cupsArrayNew(NULL, NULL);
-
-       cupsArrayAdd(p->filters, _cupsStrAlloc(value));
-      }
-      else
-       cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "Syntax error on line %d of printers.conf.", linenum);
-    }
-    else if (!strcasecmp(line, "PreFilter"))
-    {
-      if (value)
-      {
-        if (!p->pre_filters)
-         p->pre_filters = cupsArrayNew(NULL, NULL);
-
-       cupsArrayAdd(p->pre_filters, _cupsStrAlloc(value));
-      }
-      else
-       cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "Syntax error on line %d of printers.conf.", linenum);
-    }
     else if (!strcasecmp(line, "Shared"))
     {
      /*
@@ -1274,7 +1270,7 @@ cupsdLoadAllPrinters(void)
       if (value)
       {
         p->deny_users = 0;
-        cupsdAddPrinterUser(p, value);
+        cupsdAddString(&(p->users), value);
       }
       else
        cupsdLogMessage(CUPSD_LOG_ERROR,
@@ -1285,7 +1281,7 @@ cupsdLoadAllPrinters(void)
       if (value)
       {
         p->deny_users = 1;
-        cupsdAddPrinterUser(p, value);
+        cupsdAddString(&(p->users), value);
       }
       else
        cupsdLogMessage(CUPSD_LOG_ERROR,
@@ -1458,7 +1454,8 @@ cupsdSaveAllPrinters(void)
   char                 temp[1024],     /* Temporary string */
                        backup[1024],   /* printers.conf.O file */
                        value[2048],    /* Value string */
-                       *ptr;           /* Pointer into value */
+                       *ptr,           /* Pointer into value */
+                       *name;          /* Current user/group name */
   cupsd_printer_t      *printer;       /* Current printer class */
   time_t               curtime;        /* Current time */
   struct tm            *curdate;       /* Current date */
@@ -1498,7 +1495,7 @@ cupsdSaveAllPrinters(void)
   */
 
   fchown(cupsFileNumber(fp), getuid(), Group);
-  fchmod(cupsFileNumber(fp), 0600);
+  fchmod(cupsFileNumber(fp), ConfigFilePerm & 0600);
 
  /*
   * Write a small header to the file...
@@ -1510,6 +1507,7 @@ cupsdSaveAllPrinters(void)
 
   cupsFilePuts(fp, "# Printer configuration file for " CUPS_SVERSION "\n");
   cupsFilePrintf(fp, "# Written by cupsd on %s\n", temp);
+  cupsFilePuts(fp, "# DO NOT EDIT THIS FILE WHEN CUPSD IS RUNNING\n");
 
  /*
   * Write each local printer known to the system...
@@ -1537,6 +1535,8 @@ cupsdSaveAllPrinters(void)
     else
       cupsFilePrintf(fp, "<Printer %s>\n", printer->name);
 
+    cupsFilePrintf(fp, "UUID %s\n", printer->uuid);
+
     if (printer->num_auth_info_required > 0)
     {
       switch (printer->num_auth_info_required)
@@ -1591,26 +1591,12 @@ cupsdSaveAllPrinters(void)
 
     for (i = 0; i < printer->num_reasons; i ++)
       if (strcmp(printer->reasons[i], "connecting-to-device") &&
-          strcmp(printer->reasons[i], "cups-missing-filter-error"))
+          strcmp(printer->reasons[i], "cups-insecure-filter-warning") &&
+          strcmp(printer->reasons[i], "cups-missing-filter-warning"))
         cupsFilePutConf(fp, "Reason", printer->reasons[i]);
 
     cupsFilePrintf(fp, "Type %d\n", printer->type);
 
-#ifdef HAVE_DNSSD
-    if (printer->product)
-      cupsFilePutConf(fp, "Product", printer->product);
-#endif /* HAVE_DNSSD */
-
-    for (ptr = (char *)cupsArrayFirst(printer->filters);
-         ptr;
-        ptr = (char *)cupsArrayNext(printer->filters))
-      cupsFilePutConf(fp, "Filter", ptr);
-
-    for (ptr = (char *)cupsArrayFirst(printer->pre_filters);
-         ptr;
-        ptr = (char *)cupsArrayNext(printer->pre_filters))
-      cupsFilePutConf(fp, "PreFilter", ptr);
-
     if (printer->accepting)
       cupsFilePuts(fp, "Accepting Yes\n");
     else
@@ -1629,9 +1615,10 @@ cupsdSaveAllPrinters(void)
     cupsFilePrintf(fp, "PageLimit %d\n", printer->page_limit);
     cupsFilePrintf(fp, "KLimit %d\n", printer->k_limit);
 
-    for (i = 0; i < printer->num_users; i ++)
-      cupsFilePutConf(fp, printer->deny_users ? "DenyUser" : "AllowUser",
-                      printer->users[i]);
+    for (name = (char *)cupsArrayFirst(printer->users);
+         name;
+        name = (char *)cupsArrayNext(printer->users))
+      cupsFilePutConf(fp, printer->deny_users ? "DenyUser" : "AllowUser", name);
 
     if (printer->op_policy)
       cupsFilePutConf(fp, "OpPolicy", printer->op_policy);
@@ -2009,7 +1996,7 @@ cupsdSetPrinterAttr(
   * Don't allow empty values...
   */
 
-  if (!*value)
+  if (!*value && strcmp(name, "marker-message"))
   {
     cupsdLogMessage(CUPSD_LOG_ERROR, "Ignoring empty \"%s\" attribute", name);
     return;
@@ -2136,22 +2123,17 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
   ipp_t                *oldattrs;              /* Old printer attributes */
   ipp_attribute_t *attr;               /* Attribute data */
   cups_option_t        *option;                /* Current printer option */
-  char         *filter;                /* Current filter */
+  char         *name,                  /* Current user/group name */
+               *filter;                /* Current filter */
+  static const char * const air_none[] =
+               {                       /* No authentication */
+                 "none"
+               };
   static const char * const air_userpass[] =
                {                       /* Basic/Digest authentication */
                  "username",
                  "password"
                };
-#ifdef HAVE_GSSAPI
-  static const char * const air_negotiate[] =
-               {                       /* Kerberos authentication */
-                 "negotiate"
-               };
-#endif /* HAVE_GSSAPI */
-  static const char * const air_none[] =
-               {                       /* No authentication */
-                 "none"
-               };
 
 
   DEBUG_printf(("cupsdSetPrinterAttrs: entering name = %s, type = %x\n", p->name,
@@ -2182,65 +2164,50 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
   {
     num_air = p->num_auth_info_required;
     air     = p->auth_info_required;
-
-    if (!strcmp(air[0], "username"))
-      auth_supported = "basic";
-    else
-      auth_supported = "negotiate";
   }
-  else if (!(p->type & CUPS_PRINTER_DISCOVERED))
+  else if ((p->type & CUPS_PRINTER_AUTHENTICATED) &&
+           (p->type & CUPS_PRINTER_DISCOVERED))
   {
-    if (p->type & CUPS_PRINTER_CLASS)
-      snprintf(resource, sizeof(resource), "/classes/%s", p->name);
-    else
-      snprintf(resource, sizeof(resource), "/printers/%s", p->name);
+    num_air = 2;
+    air     = air_userpass;
+  }
 
-    if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL ||
-        auth->type == CUPSD_AUTH_NONE)
-      auth = cupsdFindPolicyOp(p->op_policy_ptr, IPP_PRINT_JOB);
+  if (p->type & CUPS_PRINTER_CLASS)
+    snprintf(resource, sizeof(resource), "/classes/%s", p->name);
+  else
+    snprintf(resource, sizeof(resource), "/printers/%s", p->name);
 
-    if (auth)
-    {
-      int      auth_type;              /* Authentication type */
+  if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL ||
+      auth->type == CUPSD_AUTH_NONE)
+    auth = cupsdFindPolicyOp(p->op_policy_ptr, IPP_PRINT_JOB);
 
+  if (auth)
+  {
+    int        auth_type;              /* Authentication type */
 
-      if ((auth_type = auth->type) == CUPSD_AUTH_DEFAULT)
-        auth_type = DefaultAuthType;
 
-      if (auth_type == CUPSD_AUTH_BASIC || auth_type == CUPSD_AUTH_BASICDIGEST)
-      {
-       auth_supported = "basic";
-       num_air        = 2;
-       air            = air_userpass;
-      }
-      else if (auth_type == CUPSD_AUTH_DIGEST)
-      {
-       auth_supported = "digest";
-       num_air        = 2;
-       air            = air_userpass;
-      }
+    if ((auth_type = auth->type) == CUPSD_AUTH_DEFAULT)
+      auth_type = DefaultAuthType;
+
+    if (auth_type == CUPSD_AUTH_BASIC || auth_type == CUPSD_AUTH_BASICDIGEST)
+      auth_supported = "basic";
+    else if (auth_type == CUPSD_AUTH_DIGEST)
+      auth_supported = "digest";
 #ifdef HAVE_GSSAPI
-      else if (auth_type == CUPSD_AUTH_NEGOTIATE)
-      {
-       auth_supported = "negotiate";
-       num_air        = 1;
-       air            = air_negotiate;
-      }
+    else if (auth_type == CUPSD_AUTH_NEGOTIATE)
+      auth_supported = "negotiate";
 #endif /* HAVE_GSSAPI */
 
+    if (!(p->type & CUPS_PRINTER_DISCOVERED))
+    {
       if (auth_type != CUPSD_AUTH_NONE)
-        p->type |= CUPS_PRINTER_AUTHENTICATED;
+       p->type |= CUPS_PRINTER_AUTHENTICATED;
       else
-        p->type &= ~CUPS_PRINTER_AUTHENTICATED;
+       p->type &= ~CUPS_PRINTER_AUTHENTICATED;
     }
-    else
-      p->type &= ~CUPS_PRINTER_AUTHENTICATED;
-  }
-  else if (p->type & CUPS_PRINTER_AUTHENTICATED)
-  {
-    num_air = 2;
-    air     = air_userpass;
   }
+  else if (!(p->type & CUPS_PRINTER_DISCOVERED))
+    p->type &= ~CUPS_PRINTER_AUTHENTICATED;
 
  /*
   * Create the required IPP attributes for a printer...
@@ -2259,17 +2226,24 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
                NULL, p->location ? p->location : "");
   ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info",
                NULL, p->info ? p->info : "");
+  ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uuid", NULL,
+              p->uuid);
 
-  if (p->num_users)
+  if (cupsArrayCount(p->users) > 0)
   {
     if (p->deny_users)
-      ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
-                    "requesting-user-name-denied", p->num_users, NULL,
-                   p->users);
+      attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
+                           "requesting-user-name-denied",
+                          cupsArrayCount(p->users), NULL, NULL);
     else
-      ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
-                    "requesting-user-name-allowed", p->num_users, NULL,
-                   p->users);
+      attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
+                           "requesting-user-name-allowed",
+                          cupsArrayCount(p->users), NULL, NULL);
+
+    for (i = 0, name = (char *)cupsArrayFirst(p->users);
+         name;
+        i ++, name = (char *)cupsArrayNext(p->users))
+      attr->values[i].string.text = _cupsStrRetain(name);
   }
 
   ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
@@ -2308,8 +2282,24 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
     * Tell the client this is a remote printer of some type...
     */
 
-    ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
-                "printer-uri-supported", NULL, p->uri);
+    if (strchr(p->uri, '?'))
+    {
+     /*
+      * Strip trailing "?options" from URI...
+      */
+
+      char *ptr;                       /* Pointer into URI */
+
+      strlcpy(resource, p->uri, sizeof(resource));
+      if ((ptr = strchr(resource, '?')) != NULL)
+        *ptr = '\0';
+
+      ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
+                  "printer-uri-supported", NULL, resource);
+    }
+    else
+      ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
+                  "printer-uri-supported", NULL, p->uri);
 
     ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-more-info",
                 NULL, p->uri);
@@ -2389,20 +2379,58 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
       * Add filters for printer...
       */
 
-      cupsdSetPrinterReasons(p, "-cups-missing-filter-error");
+      cupsdSetPrinterReasons(p, "-cups-missing-filter-warning,"
+                                "cups-insecure-filter-warning");
 
-      for (filter = (char *)cupsArrayFirst(p->filters);
-          filter;
-          filter = (char *)cupsArrayNext(p->filters))
-       add_printer_filter(p, p->filetype, filter);
+      if (p->pc && p->pc->filters)
+      {
+       for (filter = (char *)cupsArrayFirst(p->pc->filters);
+            filter;
+            filter = (char *)cupsArrayNext(p->pc->filters))
+         add_printer_filter(p, p->filetype, filter);
+      }
+      else if (!(p->type & CUPS_PRINTER_REMOTE))
+      {
+       char    interface[1024];        /* Interface script */
+
+
+       snprintf(interface, sizeof(interface), "%s/interfaces/%s", ServerRoot,
+                p->name);
+       if (!access(interface, X_OK))
+       {
+        /*
+         * Yes, we have a System V style interface script; use it!
+         */
+
+         snprintf(interface, sizeof(interface), "*/* 0 %s/interfaces/%s",
+                  ServerRoot, p->name);
+         add_printer_filter(p, p->filetype, interface);
+       }
+       else
+       {
+        /*
+         * Add a filter from application/vnd.cups-raw to printer/name to
+         * handle "raw" printing by users.
+         */
+
+         add_printer_filter(p, p->filetype, "application/vnd.cups-raw 0 -");
+
+        /*
+         * Add a PostScript filter, since this is still possibly PS printer.
+         */
 
-      if (p->pre_filters)
+         add_printer_filter(p, p->filetype,
+                            "application/vnd.cups-postscript 0 -");
+       }
+      }
+
+      if (p->pc && p->pc->prefilters)
       {
         p->prefiltertype = mimeAddType(MimeDatabase, "prefilter", p->name);
 
-        for (filter = (char *)cupsArrayFirst(p->pre_filters);
+        for (filter = (char *)cupsArrayFirst(p->pc->prefilters);
             filter;
-            filter = (char *)cupsArrayNext(p->pre_filters))
+            filter = (char *)cupsArrayNext(p->pc->prefilters))
          add_printer_filter(p, p->prefiltertype, filter);
       }
     }
@@ -2442,20 +2470,49 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
       }
     }
 
-    if ((oldattr = ippFindAttribute(oldattrs, "marker-names",
-                                    IPP_TAG_NAME)) != NULL)
+    if ((oldattr = ippFindAttribute(oldattrs, "marker-message",
+                                    IPP_TAG_TEXT)) != NULL)
+      ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "marker-message",
+                   NULL, oldattr->values[0].string.text);
+
+    if ((oldattr = ippFindAttribute(oldattrs, "marker-low-levels",
+                                    IPP_TAG_INTEGER)) != NULL)
     {
-      if ((attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
-                                "marker-names", oldattr->num_values, NULL,
-                               NULL)) != NULL)
+      if ((attr = ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                                 "marker-low-levels", oldattr->num_values,
+                                NULL)) != NULL)
       {
        for (i = 0; i < oldattr->num_values; i ++)
-         attr->values[i].string.text =
-             _cupsStrRetain(oldattr->values[i].string.text);
+         attr->values[i].integer = oldattr->values[i].integer;
       }
     }
 
-    if ((oldattr = ippFindAttribute(oldattrs, "marker-types",
+    if ((oldattr = ippFindAttribute(oldattrs, "marker-high-levels",
+                                    IPP_TAG_INTEGER)) != NULL)
+    {
+      if ((attr = ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                                 "marker-high-levels", oldattr->num_values,
+                                NULL)) != NULL)
+      {
+       for (i = 0; i < oldattr->num_values; i ++)
+         attr->values[i].integer = oldattr->values[i].integer;
+      }
+    }
+
+    if ((oldattr = ippFindAttribute(oldattrs, "marker-names",
+                                    IPP_TAG_NAME)) != NULL)
+    {
+      if ((attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
+                                "marker-names", oldattr->num_values, NULL,
+                               NULL)) != NULL)
+      {
+       for (i = 0; i < oldattr->num_values; i ++)
+         attr->values[i].string.text =
+             _cupsStrRetain(oldattr->values[i].string.text);
+      }
+    }
+
+    if ((oldattr = ippFindAttribute(oldattrs, "marker-types",
                                     IPP_TAG_KEYWORD)) != NULL)
     {
       if ((attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
@@ -2520,14 +2577,6 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
     if (BrowseLocalOptions)
       length += 12 + strlen(BrowseLocalOptions);
 
-    if (p->num_auth_info_required > 0)
-    {
-      length += 18;                    /* auth-info-required */
-
-      for (i = 0; i < p->num_auth_info_required; i ++)
-        length += strlen(p->auth_info_required[i]) + 1;
-    }
-
    /*
     * Allocate the new string...
     */
@@ -2573,21 +2622,6 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
          }
        }
       }
-
-      if (p->num_auth_info_required > 0)
-      {
-        strcpy(attrptr, "auth-info-required");
-       attrptr += 18;
-
-       for (i = 0; i < p->num_auth_info_required; i ++)
-       {
-         *attrptr++ = i ? ',' : '=';
-         strcpy(attrptr, p->auth_info_required[i]);
-         attrptr += strlen(attrptr);
-       }
-      }
-      else
-       *attrptr = '\0';
     }
   }
 
@@ -2627,35 +2661,20 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
  * 'cupsdSetPrinterReasons()' - Set/update the reasons strings.
  */
 
-void
+int                                    /* O - 1 if something changed, 0 otherwise */
 cupsdSetPrinterReasons(
-    cupsd_printer_t  *p,               /* I - Printer */
-    const char *s)                     /* I - Reasons strings */
+    cupsd_printer_t *p,                        /* I - Printer */
+    const char      *s)                        /* I - Reasons strings */
 {
-  int          i;                      /* Looping var */
+  int          i,                      /* Looping var */
+               changed = 0;            /* Did something change? */
   const char   *sptr;                  /* Pointer into reasons */
   char         reason[255],            /* Reason string */
                *rptr;                  /* Pointer into reason */
 
 
-  if (!p || !s)
-  {
-    cupsdLogMessage(CUPSD_LOG_EMERG,
-                    "cupsdSetPrinterReasons called with p=%p and s=%p!", p, s);
-    return;
-  }
-
-  if (LogLevel == CUPSD_LOG_DEBUG2)
-  {
-    cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                   "cupsdSetPrinterReasons(p=%p(%s),s=\"%s\"", p, p->name, s);
-    cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSetPrinterReasons: num_reasons=%d",
-                    p->num_reasons);
-    for (i = 0; i < p->num_reasons; i ++)
-      cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                      "cupsdSetPrinterReasons: reasons[%d]=%p(\"%s\")", i,
-                     p->reasons[i], p->reasons[i]);
-  }
+  cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                 "cupsdSetPrinterReasons(p=%p(%s),s=\"%s\"", p, p->name, s);
 
   if (s[0] == '-' || s[0] == '+')
   {
@@ -2677,15 +2696,13 @@ cupsdSetPrinterReasons(
       _cupsStrFree(p->reasons[i]);
 
     p->num_reasons = 0;
+    changed        = 1;
 
-    cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
-
-    if (PrintcapFormat == PRINTCAP_PLIST)
-      cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
+    dirty_printer(p);
   }
 
   if (!strcmp(s, "none"))
-    return;
+    return (changed);
 
  /*
   * Loop through all of the reasons...
@@ -2722,11 +2739,8 @@ cupsdSetPrinterReasons(
          * Found a match, so remove it...
          */
 
-         cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                         "cupsdSetPrinterReasons: Removing \"%s\" at index %d",
-                         reason, i);
-
          p->num_reasons --;
+          changed = 1;
          _cupsStrFree(p->reasons[i]);
 
          if (i < p->num_reasons)
@@ -2737,11 +2751,7 @@ cupsdSetPrinterReasons(
            cupsdSetPrinterState(p, IPP_PRINTER_IDLE, 1);
 
           if (strcmp(reason, "connecting-to-device"))
-           cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
-
-         if (PrintcapFormat == PRINTCAP_PLIST)
-           cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
-
+           dirty_printer(p);
          break;
        }
     }
@@ -2762,38 +2772,23 @@ cupsdSetPrinterReasons(
          cupsdLogMessage(CUPSD_LOG_ALERT,
                          "Too many printer-state-reasons values for %s (%d)",
                          p->name, i + 1);
-          return;
+          return (changed);
         }
 
-       cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                       "cupsdSetPrinterReasons: Adding \"%s\" at index %d",
-                       reason, i);
-
         p->reasons[i] = _cupsStrAlloc(reason);
        p->num_reasons ++;
+        changed = 1;
 
        if (!strcmp(reason, "paused") && p->state != IPP_PRINTER_STOPPED)
          cupsdSetPrinterState(p, IPP_PRINTER_STOPPED, 1);
 
        if (strcmp(reason, "connecting-to-device"))
-         cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
-
-       if (PrintcapFormat == PRINTCAP_PLIST)
-         cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
+         dirty_printer(p);
       }
     }
   }
 
-  if (LogLevel == CUPSD_LOG_DEBUG2)
-  {
-    cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                    "cupsdSetPrinterReasons: NEW num_reasons=%d",
-                    p->num_reasons);
-    for (i = 0; i < p->num_reasons; i ++)
-      cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                      "cupsdSetPrinterReasons: NEW reasons[%d]=%p(\"%s\")", i,
-                     p->reasons[i], p->reasons[i]);
-  }
+  return (changed);
 }
 
 
@@ -2808,6 +2803,12 @@ cupsdSetPrinterState(
     int             update)            /* I - Update printers.conf? */
 {
   ipp_pstate_t old_state;              /* Old printer state */
+  static const char * const printer_states[] =
+  {                                    /* State strings */
+    "idle",
+    "processing",
+    "stopped"
+  };
 
 
  /*
@@ -2821,9 +2822,6 @@ cupsdSetPrinterState(
   * Set the new state...
   */
 
-  if (PrintcapFormat == PRINTCAP_PLIST)
-    cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
-
   old_state = p->state;
   p->state  = s;
 
@@ -2831,9 +2829,9 @@ cupsdSetPrinterState(
   {
     cupsdAddEvent(s == IPP_PRINTER_STOPPED ? CUPSD_EVENT_PRINTER_STOPPED :
                       CUPSD_EVENT_PRINTER_STATE, p, NULL,
-                 "%s \"%s\" state changed.",
+                 "%s \"%s\" state changed to %s.",
                  (p->type & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
-                 p->name);
+                 p->name, printer_states[p->state - IPP_PRINTER_IDLE]);
 
    /*
     * Let the browse code know this needs to be updated...
@@ -2848,12 +2846,21 @@ cupsdSetPrinterState(
 #endif /* __sgi */
   }
 
+ /*
+  * Set/clear the paused reason as needed...
+  */
+
   if (s == IPP_PRINTER_STOPPED)
     cupsdSetPrinterReasons(p, "+paused");
   else
     cupsdSetPrinterReasons(p, "-paused");
 
-  cupsdAddPrinterHistory(p);
+ /*
+  * Clear the message for the queue when going to processing...
+  */
+
+  if (s == IPP_PRINTER_PROCESSING)
+    p->state_message[0] = '\0';
 
  /*
   * Let the browse protocols reflect the change...
@@ -2867,14 +2874,9 @@ cupsdSetPrinterState(
   * to stopped (or visa-versa)...
   */
 
-  if ((old_state == IPP_PRINTER_STOPPED) != (s == IPP_PRINTER_STOPPED) &&
-      update)
-  {
-    if (p->type & CUPS_PRINTER_CLASS)
-      cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
-    else
-      cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
-  }
+  if (update &&
+      (old_state == IPP_PRINTER_STOPPED) != (s == IPP_PRINTER_STOPPED))
+    dirty_printer(p);
 }
 
 
@@ -2886,9 +2888,6 @@ void
 cupsdStopPrinter(cupsd_printer_t *p,   /* I - Printer to stop */
                  int             update)/* I - Update printers.conf? */
 {
-  cupsd_job_t  *job;                   /* Active print job */
-
-
  /*
   * Set the printer state...
   */
@@ -2899,33 +2898,9 @@ cupsdStopPrinter(cupsd_printer_t *p,     /* I - Printer to stop */
   * See if we have a job printing on this printer...
   */
 
-  if (p->job)
-  {
-   /*
-    * Get pointer to job...
-    */
-
-    job = (cupsd_job_t *)p->job;
-
-   /*
-    * Stop it...
-    */
-
-    cupsdStopJob(job, 0);
-
-   /*
-    * Reset the state to pending...
-    */
-
-    job->state->values[0].integer = IPP_JOB_PENDING;
-    job->state_value              = IPP_JOB_PENDING;
-    job->dirty                    = 1;
-
-    cupsdMarkDirty(CUPSD_DIRTY_JOBS);
-
-    cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, p, job,
-                 "Job stopped due to printer being paused");
-  }
+  if (p->job && p->job->state_value == IPP_JOB_PROCESSING)
+    cupsdSetJobState(p->job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT,
+                     "Job stopped due to printer being paused.");
 }
 
 
@@ -3282,6 +3257,8 @@ cupsdWritePrintcap(void)
   if (!Printcap || !*Printcap)
     return;
 
+  cupsdLogMessage(CUPSD_LOG_INFO, "Generating printcap %s...", Printcap);
+
  /*
   * Open the printcap file...
   */
@@ -3469,11 +3446,10 @@ add_printer_defaults(cupsd_printer_t *p)/* I - Printer */
     cupsArrayAdd(CommonDefaults, _cupsStrAlloc("job-hold-until-default"));
     cupsArrayAdd(CommonDefaults, _cupsStrAlloc("job-priority-default"));
     cupsArrayAdd(CommonDefaults, _cupsStrAlloc("job-sheets-default"));
-    cupsArrayAdd(CommonDefaults, _cupsStrAlloc("media-default"));
+    cupsArrayAdd(CommonDefaults, _cupsStrAlloc("media-col-default"));
     cupsArrayAdd(CommonDefaults, _cupsStrAlloc("number-up-default"));
     cupsArrayAdd(CommonDefaults,
                  _cupsStrAlloc("orientation-requested-default"));
-    cupsArrayAdd(CommonDefaults, _cupsStrAlloc("sides-default"));
   }
 
  /*
@@ -3527,10 +3503,6 @@ add_printer_defaults(cupsd_printer_t *p)/* I - Printer */
     ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
                   "number-up-default", 1);
 
-  if (!cupsGetOption("orientation-requested", p->num_options, p->options))
-    ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE,
-                 "orientation-requested-default", NULL, NULL);
-
   if (!cupsGetOption("notify-lease-duration", p->num_options, p->options))
     ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
                  "notify-lease-duration-default", DefaultLeaseDuration);
@@ -3538,6 +3510,14 @@ add_printer_defaults(cupsd_printer_t *p)/* I - Printer */
   if (!cupsGetOption("notify-events", p->num_options, p->options))
     ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
                 "notify-events-default", NULL, "job-completed");
+
+  if (!cupsGetOption("orientation-requested", p->num_options, p->options))
+    ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE,
+                 "orientation-requested-default", NULL, NULL);
+
+  if (!cupsGetOption("print-quality", p->num_options, p->options))
+    ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
+                  "print-quality-default", IPP_QUALITY_NORMAL);
 }
 
 
@@ -3553,26 +3533,59 @@ add_printer_filter(
 {
   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 */
-  mime_type_t  *temptype;              /* MIME type looping var */
+  mime_type_t  *temptype,              /* MIME type looping var */
+               *desttype;              /* Destination MIME type */
   char         filename[1024],         /* Full filter filename */
                *dirsep;                /* Pointer to directory separator */
   struct stat  fileinfo;               /* File information */
 
 
+  cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                  "add_printer_filter(p=%p(%s), filtertype=%p(%s/%s), "
+                 "filter=\"%s\")", p, p->name, filtertype, filtertype->super,
+                 filtertype->type, filter);
+
  /*
-  * Parse the filter string; it should be in the following format:
+  * Parse the filter string; it should be in one of the following formats:
   *
-  *     super/type cost program
+  *     source/type cost program
+  *     source/type dest/type cost program
   */
 
-  if (sscanf(filter, "%15[^/]/%31s%d%*[ \t]%1023[^\n]", super, type, &cost,
-             program) != 4)
+  if (sscanf(filter, "%15[^/]/%255s%*[ \t]%15[^/]/%255s%d%*[ \t]%1023[^\n]",
+             super, type, dsuper, dtype, &cost, program) == 6)
   {
-    cupsdLogMessage(CUPSD_LOG_ERROR, "%s: invalid filter string \"%s\"!",
-                    p->name, filter);
-    return;
+    snprintf(dest, sizeof(dest), "%s/%s/%s", p->name, dsuper, dtype);
+
+    if ((desttype = mimeType(MimeDatabase, "printer", dest)) == NULL)
+    {
+      desttype = mimeAddType(MimeDatabase, "printer", dest);
+      if (!p->dest_types)
+        p->dest_types = cupsArrayNew(NULL, NULL);
+
+      cupsArrayAdd(p->dest_types, desttype);
+    }
+
+  }
+  else
+  {
+    if (sscanf(filter, "%15[^/]/%255s%d%*[ \t]%1023[^\n]", super, type, &cost,
+               program) == 4)
+    {
+      desttype = filtertype;
+    }
+    else
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR, "%s: invalid filter string \"%s\"!",
+                      p->name, filter);
+      return;
+    }
   }
 
  /*
@@ -3589,76 +3602,64 @@ add_printer_filter(
 
     if (stat(filename, &fileinfo))
     {
+      memset(&fileinfo, 0, sizeof(fileinfo));
+
       snprintf(p->state_message, sizeof(p->state_message),
-               "Filter \"%s\" for printer \"%s\" not available: %s",
-              filename, p->name, strerror(errno));
-      cupsdSetPrinterReasons(p, "+cups-missing-filter-error");
+               "Printer driver \"%s\" not available: %s", filename,
+              strerror(errno));
+      cupsdSetPrinterReasons(p, "+cups-missing-filter-warning");
 
-      cupsdLogMessage(CUPSD_LOG_ERROR, "%s", p->state_message);
-      return;
+      cupsdLogMessage(CUPSD_LOG_ERROR, "%s: %s", p->name, p->state_message);
     }
 
    /*
     * When running as root, do additional security checks...
     */
 
-    if (!RunUser)
+    else if (!RunUser)
     {
      /*
       * Only use filters that are owned by root and do not have world write
       * permissions.
       */
 
-      if (fileinfo.st_uid || (fileinfo.st_mode & S_IWOTH) != 0)
+      if (fileinfo.st_uid ||
+          (fileinfo.st_gid && (fileinfo.st_mode & S_IWGRP)) ||
+          (fileinfo.st_mode & (S_ISUID | S_IWOTH)) != 0)
       {
-       if (fileinfo.st_uid)
-         snprintf(p->state_message, sizeof(p->state_message),
-                  "Filter \"%s\" for printer \"%s\" not owned by root",
-                  filename, p->name);
-       else
-         snprintf(p->state_message, sizeof(p->state_message),
-                  "Filter \"%s\" for printer \"%s\" has insecure permissions "
-                  "(0%o)", filename, p->name, fileinfo.st_mode);
+       snprintf(p->state_message, sizeof(p->state_message),
+                "Printer driver \"%s\" has insecure permissions "
+                "(0%o/uid=%d/gid=%d).", filename, fileinfo.st_mode,
+                (int)fileinfo.st_uid, (int)fileinfo.st_gid);
 
-       cupsdSetPrinterReasons(p, "+cups-insecure-filter-error");
+       cupsdSetPrinterReasons(p, "+cups-insecure-filter-warning");
 
-       cupsdLogMessage(CUPSD_LOG_ERROR, "%s", p->state_message);
-       return;
+       cupsdLogMessage(CUPSD_LOG_WARN, "%s: %s", p->name, p->state_message);
       }
-
-     /*
-      * Similarly, check that the parent directory is also owned by root and
-      * does not have world write permissions.
-      */
-
-      if ((dirsep = strrchr(filename, '/')) != NULL)
-        *dirsep = '\0';
-
-      if (stat(filename, &fileinfo))
+      else if (fileinfo.st_mode)
       {
-       snprintf(p->state_message, sizeof(p->state_message),
-                "Filter directory \"%s\" for printer \"%s\" not accessible: %s",
-                filename, p->name, strerror(errno));
-       cupsdSetPrinterReasons(p, "+cups-missing-filter-error");
+       /*
+       * Similarly, check that the parent directory is also owned by root and
+       * does not have world write permissions.
+       */
 
-       cupsdLogMessage(CUPSD_LOG_ERROR, "%s", p->state_message);
-       return;
-      }
-      else if (fileinfo.st_uid || (fileinfo.st_mode & S_IWOTH) != 0)
-      {
-       if (fileinfo.st_uid)
-         snprintf(p->state_message, sizeof(p->state_message),
-                  "Filter directory \"%s\" for printer \"%s\" not owned by "
-                  "root", filename, p->name);
-       else
+       if ((dirsep = strrchr(filename, '/')) != NULL)
+         *dirsep = '\0';
+
+       if (!stat(filename, &fileinfo) &&
+           (fileinfo.st_uid ||
+            (fileinfo.st_gid && (fileinfo.st_mode & S_IWGRP)) ||
+            (fileinfo.st_mode & (S_ISUID | S_IWOTH)) != 0))
+       {
          snprintf(p->state_message, sizeof(p->state_message),
-                  "Filter directory \"%s\" for printer \"%s\" has insecure "
-                  "permissions (0%o)", filename, p->name, fileinfo.st_mode);
+                  "Printer driver directory \"%s\" has insecure permissions "
+                  "(0%o/uid=%d/gid=%d).", filename, fileinfo.st_mode,
+                  (int)fileinfo.st_uid, (int)fileinfo.st_gid);
 
-       cupsdSetPrinterReasons(p, "+cups-insecure-filter-error");
+         cupsdSetPrinterReasons(p, "+cups-insecure-filter-warning");
 
-       cupsdLogMessage(CUPSD_LOG_ERROR, "%s", p->state_message);
-       return;
+         cupsdLogMessage(CUPSD_LOG_WARN, "%s: %s", p->name, p->state_message);
+       }
       }
     }
   }
@@ -3674,12 +3675,33 @@ add_printer_filter(
          !strcasecmp(temptype->super, super)) &&
         (type[0] == '*' || !strcasecmp(temptype->type, type)))
     {
-      cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                      "add_printer_filter: %s: adding filter %s/%s %s/%s %d %s",
-                      p->name, temptype->super, temptype->type,
-                     filtertype->super, filtertype->type,
-                      cost, program);
-      mimeAddFilter(MimeDatabase, temptype, filtertype, cost, program);
+      if (desttype != filtertype)
+      {
+        cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                       "add_printer_filter: %s: adding filter %s/%s %s/%s %d "
+                       "%s", p->name, temptype->super, temptype->type,
+                       desttype->super, desttype->type,
+                       cost, program);
+        mimeAddFilter(MimeDatabase, temptype, desttype, cost, program);
+
+        if (!mimeFilterLookup(MimeDatabase, desttype, filtertype))
+        {
+          cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                         "add_printer_filter: %s: adding filter %s/%s %s/%s "
+                         "0 -", p->name, desttype->super, desttype->type,
+                         filtertype->super, filtertype->type);
+          mimeAddFilter(MimeDatabase, desttype, filtertype, cost, "-");
+        }
+      }
+      else
+      {
+        cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                       "add_printer_filter: %s: adding filter %s/%s %s/%s %d "
+                       "%s", p->name, temptype->super, temptype->type,
+                       filtertype->super, filtertype->type,
+                       cost, program);
+        mimeAddFilter(MimeDatabase, temptype, filtertype, cost, program);
+      }
     }
 }
 
@@ -3729,6 +3751,9 @@ add_printer_formats(cupsd_printer_t *p)   /* I - Printer */
        type;
        type = mimeNextType(MimeDatabase))
   {
+    if (!strcasecmp(type->super, "printer"))
+      continue;
+
     snprintf(mimetype, sizeof(mimetype), "%s/%s", type->super, type->type);
 
     if ((filters = mimeFilter(MimeDatabase, type, p->filetype, NULL)) != NULL)
@@ -3746,10 +3771,6 @@ add_printer_formats(cupsd_printer_t *p)  /* I - Printer */
                       p->name, mimetype);
   }
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "add_printer_formats: %s: %d supported types",
-                 p->name, cupsArrayCount(p->filetypes) + 1);
-
  /*
   * Add the file formats that can be filtered...
   */
@@ -3760,9 +3781,13 @@ add_printer_formats(cupsd_printer_t *p)  /* I - Printer */
   else
     i = 0;
 
+  cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                  "add_printer_formats: %s: %d supported types",
+                  p->name, cupsArrayCount(p->filetypes) + i);
+
   attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE,
                        "document-format-supported",
-                      cupsArrayCount(p->filetypes) + 1, NULL, NULL);
+                       cupsArrayCount(p->filetypes) + i, NULL, NULL);
 
   if (i)
     attr->values[0].string.text = _cupsStrAlloc("application/octet-stream");
@@ -3782,40 +3807,50 @@ add_printer_formats(cupsd_printer_t *p) /* I - Printer */
     mime_filter_t      *filter;        /* MIME filter looping var */
 
 
-    pdl[0] = '\0';
-
-    if (mimeType(MimeDatabase, "application", "pdf"))
-      strlcat(pdl, "application/pdf,", sizeof(pdl));
-
-    if (mimeType(MimeDatabase, "application", "postscript"))
-      strlcat(pdl, "application/postscript,", sizeof(pdl));
-
-    if (mimeType(MimeDatabase, "application", "vnd.cups-raster"))
-      strlcat(pdl, "application/vnd.cups-raster,", sizeof(pdl));
-
    /*
-    * Determine if this is a Tioga PrintJobMgr based queue...
+    * We only support raw printing if this is not a Tioga PrintJobMgr based
+    * queue and if application/octet-stream is a known type...
     */
 
     for (filter = (mime_filter_t *)cupsArrayFirst(MimeDatabase->filters);
         filter;
         filter = (mime_filter_t *)cupsArrayNext(MimeDatabase->filters))
     {
-      if (filter->dst == p->filetype && filter->filter && 
+      if (filter->dst == p->filetype && filter->filter &&
          strstr(filter->filter, "PrintJobMgr"))
        break;
     }
 
-   /*
-    * We only support raw printing if this is not a Tioga PrintJobMgr based
-    * queue and if application/octet-stream is a known conversion...
-    */
+    pdl[0] = '\0';
 
     if (!filter && mimeType(MimeDatabase, "application", "octet-stream"))
       strlcat(pdl, "application/octet-stream,", sizeof(pdl));
 
-    if (mimeType(MimeDatabase, "image", "png"))
-      strlcat(pdl, "image/png,", sizeof(pdl));
+   /*
+    * Then list a bunch of formats that are supported by the printer...
+    */
+
+    for (type = (mime_type_t *)cupsArrayFirst(p->filetypes);
+        type;
+        type = (mime_type_t *)cupsArrayNext(p->filetypes))
+    {
+      if (!strcasecmp(type->super, "application"))
+      {
+        if (!strcasecmp(type->type, "pdf"))
+         strlcat(pdl, "application/pdf,", sizeof(pdl));
+        else if (!strcasecmp(type->type, "postscript"))
+         strlcat(pdl, "application/postscript,", sizeof(pdl));
+      }
+      else if (!strcasecmp(type->super, "image"))
+      {
+        if (!strcasecmp(type->type, "jpeg"))
+         strlcat(pdl, "image/jpeg,", sizeof(pdl));
+       else if (!strcasecmp(type->type, "png"))
+         strlcat(pdl, "image/png,", sizeof(pdl));
+       else if (!strcasecmp(type->type, "pwg-raster"))
+         strlcat(pdl, "image/pwg-raster,", sizeof(pdl));
+      }
+    }
 
     if (pdl[0])
       pdl[strlen(pdl) - 1] = '\0';     /* Remove trailing comma */
@@ -3826,21 +3861,6 @@ add_printer_formats(cupsd_printer_t *p)  /* I - Printer */
 }
 
 
-/*
- * 'add_string_array()' - Add a string to an array of CUPS strings.
- */
-
-static void
-add_string_array(cups_array_t **a,     /* I - Array */
-                const char   *s)       /* I - String */
-{
-  if (!*a)
-    *a = cupsArrayNew(NULL, NULL);
-
-  cupsArrayAdd(*a, _cupsStrAlloc(s));
-}
-
-
 /*
  * 'compare_printers()' - Compare two printers.
  */
@@ -3864,6 +3884,7 @@ delete_printer_filters(
     cupsd_printer_t *p)                        /* I - Printer to remove from */
 {
   mime_filter_t        *filter;                /* MIME filter looping var */
+  mime_type_t  *type;                  /* Destination types for filters */
 
 
  /*
@@ -3881,7 +3902,8 @@ delete_printer_filters(
   for (filter = mimeFirstFilter(MimeDatabase);
        filter;
        filter = mimeNextFilter(MimeDatabase))
-    if (filter->dst == p->filetype)
+    if (filter->dst == p->filetype || filter->dst == p->prefiltertype ||
+        cupsArrayFind(p->dest_types, filter->dst))
     {
      /*
       * Delete the current filter...
@@ -3889,26 +3911,37 @@ delete_printer_filters(
 
       mimeDeleteFilter(MimeDatabase, filter);
     }
+
+  for (type = (mime_type_t *)cupsArrayFirst(p->dest_types);
+       type;
+       type = (mime_type_t *)cupsArrayNext(p->dest_types))
+    mimeDeleteType(MimeDatabase, type);
+
+  cupsArrayDelete(p->dest_types);
+  p->dest_types = NULL;
+
+  cupsdSetPrinterReasons(p, "-cups-insecure-filter-warning"
+                            ",cups-missing-filter-warning");
 }
 
 
 /*
- * 'delete_string_array()' - Delete an array of CUPS strings.
+ * 'dirty_printer()' - Mark config and state files dirty for the specified
+ *                     printer.
  */
 
 static void
-delete_string_array(cups_array_t **a)  /* I - Array */
+dirty_printer(cupsd_printer_t *p)      /* I - Printer */
 {
-  char *ptr;                           /* Current string */
-
-
-  for (ptr = (char *)cupsArrayFirst(*a);
-       ptr;
-       ptr = (char *)cupsArrayNext(*a))
-    _cupsStrFree(ptr);
+  if (p->type & CUPS_PRINTER_DISCOVERED)
+    cupsdMarkDirty(CUPSD_DIRTY_REMOTE);
+  else if (p->type & CUPS_PRINTER_CLASS)
+    cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
+  else
+    cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
 
-  cupsArrayDelete(*a);
-  *a = NULL;
+  if (PrintcapFormat == PRINTCAP_PLIST)
+    cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
 }
 
 
@@ -3919,27 +3952,37 @@ delete_string_array(cups_array_t **a)   /* I - Array */
 static void
 load_ppd(cupsd_printer_t *p)           /* I - Printer */
 {
-  int          i;                      /* Looping var */
-  cups_file_t  *cache;                 /* Cache file */
+  int          i, j, k;                /* Looping vars */
   char         cache_name[1024];       /* Cache filename */
   struct stat  cache_info;             /* Cache file info */
   ppd_file_t   *ppd;                   /* PPD file */
   char         ppd_name[1024];         /* PPD filename */
   struct stat  ppd_info;               /* PPD file info */
   int          num_media;              /* Number of media options */
-  char         custom_in[256],         /* Custom size name in inches */
-               custom_mm[256];         /* Custom size name in millimeters */
-  ppd_size_t   *size;                  /* Current size */
-  ppd_option_t *output_bin,            /* OutputBin options */
-               *duplex;                /* Duplex options */
+  ppd_size_t   *size;                  /* Current PPD size */
+  ppd_option_t *duplex,                /* Duplex option */
+               *output_bin,            /* OutputBin option */
+               *output_mode,           /* OutputMode option */
+               *resolution;            /* (Set|JCL|)Resolution option */
+  ppd_choice_t *choice,                /* Current PPD choice */
+               *input_slot,            /* Current input slot */
+               *media_type;            /* Current media type */
   ppd_attr_t   *ppd_attr;              /* PPD attribute */
-  _cups_pwg_media_t *pwgmedia;         /* Matching PWG size name */
+  int          xdpi,                   /* Horizontal resolution */
+               ydpi;                   /* Vertical resolution */
+  const char   *resptr;                /* Pointer into resolution keyword */
+  _pwg_size_t  *pwgsize;               /* Current PWG size */
+  _pwg_map_t   *pwgsource,             /* Current PWG source */
+               *pwgtype;               /* Current PWG type */
   ipp_attribute_t *attr;               /* Attribute data */
-  ipp_t                *media_col_default,     /* media-col-default collection value */
-               *media_size;            /* media-size collection value */
   ipp_value_t  *val;                   /* Attribute value */
-  int          num_finishings;         /* Number of finishings */
-  int          finishings[5];          /* finishings-supported values */
+  int          num_finishings,         /* Number of finishings */
+               finishings[5];          /* finishings-supported values */
+  int          num_qualities,          /* Number of print-quality values */
+               qualities[3];           /* print-quality values */
+  int          num_margins,            /* Number of media-*-margin-supported values */
+               margins[16];            /* media-*-margin-supported values */
+  const char   *filter;                /* Current filter */
   static const char * const sides[3] = /* sides-supported values */
                {
                  "one-sided",
@@ -3958,7 +4001,7 @@ load_ppd(cupsd_printer_t *p)              /* I - Printer */
   * Check to see if the cache is up-to-date...
   */
 
-  snprintf(cache_name, sizeof(cache_name), "%s/%s.ipp", CacheDir, p->name);
+  snprintf(cache_name, sizeof(cache_name), "%s/%s.data", CacheDir, p->name);
   if (stat(cache_name, &cache_info))
     cache_info.st_mtime = 0;
 
@@ -3969,23 +4012,22 @@ load_ppd(cupsd_printer_t *p)            /* I - Printer */
   ippDelete(p->ppd_attrs);
   p->ppd_attrs = ippNew();
 
-  if (cache_info.st_mtime >= ppd_info.st_mtime &&
-      (cache = cupsFileOpen(cache_name, "r")) != NULL)
-  {
-   /*
-    * Load cached information and return...
-    */
+  _ppdCacheDestroy(p->pc);
+  p->pc = NULL;
 
+  if (cache_info.st_mtime >= ppd_info.st_mtime)
+  {
     cupsdLogMessage(CUPSD_LOG_DEBUG, "load_ppd: Loading %s...", cache_name);
 
-    if (ippReadIO(cache, (ipp_iocb_t)cupsFileRead, 1, NULL,
-                  p->ppd_attrs) == IPP_DATA)
+    if ((p->pc = _ppdCacheCreateWithFile(cache_name, &p->ppd_attrs)) != NULL &&
+        p->ppd_attrs)
     {
-      cupsFileClose(cache);
+     /*
+      * Loaded successfully!
+      */
+
       return;
     }
-
-    cupsFileClose(cache);
   }
 
  /*
@@ -3996,9 +4038,6 @@ load_ppd(cupsd_printer_t *p)              /* I - Printer */
 
   cupsdLogMessage(CUPSD_LOG_DEBUG, "load_ppd: Loading %s...", ppd_name);
 
-  delete_string_array(&(p->filters));
-  delete_string_array(&(p->pre_filters));
-
   p->type &= ~CUPS_PRINTER_OPTIONS;
   p->type |= CUPS_PRINTER_BW;
 
@@ -4011,6 +4050,8 @@ load_ppd(cupsd_printer_t *p)              /* I - Printer */
     * Add make/model and other various attributes...
     */
 
+    p->pc = _ppdCacheCreateWithPPD(ppd);
+
     ppdMarkDefaults(ppd);
 
     if (ppd->color_device)
@@ -4026,8 +4067,62 @@ load_ppd(cupsd_printer_t *p)             /* I - Printer */
     ippAddBoolean(p->ppd_attrs, IPP_TAG_PRINTER, "color-supported",
                  ppd->color_device);
     if (ppd->throughput)
+    {
       ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
                    "pages-per-minute", ppd->throughput);
+      if (ppd->color_device)
+       ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                     "pages-per-minute-color", ppd->throughput);
+    }
+    else
+    {
+     /*
+      * When there is no speed information, just say "1 page per minute".
+      */
+
+      ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                   "pages-per-minute", 1);
+      if (ppd->color_device)
+       ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                     "pages-per-minute-color", 1);
+    }
+
+    num_qualities = 0;
+
+    if ((output_mode = ppdFindOption(ppd, "OutputMode")) != NULL)
+    {
+      if (ppdFindChoice(output_mode, "draft") ||
+          ppdFindChoice(output_mode, "fast"))
+        qualities[num_qualities ++] = IPP_QUALITY_DRAFT;
+
+      qualities[num_qualities ++] = IPP_QUALITY_NORMAL;
+
+      if (ppdFindChoice(output_mode, "best") ||
+          ppdFindChoice(output_mode, "high"))
+        qualities[num_qualities ++] = IPP_QUALITY_HIGH;
+    }
+    else if ((ppd_attr = ppdFindAttr(ppd, "APPrinterPreset", NULL)) != NULL)
+    {
+      do
+      {
+        if (strstr(ppd_attr->spec, "draft") ||
+           strstr(ppd_attr->spec, "Draft"))
+       {
+         qualities[num_qualities ++] = IPP_QUALITY_DRAFT;
+         break;
+       }
+      }
+      while ((ppd_attr = ppdFindNextAttr(ppd, "APPrinterPreset",
+                                         NULL)) != NULL);
+
+      qualities[num_qualities ++] = IPP_QUALITY_NORMAL;
+      qualities[num_qualities ++] = IPP_QUALITY_HIGH;
+    }
+    else
+      qualities[num_qualities ++] = IPP_QUALITY_NORMAL;
+
+    ippAddIntegers(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
+                   "print-quality-supported", num_qualities, qualities);
 
     if (ppd->nickname)
     {
@@ -4058,108 +4153,296 @@ load_ppd(cupsd_printer_t *p)          /* I - Printer */
     * Add media options from the PPD file...
     */
 
-    if (ppd->num_sizes == 0)
+    if (ppd->num_sizes == 0 || !p->pc)
     {
-      cupsdLogMessage(CUPSD_LOG_CRIT,
-                     "The PPD file for printer %s contains no media "
-                     "options and is therefore invalid!", p->name);
+      if (!ppdFindAttr(ppd, "APScannerOnly", NULL))
+       cupsdLogMessage(CUPSD_LOG_CRIT,
+                       "The PPD file for printer %s contains no media "
+                       "options and is therefore invalid!", p->name);
+
+      ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                  "media-default", NULL, "unknown");
+      ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                  "media-supported", NULL, "unknown");
     }
     else
     {
-      num_media = ppd->num_sizes;
-      if (ppd->variable_sizes)
-       num_media ++;
+     /*
+      * media-default
+      */
 
-      attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-                          "media-supported", num_media, NULL, NULL);
-      if (attr != NULL)
+      if ((size = ppdPageSize(ppd, NULL)) != NULL)
+        pwgsize = _ppdCacheGetSize(p->pc, size->name);
+      else
+        pwgsize = NULL;
+
+      ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                  "media-default", NULL,
+                  pwgsize ? pwgsize->map.pwg : "unknown");
+
+     /*
+      * media-col-default
+      */
+
+      if (pwgsize)
+      {
+        ipp_t  *col;                   /* Collection value */
+
+       input_slot = ppdFindMarkedChoice(ppd, "InputSlot");
+       media_type = ppdFindMarkedChoice(ppd, "MediaType");
+       col        = new_media_col(pwgsize,
+                                  input_slot ?
+                                      _ppdCacheGetSource(p->pc,
+                                                         input_slot->choice) :
+                                      NULL,
+                                  media_type ?
+                                      _ppdCacheGetType(p->pc,
+                                                       media_type->choice) :
+                                      NULL);
+
+       ippAddCollection(p->ppd_attrs, IPP_TAG_PRINTER, "media-col-default",
+                        col);
+        ippDelete(col);
+      }
+
+     /*
+      * media-supported
+      */
+
+      num_media = p->pc->num_sizes;
+      if (p->pc->custom_min_keyword)
+       num_media += 2;
+
+      if ((attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                               "media-supported", num_media, NULL,
+                               NULL)) != NULL)
       {
        val = attr->values;
 
-        for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++)
+        for (i = p->pc->num_sizes, pwgsize = p->pc->sizes;
+            i > 0;
+            i --, pwgsize ++, val ++)
+         val->string.text = _cupsStrRetain(pwgsize->map.pwg);
+
+        if (p->pc->custom_min_keyword)
        {
-         if (strcasecmp(size->name, "Custom"))
-         {
-           if ((pwgmedia = _cupsPWGMediaBySize(size->width,
-                                               size->length)) != NULL)
-           {
-             val->string.text = _cupsStrAlloc(pwgmedia->pwg);
-           }
-           else
-           {
-             snprintf(custom_in, sizeof(custom_in), "adobe_%s_%gx%gin",
-                      size->name, size->width / 72.0, size->length / 72.0);
-             snprintf(custom_mm, sizeof(custom_mm), "adobe_%s_%gx%gmm",
-                      size->name, size->width * 25.4 / 72.0,
-                      size->length * 25.4 / 72.0);
-              if (strlen(custom_in) < strlen(custom_mm))
-               val->string.text = _cupsStrAlloc(custom_in);
-             else
-               val->string.text = _cupsStrAlloc(custom_mm);
-           }
+         val->string.text = _cupsStrRetain(p->pc->custom_min_keyword);
+         val ++;
+         val->string.text = _cupsStrRetain(p->pc->custom_max_keyword);
+        }
+      }
 
-           if (size->marked)
-           {
-            /*
-             * Add media-default...
-             */
+     /*
+      * media-source-supported
+      */
 
-             ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-                          "media-default", NULL, val->string.text);
+      if (p->pc->num_sources > 0 &&
+          (attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                               "media-source-supported", p->pc->num_sources,
+                               NULL, NULL)) != NULL)
+      {
+       for (i = p->pc->num_sources, pwgsource = p->pc->sources,
+                val = attr->values;
+            i > 0;
+            i --, pwgsource ++, val ++)
+         val->string.text = _cupsStrRetain(pwgsource->pwg);
+      }
 
-             /*
-             * Add media-col-default...
-             */
-
-             media_size = ippNew();
-             ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
-                           "x-dimension", (int)(size->width * 2540.0 / 72.0));
-             ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
-                           "y-dimension", (int)(size->length * 2540.0 / 72.0));
-
-             media_col_default = ippNew();
-             ippAddString(media_col_default, IPP_TAG_PRINTER,
-                          IPP_TAG_KEYWORD | IPP_TAG_COPY, "media-color", NULL,
-                          "white");
-             ippAddString(media_col_default, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-                          "media-key", NULL,val->string.text);
-             ippAddCollection(media_col_default, IPP_TAG_PRINTER, "media-size",
-                              media_size);
-             ippAddString(media_col_default, IPP_TAG_PRINTER,
-                          IPP_TAG_KEYWORD | IPP_TAG_COPY, "media-type", NULL,
-                          "stationary");
-
-              ippAddCollection(p->ppd_attrs, IPP_TAG_PRINTER,
-                              "media-col-default", media_col_default);
-           }
+     /*
+      * media-type-supported
+      */
 
-            val ++;
-         }
+      if (p->pc->num_types > 0 &&
+          (attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                               "media-type-supported", p->pc->num_types,
+                               NULL, NULL)) != NULL)
+      {
+       for (i = p->pc->num_types, pwgtype = p->pc->types,
+                val = attr->values;
+            i > 0;
+            i --, pwgtype ++, val ++)
+         val->string.text = _cupsStrRetain(pwgtype->pwg);
+      }
+
+     /*
+      * media-*-margin-supported
+      */
+
+      for (i = p->pc->num_sizes, pwgsize = p->pc->sizes, num_margins = 0;
+          i > 0 && num_margins < (int)(sizeof(margins) / sizeof(margins[0]));
+          i --, pwgsize ++)
+      {
+        for (j = 0; j < num_margins; j ++)
+         if (pwgsize->bottom == margins[j])
+           break;
+
+       if (j >= num_margins)
+       {
+         margins[num_margins] = pwgsize->bottom;
+         num_margins ++;
+       }
+      }
+
+      if (num_margins > 0)
+        ippAddIntegers(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                      "media-bottom-margin-supported", num_margins, margins);
+      else
+        ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                     "media-bottom-margin-supported", 0);
+
+      for (i = p->pc->num_sizes, pwgsize = p->pc->sizes, num_margins = 0;
+          i > 0 && num_margins < (int)(sizeof(margins) / sizeof(margins[0]));
+          i --, pwgsize ++)
+      {
+        for (j = 0; j < num_margins; j ++)
+         if (pwgsize->left == margins[j])
+           break;
+
+       if (j >= num_margins)
+       {
+         margins[num_margins] = pwgsize->left;
+         num_margins ++;
        }
+      }
+
+      if (num_margins > 0)
+        ippAddIntegers(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                      "media-left-margin-supported", num_margins, margins);
+      else
+        ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                     "media-left-margin-supported", 0);
+
+      for (i = p->pc->num_sizes, pwgsize = p->pc->sizes, num_margins = 0;
+          i > 0 && num_margins < (int)(sizeof(margins) / sizeof(margins[0]));
+          i --, pwgsize ++)
+      {
+        for (j = 0; j < num_margins; j ++)
+         if (pwgsize->right == margins[j])
+           break;
 
-        if (ppd->variable_sizes)
+       if (j >= num_margins)
        {
-         snprintf(custom_in, sizeof(custom_in), "custom_min_%gx%gin",
-                  ppd->custom_min[0] / 72.0, ppd->custom_min[1] / 72.0);
-         snprintf(custom_mm, sizeof(custom_mm), "custom_min_%gx%gmm",
-                  ppd->custom_min[0] * 25.4 / 72.0,
-                  ppd->custom_min[1] * 25.4 / 72.0);
-         if (strlen(custom_in) < strlen(custom_mm))
-           val->string.text = _cupsStrAlloc(custom_in);
-         else
-           val->string.text = _cupsStrAlloc(custom_mm);
+         margins[num_margins] = pwgsize->right;
+         num_margins ++;
+       }
+      }
+
+      if (num_margins > 0)
+        ippAddIntegers(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                      "media-right-margin-supported", num_margins, margins);
+      else
+        ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                     "media-right-margin-supported", 0);
+
+      for (i = p->pc->num_sizes, pwgsize = p->pc->sizes, num_margins = 0;
+          i > 0 && num_margins < (int)(sizeof(margins) / sizeof(margins[0]));
+          i --, pwgsize ++)
+      {
+        for (j = 0; j < num_margins; j ++)
+         if (pwgsize->top == margins[j])
+           break;
+
+       if (j >= num_margins)
+       {
+         margins[num_margins] = pwgsize->top;
+         num_margins ++;
+       }
+      }
+
+      if (num_margins > 0)
+        ippAddIntegers(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                      "media-top-margin-supported", num_margins, margins);
+      else
+        ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                     "media-top-margin-supported", 0);
+
+     /*
+      * media-col-database
+      */
+
+      num_media = p->pc->num_sizes;
+      if (p->pc->num_sources)
+      {
+        if (p->pc->num_types > 0)
+         num_media += p->pc->num_sizes * p->pc->num_sources *
+                      p->pc->num_types;
+       else
+          num_media += p->pc->num_sizes * p->pc->num_sources;
+      }
+      else if (p->pc->num_types)
+        num_media += p->pc->num_sizes * p->pc->num_types;
+
+      if ((attr = ippAddCollections(p->ppd_attrs, IPP_TAG_PRINTER,
+                                    "media-col-database", num_media,
+                                   NULL)) != NULL)
+      {
+        for (i = p->pc->num_sizes, pwgsize = p->pc->sizes, val = attr->values;
+            i > 0;
+            i --, pwgsize ++)
+       {
+        /*
+         * Start by adding the page size without source or type...
+         */
+
+         ppdMarkOption(ppd, "PageSize", pwgsize->map.ppd);
+
+          val->collection = new_media_col(pwgsize, NULL, NULL);
          val ++;
 
-         snprintf(custom_in, sizeof(custom_in), "custom_max_%gx%gin",
-                  ppd->custom_max[0] / 72.0, ppd->custom_max[1] / 72.0);
-         snprintf(custom_mm, sizeof(custom_mm), "custom_max_%gx%gmm",
-                  ppd->custom_max[0] * 25.4 / 72.0,
-                  ppd->custom_max[1] * 25.4 / 72.0);
-         if (strlen(custom_in) < strlen(custom_mm))
-           val->string.text = _cupsStrAlloc(custom_in);
-         else
-           val->string.text = _cupsStrAlloc(custom_mm);
+         /*
+         * Then add the specific, supported combinations of size, source, and
+         * type...
+         */
+
+         if (p->pc->num_sources > 0)
+         {
+           for (j = p->pc->num_sources, pwgsource = p->pc->sources;
+                j > 0;
+                j --, pwgsource ++)
+           {
+             ppdMarkOption(ppd, "InputSlot", pwgsource->ppd);
+
+             if (p->pc->num_types > 0)
+             {
+               for (k = p->pc->num_types, pwgtype = p->pc->types;
+                    k > 0;
+                    k --, pwgtype ++)
+               {
+                 if (!ppdMarkOption(ppd, "MediaType", pwgtype->ppd))
+                 {
+                   val->collection = new_media_col(pwgsize, pwgsource->pwg,
+                                                   pwgtype->pwg);
+                   val ++;
+                 }
+               }
+             }
+             else if (!ppdConflicts(ppd))
+             {
+               val->collection = new_media_col(pwgsize, pwgsource->pwg, NULL);
+               val ++;
+             }
+           }
+         }
+         else if (p->pc->num_types > 0)
+         {
+           for (j = p->pc->num_types, pwgtype = p->pc->types;
+                j > 0;
+                j --, pwgtype ++)
+           {
+             if (!ppdMarkOption(ppd, "MediaType", pwgtype->ppd))
+             {
+               val->collection = new_media_col(pwgsize, NULL, pwgtype->pwg);
+               val ++;
+             }
+           }
+         }
        }
+
+       /*
+        * Update the number of media-col-database values...
+       */
+
+       attr->num_values = val - attr->values;
       }
     }
 
@@ -4167,28 +4450,193 @@ load_ppd(cupsd_printer_t *p)           /* I - Printer */
     * Output bin...
     */
 
-    if ((output_bin = ppdFindOption(ppd, "OutputBin")) != NULL)
+    if (p->pc && p->pc->num_bins > 0)
     {
       attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-                          "output-bin-supported", output_bin->num_choices,
+                          "output-bin-supported", p->pc->num_bins,
                           NULL, NULL);
 
       if (attr != NULL)
       {
        for (i = 0, val = attr->values;
-            i < output_bin->num_choices;
+            i < p->pc->num_bins;
             i ++, val ++)
-         val->string.text = _cupsStrAlloc(output_bin->choices[i].choice);
+         val->string.text = _cupsStrAlloc(p->pc->bins[i].pwg);
+      }
+
+      if ((output_bin = ppdFindOption(ppd, "OutputBin")) != NULL)
+      {
+       for (i = 0; i < p->pc->num_bins; i ++)
+         if (!strcmp(p->pc->bins[i].ppd, output_bin->defchoice))
+           break;
+
+        if (i >= p->pc->num_bins)
+         i = 0;
+
+       ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                    "output-bin-default", NULL, p->pc->bins[i].pwg);
+      }
+      else
+        ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                    "output-bin-default", NULL, p->pc->bins[0].pwg);
+    }
+    else if (((ppd_attr = ppdFindAttr(ppd, "DefaultOutputOrder",
+                                     NULL)) != NULL &&
+             !strcasecmp(ppd_attr->value, "Reverse")) ||
+            (!ppd_attr && ppd->manufacturer && /* "Compatibility heuristic" */
+             (!strcasecmp(ppd->manufacturer, "epson") ||
+              !strcasecmp(ppd->manufacturer, "lexmark"))))
+    {
+     /*
+      * Report that this printer has a single output bin that leaves pages face
+      * up.
+      */
+
+      ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                  "output-bin-supported", NULL, "face-up");
+      ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                  "output-bin-default", NULL, "face-up");
+    }
+    else
+    {
+      ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                  "output-bin-supported", NULL, "face-down");
+      ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                  "output-bin-default", NULL, "face-down");
+    }
+
+   /*
+    * output-mode and print-color-mode...
+    */
+
+    if (ppd->color_device)
+    {
+      static const char * const output_modes[] =
+      {
+        "monochrome",
+       "color"
+      };
+
+      ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                    "output-mode-supported", 2, NULL, output_modes);
+      ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                   "output-mode-default", NULL, "color");
+
+      ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                    "print-color-mode-supported", 2, NULL, output_modes);
+      ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                   "print-color-mode-default", NULL, "color");
+    }
+    else
+    {
+      ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                   "output-mode-supported", NULL, "monochrome");
+      ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                   "output-mode-default", NULL, "monochrome");
+
+      ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                   "print-color-mode-supported", NULL, "monochrome");
+      ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                   "print-color-mode-default", NULL, "monochrome");
+    }
+
+   /*
+    * Printer resolutions...
+    */
+
+    if ((resolution = ppdFindOption(ppd, "Resolution")) == NULL)
+      if ((resolution = ppdFindOption(ppd, "JCLResolution")) == NULL)
+        if ((resolution = ppdFindOption(ppd, "SetResolution")) == NULL)
+         resolution = ppdFindOption(ppd, "CNRes_PGP");
+
+    if (resolution)
+    {
+     /*
+      * Report all supported resolutions...
+      */
+
+      attr = ippAddResolutions(p->ppd_attrs, IPP_TAG_PRINTER,
+                               "printer-resolution-supported",
+                               resolution->num_choices, IPP_RES_PER_INCH,
+                              NULL, NULL);
+
+      for (i = 0, choice = resolution->choices;
+           i < resolution->num_choices;
+          i ++, choice ++)
+      {
+        xdpi = ydpi = (int)strtol(choice->choice, (char **)&resptr, 10);
+       if (resptr > choice->choice && xdpi > 0 && *resptr == 'x')
+         ydpi = (int)strtol(resptr + 1, (char **)&resptr, 10);
+
+       if (xdpi <= 0 || ydpi <= 0)
+       {
+         cupsdLogMessage(CUPSD_LOG_WARN,
+                         "Bad resolution \"%s\" for printer %s.",
+                         choice->choice, p->name);
+         xdpi = ydpi = 300;
+       }
+
+        attr->values[i].resolution.xres  = xdpi;
+        attr->values[i].resolution.yres  = ydpi;
+        attr->values[i].resolution.units = IPP_RES_PER_INCH;
+
+        if (choice->marked)
+         ippAddResolution(p->ppd_attrs, IPP_TAG_PRINTER,
+                          "printer-resolution-default", IPP_RES_PER_INCH,
+                          xdpi, ydpi);
+      }
+    }
+    else if ((ppd_attr = ppdFindAttr(ppd, "DefaultResolution", NULL)) != NULL &&
+             ppd_attr->value)
+    {
+     /*
+      * Just the DefaultResolution to report...
+      */
+
+      xdpi = ydpi = (int)strtol(ppd_attr->value, (char **)&resptr, 10);
+      if (resptr > ppd_attr->value && xdpi > 0)
+      {
+       if (*resptr == 'x')
+         ydpi = (int)strtol(resptr + 1, (char **)&resptr, 10);
+       else
+         ydpi = xdpi;
       }
 
-      attr = ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-                         "output-bin-default", NULL, output_bin->defchoice);
+      if (xdpi <= 0 || ydpi <= 0)
+      {
+       cupsdLogMessage(CUPSD_LOG_WARN,
+                       "Bad default resolution \"%s\" for printer %s.",
+                       ppd_attr->value, p->name);
+       xdpi = ydpi = 300;
+      }
+
+      ippAddResolution(p->ppd_attrs, IPP_TAG_PRINTER,
+                      "printer-resolution-default", IPP_RES_PER_INCH,
+                      xdpi, ydpi);
+      ippAddResolution(p->ppd_attrs, IPP_TAG_PRINTER,
+                      "printer-resolution-supported", IPP_RES_PER_INCH,
+                      xdpi, ydpi);
+    }
+    else
+    {
+     /*
+      * No resolutions in PPD - make one up...
+      */
+
+      ippAddResolution(p->ppd_attrs, IPP_TAG_PRINTER,
+                      "printer-resolution-default", IPP_RES_PER_INCH,
+                      300, 300);
+      ippAddResolution(p->ppd_attrs, IPP_TAG_PRINTER,
+                      "printer-resolution-supported", IPP_RES_PER_INCH,
+                      300, 300);
     }
 
    /*
     * Duplexing, etc...
     */
 
+    ppdMarkDefaults(ppd);
+
     if ((duplex = ppdFindOption(ppd, "Duplex")) == NULL)
       if ((duplex = ppdFindOption(ppd, "EFDuplex")) == NULL)
        if ((duplex = ppdFindOption(ppd, "EFDuplexing")) == NULL)
@@ -4213,6 +4661,13 @@ load_ppd(cupsd_printer_t *p)             /* I - Printer */
        ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
                     "sides-default", NULL, "one-sided");
     }
+    else
+    {
+      ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                  "sides-supported", NULL, "one-sided");
+      ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                  "sides-default", NULL, "one-sided");
+    }
 
     if (ppdFindOption(ppd, "Collate") != NULL)
       p->type |= CUPS_PRINTER_COLLATE;
@@ -4237,73 +4692,32 @@ load_ppd(cupsd_printer_t *p)            /* I - Printer */
       else
        p->type |= CUPS_PRINTER_SMALL;
 
-   /*
-    * Add a filter from application/vnd.cups-raw to printer/name to
-    * handle "raw" printing by users.
-    */
-
-    add_string_array(&(p->filters), "application/vnd.cups-raw 0 -");
-
-   /*
-    * Add any pre-filters in the PPD file...
-    */
-
-    if ((ppd_attr = ppdFindAttr(ppd, "cupsPreFilter", NULL)) != NULL)
+    if ((ppd_attr = ppdFindAttr(ppd, "APICADriver", NULL)) != NULL &&
+        ppd_attr->value && !strcasecmp(ppd_attr->value, "true"))
     {
-      for (; ppd_attr; ppd_attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL))
-       if (ppd_attr->value)
-         add_string_array(&(p->pre_filters), ppd_attr->value);
+      if ((ppd_attr = ppdFindAttr(ppd, "APScannerOnly", NULL)) != NULL &&
+         ppd_attr->value && !strcasecmp(ppd_attr->value, "true"))
+        p->type |= CUPS_PRINTER_SCANNER;
+      else
+        p->type |= CUPS_PRINTER_MFP;
     }
 
    /*
-    * Add any filters in the PPD file...
+    * Scan the filters in the PPD file...
     */
 
-    DEBUG_printf(("ppd->num_filters = %d\n", ppd->num_filters));
-    for (i = 0; i < ppd->num_filters; i ++)
-    {
-      DEBUG_printf(("ppd->filters[%d] = \"%s\"\n", i, ppd->filters[i]));
-      add_string_array(&(p->filters), ppd->filters[i]);
-
-      if (!strncasecmp(ppd->filters[i], "application/vnd.cups-command", 28) &&
-          isspace(ppd->filters[i][28] & 255))
-        p->type |= CUPS_PRINTER_COMMANDS;
-    }
-
-    if (ppd->num_filters == 0)
+    if (p->pc)
     {
-     /*
-      * If there are no filters, add PostScript printing filters.
-      */
-
-      add_string_array(&(p->filters),
-                       "application/vnd.cups-command 0 commandtops");
-      add_string_array(&(p->filters),
-                       "application/vnd.cups-postscript 0 -");
-
-      p->type |= CUPS_PRINTER_COMMANDS;
-    }
-    else if (!(p->type & CUPS_PRINTER_COMMANDS))
-    {
-     /*
-      * See if this is a PostScript device without a command filter...
-      */
-
-      for (i = 0; i < ppd->num_filters; i ++)
-       if (!strncasecmp(ppd->filters[i],
-                        "application/vnd.cups-postscript", 31) &&
-            isspace(ppd->filters[i][31] & 255))
-         break;
-
-      if (i < ppd->num_filters)
+      for (filter = (const char *)cupsArrayFirst(p->pc->filters);
+          filter;
+          filter = (const char *)cupsArrayNext(p->pc->filters))
       {
-       /*
-       * Add the generic PostScript command filter...
-       */
-
-       add_string_array(&(p->filters),
-                        "application/vnd.cups-command 0 commandtops");
-       p->type |= CUPS_PRINTER_COMMANDS;
+       if (!strncasecmp(filter, "application/vnd.cups-command", 28) &&
+           _cups_isspace(filter[28]))
+       {
+         p->type |= CUPS_PRINTER_COMMANDS;
+         break;
+       }
       }
     }
 
@@ -4314,13 +4728,11 @@ load_ppd(cupsd_printer_t *p)            /* I - Printer */
                *end;                   /* End of name */
       int      count;                  /* Number of commands */
 
-
-      if ((ppd_attr = ppdFindAttr(ppd, "cupsCommands", NULL)) != NULL &&
-         ppd_attr->value && ppd_attr->value[0])
+      if ((ppd_attr = ppdFindAttr(ppd, "cupsCommands", NULL)) != NULL)
       {
        for (count = 0, start = ppd_attr->value; *start; count ++)
        {
-         while (isspace(*start & 255))
+         while (_cups_isspace(*start))
            start ++;
 
          if (!*start)
@@ -4336,7 +4748,8 @@ load_ppd(cupsd_printer_t *p)              /* I - Printer */
       if (count > 0)
       {
        /*
-       * Make a copy of the commands string and count how many ...
+       * Make a copy of the commands string and count how many commands there
+       * are...
        */
 
        attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
@@ -4426,13 +4839,138 @@ load_ppd(cupsd_printer_t *p)           /* I - Printer */
        attr->values[i].string.text = _cupsStrAlloc("bcp");
     }
 
-#ifdef HAVE_DNSSD
-    cupsdSetString(&p->product, ppd->product);
-#endif /* HAVE_DNSSD */
-
     if (ppdFindAttr(ppd, "APRemoteQueueID", NULL))
       p->type |= CUPS_PRINTER_REMOTE;
 
+#ifdef HAVE_APPLICATIONSERVICES_H
+   /*
+    * Convert the file referenced in APPrinterIconPath to a 128x128 PNG
+    * and save it as cacheDir/printername.png
+    */
+
+    if ((ppd_attr = ppdFindAttr(ppd, "APPrinterIconPath", NULL)) != NULL &&
+        ppd_attr->value)
+    {
+      CGImageRef       imageRef = NULL;/* Current icon image */
+      CGImageRef       biggestIconRef = NULL;
+                                       /* Biggest icon image */
+      CGImageRef       closestTo128IconRef = NULL;
+                                       /* Icon image closest to and >= 128 */
+      CGImageSourceRef sourceRef;      /* The file's image source */
+      char             outPath[HTTP_MAX_URI];
+                                       /* The path to the PNG file */
+      CFURLRef         outUrl;         /* The URL made from the outPath */
+      CFURLRef         icnsFileUrl;    /* The URL of the original ICNS icon file */
+      CGImageDestinationRef destRef;   /* The image destination to write */
+      size_t           bytesPerRow;    /* The bytes per row used for resizing */
+      CGContextRef     context;        /* The CG context used for resizing */
+
+      snprintf(outPath, sizeof(outPath), "%s/%s.png", CacheDir, p->name);
+      outUrl      = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
+                                                            (UInt8 *)outPath,
+                                                           strlen(outPath),
+                                                           FALSE);
+      icnsFileUrl = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
+                                                           (UInt8 *)ppd_attr->value,
+                                                           strlen(ppd_attr->value),
+                                                           FALSE);
+      if (outUrl && icnsFileUrl)
+      {
+        sourceRef = CGImageSourceCreateWithURL(icnsFileUrl, NULL);
+        if (sourceRef)
+        {
+          for (i = 0; i < CGImageSourceGetCount(sourceRef); i ++)
+          {
+            imageRef = CGImageSourceCreateImageAtIndex(sourceRef, i, NULL);
+           if (!imageRef)
+             continue;
+
+            if (CGImageGetWidth(imageRef) == CGImageGetHeight(imageRef))
+            {
+             /*
+              * Loop through remembering the icon closest to 128 but >= 128
+              * and then remember the largest icon.
+              */
+
+              if (CGImageGetWidth(imageRef) >= 128 &&
+                 (!closestTo128IconRef ||
+                  CGImageGetWidth(imageRef) <
+                      CGImageGetWidth(closestTo128IconRef)))
+              {
+                CGImageRelease(closestTo128IconRef);
+                CGImageRetain(imageRef);
+                closestTo128IconRef = imageRef;
+              }
+
+              if (!biggestIconRef ||
+                 CGImageGetWidth(imageRef) > CGImageGetWidth(biggestIconRef))
+              {
+                CGImageRelease(biggestIconRef);
+                CGImageRetain(imageRef);
+                biggestIconRef = imageRef;
+              }
+           }
+
+           CGImageRelease(imageRef);
+          }
+
+          if (biggestIconRef)
+          {
+           /*
+            * If biggestIconRef is NULL, we found no icons. Otherwise we first
+            * want the closest to 128, but if none are larger than 128, we want
+            * the largest icon available.
+            */
+
+            imageRef = closestTo128IconRef ? closestTo128IconRef :
+                                             biggestIconRef;
+            CGImageRetain(imageRef);
+            CGImageRelease(biggestIconRef);
+            if (closestTo128IconRef)
+             CGImageRelease(closestTo128IconRef);
+            destRef = CGImageDestinationCreateWithURL(outUrl, kUTTypePNG, 1,
+                                                      NULL);
+            if (destRef)
+            {
+              if (CGImageGetWidth(imageRef) != 128)
+              {
+                bytesPerRow = CGImageGetBytesPerRow(imageRef) /
+                              CGImageGetWidth(imageRef) * 128;
+                context     = CGBitmapContextCreate(NULL, 128, 128,
+                                                   CGImageGetBitsPerComponent(imageRef),
+                                                   bytesPerRow,
+                                                   CGImageGetColorSpace(imageRef),
+                                                   kCGImageAlphaPremultipliedFirst);
+                if (context)
+                {
+                  CGContextDrawImage(context, CGRectMake(0, 0, 128, 128),
+                                    imageRef);
+                  CGImageRelease(imageRef);
+                  imageRef = CGBitmapContextCreateImage(context);
+                  CGContextRelease(context);
+                }
+              }
+
+              CGImageDestinationAddImage(destRef, imageRef, NULL);
+              CGImageDestinationFinalize(destRef);
+              CFRelease(destRef);
+            }
+
+            CGImageRelease(imageRef);
+          }
+
+          CFRelease(sourceRef);
+        }
+      }
+
+      if (outUrl)
+        CFRelease(outUrl);
+
+      if (icnsFileUrl)
+        CFRelease(icnsFileUrl);
+    }
+#endif /* HAVE_APPLICATIONSERVICES_H */
+
    /*
     * Close the PPD and set the type...
     */
@@ -4459,19 +4997,6 @@ load_ppd(cupsd_printer_t *p)             /* I - Printer */
     cupsdLogMessage(CUPSD_LOG_INFO,
                    "Hint: Run \"cupstestppd %s\" and fix any errors.",
                    ppd_name);
-
-   /*
-    * Add a filter from application/vnd.cups-raw to printer/name to
-    * handle "raw" printing by users.
-    */
-
-    add_string_array(&(p->filters), "application/vnd.cups-raw 0 -");
-
-   /*
-    * Add a PostScript filter, since this is still possibly PS printer.
-    */
-
-    add_string_array(&(p->filters), "application/vnd.cups-postscript 0 -");
   }
   else
   {
@@ -4493,10 +5018,6 @@ load_ppd(cupsd_printer_t *p)             /* I - Printer */
       ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
                   "printer-make-and-model", NULL,
                   "Local System V Printer");
-
-      snprintf(interface, sizeof(interface), "*/* 0 %s/interfaces/%s",
-              ServerRoot, p->name);
-      add_string_array(&(p->filters), interface);
     }
     else if (!strncmp(p->device_uri, "ipp://", 6) &&
             (strstr(p->device_uri, "/printers/") != NULL ||
@@ -4516,8 +5037,25 @@ load_ppd(cupsd_printer_t *p)             /* I - Printer */
       * remote printer...
       */
 
-      ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
-                  "printer-uri-supported", NULL, p->device_uri);
+      if (strchr(p->device_uri, '?'))
+      {
+       /*
+       * Strip trailing "?options" from URI...
+       */
+
+       char    resource[HTTP_MAX_URI], /* New URI */
+               *ptr;                   /* Pointer into URI */
+
+       strlcpy(resource, p->device_uri, sizeof(resource));
+       if ((ptr = strchr(resource, '?')) != NULL)
+         *ptr = '\0';
+
+       ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
+                    "printer-uri-supported", NULL, resource);
+      }
+      else
+       ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
+                    "printer-uri-supported", NULL, p->device_uri);
 
      /*
       * Then set the make-and-model accordingly...
@@ -4552,7 +5090,7 @@ load_ppd(cupsd_printer_t *p)              /* I - Printer */
   ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
                "finishings-default", IPP_FINISHINGS_NONE);
 
-  if (ppd && (cache = cupsFileOpen(cache_name, "w")) != NULL)
+  if (ppd && p->pc)
   {
    /*
     * Save cached PPD attributes to disk...
@@ -4560,30 +5098,64 @@ load_ppd(cupsd_printer_t *p)            /* I - Printer */
 
     cupsdLogMessage(CUPSD_LOG_DEBUG, "load_ppd: Saving %s...", cache_name);
 
-    p->ppd_attrs->state = IPP_IDLE;
-
-    if (ippWriteIO(cache, (ipp_iocb_t)cupsFileWrite, 1, NULL,
-                   p->ppd_attrs) != IPP_DATA)
-    {
-      cupsdLogMessage(CUPSD_LOG_ERROR,
-                      "Unable to save PPD cache file \"%s\" - %s", cache_name,
-                     strerror(errno));
-      unlink(cache_name);
-    }
-
-    cupsFileClose(cache);
+    _ppdCacheWriteFile(p->pc, cache_name, p->ppd_attrs);
   }
-  else if (cache_info.st_mtime)
+  else
   {
    /*
-    * Remove cache file...
+    * Remove cache files...
     */
 
-    unlink(cache_name);
+    if (cache_info.st_mtime)
+      unlink(cache_name);
   }
 }
 
 
+/*
+ * 'new_media_col()' - Create a media-col collection value.
+ */
+
+static ipp_t *                         /* O - Collection value */
+new_media_col(_pwg_size_t *size,       /* I - media-size/margin values */
+              const char  *source,     /* I - media-source value */
+              const char  *type)       /* I - media-type value */
+{
+  ipp_t        *media_col,                     /* Collection value */
+       *media_size;                    /* media-size value */
+
+
+  media_col = ippNew();
+
+  media_size = ippNew();
+  ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+               "x-dimension", size->width);
+  ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+               "y-dimension", size->length);
+  ippAddCollection(media_col, IPP_TAG_PRINTER, "media-size", media_size);
+  ippDelete(media_size);
+
+  ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+               "media-bottom-margin", size->bottom);
+  ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+               "media-left-margin", size->left);
+  ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+               "media-right-margin", size->right);
+  ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+               "media-top-margin", size->top);
+
+  if (source)
+    ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-source",
+                NULL, source);
+
+  if (type)
+    ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-type",
+                NULL, type);
+
+  return (media_col);
+}
+
+
 #ifdef __sgi
 /*
  * 'write_irix_config()' - Update the config files used by the IRIX