]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/printers.c
Merge changes from CUPS 1.4svn-r8628.
[thirdparty/cups.git] / scheduler / printers.c
index f15cef334a6e57c0d2dee409a2a975b9a054a988..c5008eec81cf455a4f621657c77488fd89b03d26 100644 (file)
@@ -3,7 +3,7 @@
  *
  *   Printer routines for the Common UNIX Printing System (CUPS).
  *
- *   Copyright 2007-2008 by Apple Inc.
+ *   Copyright 2007-2009 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.
- *   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
- *                                 printers.conf file.
- *   cupsdSetAuthInfoRequired()  - Set the required authentication info.
- *   cupsdSetDeviceURI()         - Set the device URI for a printer.
- *   cupsdSetPrinterAttr()       - Set a printer attribute.
- *   cupsdSetPrinterAttrs()      - Set printer attributes based upon the PPD
- *                                 file.
- *   cupsdSetPrinterReasons()    - Set/update the reasons strings.
- *   cupsdSetPrinterState()      - Update the current state of a printer.
- *   cupsdStopPrinter()          - Stop a printer from printing any jobs...
- *   cupsdUpdatePrinters()       - Update printers after a partial reload.
- *   cupsdValidateDest()         - Validate a printer/class destination.
- *   cupsdWritePrintcap()        - Write a pseudo-printcap file for older
- *                                 applications that need it...
- *   add_printer_defaults()      - Add name-default attributes to the printer
- *                                 attributes.
- *   add_printer_filter()        - Add a MIME filter for a printer.
- *   add_printer_formats()       - Add document-format-supported values for
- *                                 a printer.
- *   compare_printers()          - Compare two printers.
- *   delete_printer_filters()    - Delete all MIME filters for a printer.
- *   write_irix_config()         - Update the config files used by the IRIX
- *                                 desktop tools.
- *   write_irix_state()          - Update the status files used by IRIX
- *                                 printing desktop tools.
- *   write_xml_string()          - Write a string with XML escaping.
+ *   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
+ *                                printers.conf file.
+ *   cupsdSetAuthInfoRequired() - Set the required authentication info.
+ *   cupsdSetDeviceURI()        - Set the device URI for a printer.
+ *   cupsdSetPrinterAttr()      - Set a printer attribute.
+ *   cupsdSetPrinterAttrs()     - Set printer attributes based upon the PPD
+ *                                file.
+ *   cupsdSetPrinterReasons()   - Set/update the reasons strings.
+ *   cupsdSetPrinterState()     - Update the current state of a printer.
+ *   cupsdStopPrinter()         - Stop a printer from printing any jobs...
+ *   cupsdUpdatePrinterPPD()    - Update keywords in a printer's PPD file.
+ *   cupsdUpdatePrinters()      - Update printers after a partial reload.
+ *   cupsdValidateDest()        - Validate a printer/class destination.
+ *   cupsdWritePrintcap()       - Write a pseudo-printcap file for older
+ *                                applications that need it...
+ *   add_printer_defaults()     - Add name-default attributes to the printer
+ *                                attributes.
+ *   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.
+ *   load_ppd()                 - Load a cached PPD file, updating the cache as
+ *                                needed.
+ *   write_irix_config()        - Update the config files used by the IRIX
+ *                                desktop tools.
+ *   write_irix_state()         - Update the status files used by IRIX printing
+ *                                desktop tools.
+ *   write_xml_string()         - Write a string with XML escaping.
  */
 
 /*
@@ -58,6 +64,7 @@
 
 #include "cupsd.h"
 #include <cups/dir.h>
+#include <cups/pwgmedia.h>
 
 
 /*
@@ -111,8 +118,9 @@ 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);
-  cupsdSetDeviceURI(p, "file:/dev/null");
+  cupsdSetStringf(&p->uri, "ipp://%s:%d/printers/%s", ServerName, RemotePort,
+                  name);
+  cupsdSetDeviceURI(p, "file:///dev/null");
 
   p->state      = IPP_PRINTER_STOPPED;
   p->state_time = time(NULL);
@@ -204,8 +212,7 @@ cupsdAddPrinterHistory(
 #endif /* __APPLE__ */
   if (p->num_reasons == 0)
     ippAddString(history, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-                 "printer-state-reasons", NULL,
-                p->state == IPP_PRINTER_STOPPED ? "paused" : "none");
+                 "printer-state-reasons", NULL, "none");
   else
     ippAddStrings(history, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
                   "printer-state-reasons", p->num_reasons, NULL,
@@ -289,7 +296,9 @@ cupsdCreateCommonData(void)
   static const char * const versions[] =/* ipp-versions-supported values */
                {
                  "1.0",
-                 "1.1"
+                 "1.1",
+                 "2.0",
+                 "2.1"
                };
   static const int     ops[] =         /* operations-supported values */
                {
@@ -306,7 +315,9 @@ cupsdCreateCommonData(void)
                  IPP_PAUSE_PRINTER,
                  IPP_RESUME_PRINTER,
                  IPP_PURGE_JOBS,
+                 IPP_SET_PRINTER_ATTRIBUTES,
                  IPP_SET_JOB_ATTRIBUTES,
+                 IPP_GET_PRINTER_SUPPORTED_VALUES,
                  IPP_CREATE_PRINTER_SUBSCRIPTION,
                  IPP_CREATE_JOB_SUBSCRIPTION,
                  IPP_GET_SUBSCRIPTION_ATTRIBUTES,
@@ -348,18 +359,18 @@ cupsdCreateCommonData(void)
                  ,"gzip"
 #endif /* HAVE_LIBZ */
                };
+  static const char * const media_col_supported[] =
+               {                       /* media-col-supported values */
+                 "media-color",
+                 "media-key",
+                 "media-size",
+                 "media-type"
+               };
   static const char * const multiple_document_handling[] =
                {                       /* multiple-document-handling-supported values */
                  "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",
@@ -405,6 +416,11 @@ cupsdCreateCommonData(void)
                  "printer-resolution",
                  "sides"
                };
+  static const char * const printer_settable[] =
+               {                       /* printer-settable-attributes-supported */
+                 "printer-info",
+                 "printer-location"
+               };
 
 
   if (CommonData)
@@ -501,6 +517,13 @@ cupsdCreateCommonData(void)
     ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
                  "job-sheets-supported", NULL, "none");
 
+  /* media-col-supported */
+  ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
+                "media-col-supported",
+                sizeof(media_col_supported) /
+                   sizeof(media_col_supported[0]), NULL,
+               media_col_supported);
+
   /* multiple-document-handling-supported */
   ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
                 "multiple-document-handling-supported",
@@ -514,7 +537,7 @@ cupsdCreateCommonData(void)
 
   /* multiple-operation-time-out */
   ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
-                "multiple-operation-time-out", 60);
+                "multiple-operation-time-out", MultipleOperationTimeout);
 
   /* natural-language-configured */
   ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE | IPP_TAG_COPY,
@@ -592,11 +615,6 @@ cupsdCreateCommonData(void)
   ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
                "pdl-override-supported", NULL, "not-attempted");
 
-  /* 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);
-
   /* printer-op-policy-supported */
   attr = ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
                        "printer-op-policy-supported", cupsArrayCount(Policies),
@@ -606,6 +624,12 @@ cupsdCreateCommonData(void)
        i ++, p = (cupsd_policy_t *)cupsArrayNext(Policies))
     attr->values[i].string.text = p->name;
 
+  /* printer-settable-attributes-supported */
+  ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
+                "printer-settable-attributes-supported",
+               sizeof(printer_settable) / sizeof(printer_settable[0]),
+               NULL, printer_settable);
+
   /* server-is-sharing-printers */
   ippAddBoolean(CommonData, IPP_TAG_PRINTER, "server-is-sharing-printers",
                 BrowseLocalProtocols != 0 && Browsing);
@@ -657,7 +681,12 @@ cupsdDeletePrinter(
   * Stop printing on this printer...
   */
 
-  cupsdStopPrinter(p, update);
+  cupsdSetPrinterState(p, IPP_PRINTER_STOPPED, update);
+
+  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...
@@ -768,14 +797,14 @@ cupsdDeletePrinter(
     free(p->history);
   }
 
+  delete_printer_filters(p);
+
   for (i = 0; i < p->num_reasons; i ++)
     _cupsStrFree(p->reasons[i]);
 
   ippDelete(p->attrs);
   ippDelete(p->ppd_attrs);
 
-  delete_printer_filters(p);
-
   mimeDeleteType(MimeDatabase, p->filetype);
   mimeDeleteType(MimeDatabase, p->prefiltertype);
 
@@ -891,9 +920,10 @@ cupsdFreePrinterUsers(
 void
 cupsdLoadAllPrinters(void)
 {
+  int                  i;              /* Looping var */
   cups_file_t          *fp;            /* printers.conf file */
   int                  linenum;        /* Current line number */
-  char                 line[1024],     /* Line from file */
+  char                 line[4096],     /* Line from file */
                        *value,         /* Pointer to value */
                        *valueptr;      /* Pointer into value */
   cupsd_printer_t      *p;             /* Current printer */
@@ -1064,10 +1094,21 @@ cupsdLoadAllPrinters(void)
     else if (!strcasecmp(line, "Reason"))
     {
       if (value &&
-          p->num_reasons < (int)(sizeof(p->reasons) / sizeof(p->reasons[0])))
+          strcmp(value, "com.apple.print.recoverable-warning") &&
+          strcmp(value, "connecting-to-device") &&
+          strcmp(value, "cups-insecure-filter-warning") &&
+          strcmp(value, "cups-missing-filter-warning"))
       {
-        p->reasons[p->num_reasons] = _cupsStrAlloc(value);
-       p->num_reasons ++;
+        for (i = 0 ; i < p->num_reasons; i ++)
+         if (!strcmp(value, 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(value);
+         p->num_reasons ++;
+       }
       }
       else
        cupsdLogMessage(CUPSD_LOG_ERROR,
@@ -1082,7 +1123,10 @@ cupsdLoadAllPrinters(void)
       if (value && !strcasecmp(value, "idle"))
         p->state = IPP_PRINTER_IDLE;
       else if (value && !strcasecmp(value, "stopped"))
+      {
         p->state = IPP_PRINTER_STOPPED;
+       cupsdSetPrinterReasons(p, "+paused");
+      }
       else
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
@@ -1312,10 +1356,10 @@ cupsdLoadAllPrinters(void)
         if (!p->attrs)
          cupsdSetPrinterAttrs(p);
 
-        cupsdSetPrinterAttr(p, value, valueptr);
-
-       if (!strncmp(value, "marker-", 7))
-         p->marker_time = time(NULL);
+        if (!strcmp(value, "marker-change-time"))
+         p->marker_time = atoi(valueptr);
+       else
+          cupsdSetPrinterAttr(p, value, valueptr);
       }
     }
     else
@@ -1544,7 +1588,10 @@ cupsdSaveAllPrinters(void)
     cupsFilePrintf(fp, "StateTime %d\n", (int)printer->state_time);
 
     for (i = 0; i < printer->num_reasons; i ++)
-      if (strcmp(printer->reasons[i], "connecting-to-device"))
+      if (strcmp(printer->reasons[i], "com.apple.print.recoverable-warning") &&
+          strcmp(printer->reasons[i], "connecting-to-device") &&
+          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);
@@ -1630,6 +1677,26 @@ cupsdSaveAllPrinters(void)
       cupsFilePuts(fp, "\n");
     }
 
+    if ((marker = ippFindAttribute(printer->attrs, "marker-low-levels",
+                                   IPP_TAG_INTEGER)) != NULL)
+    {
+      cupsFilePrintf(fp, "Attribute %s %d", marker->name,
+                     marker->values[0].integer);
+      for (i = 1; i < marker->num_values; i ++)
+        cupsFilePrintf(fp, ",%d", marker->values[i].integer);
+      cupsFilePuts(fp, "\n");
+    }
+
+    if ((marker = ippFindAttribute(printer->attrs, "marker-high-levels",
+                                   IPP_TAG_INTEGER)) != NULL)
+    {
+      cupsFilePrintf(fp, "Attribute %s %d", marker->name,
+                     marker->values[0].integer);
+      for (i = 1; i < marker->num_values; i ++)
+        cupsFilePrintf(fp, ",%d", marker->values[i].integer);
+      cupsFilePuts(fp, "\n");
+    }
+
     if ((marker = ippFindAttribute(printer->attrs, "marker-message",
                                    IPP_TAG_TEXT)) != NULL)
     {
@@ -1681,6 +1748,10 @@ cupsdSaveAllPrinters(void)
       cupsFilePutConf(fp, "Attribute", value);
     }
 
+    if (printer->marker_time)
+      cupsFilePrintf(fp, "Attribute marker-change-time %ld\n",
+                     (long)printer->marker_time);
+
     cupsFilePuts(fp, "</Printer>\n");
 
 #ifdef __sgi
@@ -1938,7 +2009,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;
@@ -1956,7 +2027,8 @@ cupsdSetPrinterAttr(
   * Then add or update the attribute as needed...
   */
 
-  if (!strcmp(name, "marker-levels"))
+  if (!strcmp(name, "marker-levels") || !strcmp(name, "marker-low-levels") ||
+      !strcmp(name, "marker-high-levels"))
   {
    /*
     * Integer values...
@@ -2044,8 +2116,6 @@ cupsdSetPrinterAttr(
         value = ptr;
     }
   }
-
-  cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
 }
 
 
@@ -2067,21 +2137,15 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
   ipp_attribute_t *attr;               /* Attribute data */
   cups_option_t        *option;                /* Current printer option */
   char         *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,
@@ -2112,65 +2176,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...
@@ -2294,7 +2343,7 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
        for (i = 0; i < p->num_printers; i ++)
        {
           if (attr != NULL)
-            attr->values[i].string.text = _cupsStrAlloc(p->printers[i]->name);
+            attr->values[i].string.text = _cupsStrRetain(p->printers[i]->name);
 
          p->type &= ~CUPS_PRINTER_OPTIONS | p->printers[i]->type;
         }
@@ -2319,6 +2368,9 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
       * Add filters for printer...
       */
 
+      cupsdSetPrinterReasons(p, "-cups-missing-filter-warning,"
+                                "cups-insecure-filter-warning");
+
       for (filter = (char *)cupsArrayFirst(p->filters);
           filter;
           filter = (char *)cupsArrayNext(p->filters))
@@ -2354,7 +2406,7 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
       {
        for (i = 0; i < oldattr->num_values; i ++)
          attr->values[i].string.text =
-             _cupsStrAlloc(oldattr->values[i].string.text);
+             _cupsStrRetain(oldattr->values[i].string.text);
       }
     }
 
@@ -2370,6 +2422,35 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
       }
     }
 
+    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 = 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].integer = oldattr->values[i].integer;
+      }
+    }
+
+    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)
     {
@@ -2379,7 +2460,7 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
       {
        for (i = 0; i < oldattr->num_values; i ++)
          attr->values[i].string.text =
-             _cupsStrAlloc(oldattr->values[i].string.text);
+             _cupsStrRetain(oldattr->values[i].string.text);
       }
     }
 
@@ -2392,7 +2473,7 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
       {
        for (i = 0; i < oldattr->num_values; i ++)
          attr->values[i].string.text =
-             _cupsStrAlloc(oldattr->values[i].string.text);
+             _cupsStrRetain(oldattr->values[i].string.text);
       }
     }
 
@@ -2448,14 +2529,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...
     */
@@ -2501,21 +2574,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';
     }
   }
 
@@ -2555,17 +2613,21 @@ 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 */
 {
-  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 */
 
 
+  cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                 "cupsdSetPrinterReasons(p=%p(%s),s=\"%s\"", p, p->name, s);
+
   if (s[0] == '-' || s[0] == '+')
   {
    /*
@@ -2586,6 +2648,7 @@ cupsdSetPrinterReasons(
       _cupsStrFree(p->reasons[i]);
 
     p->num_reasons = 0;
+    changed        = 1;
 
     cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
 
@@ -2594,7 +2657,7 @@ cupsdSetPrinterReasons(
   }
 
   if (!strcmp(s, "none"))
-    return;
+    return (changed);
 
  /*
   * Loop through all of the reasons...
@@ -2625,29 +2688,31 @@ cupsdSetPrinterReasons(
       */
 
       for (i = 0; i < p->num_reasons; i ++)
-        if (!strcasecmp(reason, p->reasons[i]))
+        if (!strcmp(reason, p->reasons[i]))
        {
         /*
          * Found a match, so remove it...
          */
 
          p->num_reasons --;
+          changed = 1;
          _cupsStrFree(p->reasons[i]);
 
          if (i < p->num_reasons)
            memmove(p->reasons + i, p->reasons + i + 1,
                    (p->num_reasons - i) * sizeof(char *));
 
-         i --;
-
           if (!strcmp(reason, "paused") && p->state == IPP_PRINTER_STOPPED)
            cupsdSetPrinterState(p, IPP_PRINTER_IDLE, 1);
 
           if (strcmp(reason, "connecting-to-device"))
+         {
            cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
 
-         if (PrintcapFormat == PRINTCAP_PLIST)
-           cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
+           if (PrintcapFormat == PRINTCAP_PLIST)
+             cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
+          }
+         break;
        }
     }
     else if (p->num_reasons < (int)(sizeof(p->reasons) / sizeof(p->reasons[0])))
@@ -2657,25 +2722,38 @@ cupsdSetPrinterReasons(
       */
 
       for (i = 0; i < p->num_reasons; i ++)
-        if (!strcasecmp(reason, p->reasons[i]))
+        if (!strcmp(reason, p->reasons[i]))
          break;
 
       if (i >= p->num_reasons)
       {
+        if (i >= (int)(sizeof(p->reasons) / sizeof(p->reasons[0])))
+       {
+         cupsdLogMessage(CUPSD_LOG_ALERT,
+                         "Too many printer-state-reasons values for %s (%d)",
+                         p->name, i + 1);
+          return (changed);
+        }
+
         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);
+         if (PrintcapFormat == PRINTCAP_PLIST)
+           cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
+       }
       }
     }
   }
+
+  return (changed);
 }
 
 
@@ -2730,6 +2808,26 @@ cupsdSetPrinterState(
 #endif /* __sgi */
   }
 
+ /*
+  * Set/clear the paused reason as needed...
+  */
+
+  if (s == IPP_PRINTER_STOPPED)
+    cupsdSetPrinterReasons(p, "+paused");
+  else
+    cupsdSetPrinterReasons(p, "-paused");
+
+ /*
+  * Clear the message for the queue when going to processing...
+  */
+
+  if (s == IPP_PRINTER_PROCESSING)
+    p->state_message[0] = '\0';
+
+ /*
+  * Update the printer history...
+  */
+
   cupsdAddPrinterHistory(p);
 
  /*
@@ -2744,8 +2842,8 @@ cupsdSetPrinterState(
   * to stopped (or visa-versa)...
   */
 
-  if ((old_state == IPP_PRINTER_STOPPED) != (s == IPP_PRINTER_STOPPED) &&
-      update)
+  if (update &&
+      (old_state == IPP_PRINTER_STOPPED) != (s == IPP_PRINTER_STOPPED))
   {
     if (p->type & CUPS_PRINTER_CLASS)
       cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
@@ -2763,9 +2861,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...
   */
@@ -2776,33 +2871,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.");
 }
 
 
@@ -3177,140 +3248,137 @@ cupsdWritePrintcap(void)
                        "# %s/printers.conf file.  All changes to this file\n"
                       "# will be lost.\n", ServerRoot);
 
-  if (Printers)
+ /*
+  * Write a new printcap with the current list of printers.
+  */
+
+  switch (PrintcapFormat)
   {
-   /*
-    * Write a new printcap with the current list of printers.
-    */
+    case PRINTCAP_BSD :
+       /*
+       * Each printer is put in the file as:
+       *
+       *    Printer1:
+       *    Printer2:
+       *    Printer3:
+       *    ...
+       *    PrinterN:
+       */
 
-    switch (PrintcapFormat)
-    {
-      case PRINTCAP_BSD :
-        /*
-          * Each printer is put in the file as:
-         *
-         *    Printer1:
-         *    Printer2:
-         *    Printer3:
-         *    ...
-         *    PrinterN:
-         */
+       if (DefaultPrinter)
+         cupsFilePrintf(fp, "%s|%s:rm=%s:rp=%s:\n", DefaultPrinter->name,
+                        DefaultPrinter->info, ServerName,
+                        DefaultPrinter->name);
+
+       for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
+            p;
+            p = (cupsd_printer_t *)cupsArrayNext(Printers))
+         if (p != DefaultPrinter)
+           cupsFilePrintf(fp, "%s|%s:rm=%s:rp=%s:\n", p->name, p->info,
+                          ServerName, p->name);
+       break;
 
-          if (DefaultPrinter)
-           cupsFilePrintf(fp, "%s|%s:rm=%s:rp=%s:\n", DefaultPrinter->name,
-                          DefaultPrinter->info, ServerName,
-                          DefaultPrinter->name);
-
-         for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
-              p;
-              p = (cupsd_printer_t *)cupsArrayNext(Printers))
-           if (p != DefaultPrinter)
-             cupsFilePrintf(fp, "%s|%s:rm=%s:rp=%s:\n", p->name, p->info,
-                            ServerName, p->name);
-          break;
-
-      case PRINTCAP_PLIST :
-         /*
-         * Each printer is written as a dictionary in a plist file.
-         * Currently the printer-name, printer-info, printer-is-accepting-jobs,
-         * printer-location, printer-make-and-model, printer-state,
-         * printer-state-reasons, printer-type, and (sanitized) device-uri.
-         */
+    case PRINTCAP_PLIST :
+       /*
+       * Each printer is written as a dictionary in a plist file.
+       * Currently the printer-name, printer-info, printer-is-accepting-jobs,
+       * printer-location, printer-make-and-model, printer-state,
+       * printer-state-reasons, printer-type, and (sanitized) device-uri.
+       */
 
-          cupsFilePuts(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-                          "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD "
-                          "PLIST 1.0//EN\" \"http://www.apple.com/DTDs/"
-                          "PropertyList-1.0.dtd\">\n"
-                          "<plist version=\"1.0\">\n"
-                          "<array>\n");
-
-         for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
-              p;
-              p = (cupsd_printer_t *)cupsArrayNext(Printers))
-          {
-           cupsFilePuts(fp, "\t<dict>\n"
-                            "\t\t<key>printer-name</key>\n"
-                            "\t\t<string>");
-            write_xml_string(fp, p->name);
-           cupsFilePuts(fp, "</string>\n"
-                            "\t\t<key>printer-info</key>\n"
-                            "\t\t<string>");
-            write_xml_string(fp, p->info);
-           cupsFilePrintf(fp, "</string>\n"
-                              "\t\t<key>printer-is-accepting-jobs</key>\n"
-                              "\t\t<%s/>\n"
-                              "\t\t<key>printer-location</key>\n"
-                              "\t\t<string>", p->accepting ? "true" : "false");
-            write_xml_string(fp, p->location);
-           cupsFilePuts(fp, "</string>\n"
-                            "\t\t<key>printer-make-and-model</key>\n"
-                            "\t\t<string>");
-            write_xml_string(fp, p->make_model);
-           cupsFilePrintf(fp, "</string>\n"
-                              "\t\t<key>printer-state</key>\n"
-                              "\t\t<integer>%d</integer>\n"
-                              "\t\t<key>printer-state-reasons</key>\n"
-                              "\t\t<array>\n", p->state);
-            for (i = 0; i < p->num_reasons; i ++)
-           {
-             cupsFilePuts(fp, "\t\t\t<string>");
-             write_xml_string(fp, p->reasons[i]);
-             cupsFilePuts(fp, "</string>\n");
-           }
-           cupsFilePrintf(fp, "\t\t</array>\n"
-                              "\t\t<key>printer-type</key>\n"
-                              "\t\t<integer>%d</integer>\n"
-                              "\t\t<key>device-uri</key>\n"
-                              "\t\t<string>", p->type);
-            write_xml_string(fp, p->sanitized_device_uri);
-           cupsFilePuts(fp, "</string>\n"
-                            "\t</dict>\n");
-          }
-         cupsFilePuts(fp, "</array>\n"
-                          "</plist>\n");
-         break;
+       cupsFilePuts(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+                        "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD "
+                        "PLIST 1.0//EN\" \"http://www.apple.com/DTDs/"
+                        "PropertyList-1.0.dtd\">\n"
+                        "<plist version=\"1.0\">\n"
+                        "<array>\n");
 
-      case PRINTCAP_SOLARIS :
-        /*
-          * Each printer is put in the file as:
-         *
-         *    _all:all=Printer1,Printer2,Printer3,...,PrinterN
-         *    _default:use=DefaultPrinter
-         *    Printer1:\
-         *            :bsdaddr=ServerName,Printer1:\
-         *            :description=Description:
-         *    Printer2:
-         *            :bsdaddr=ServerName,Printer2:\
-         *            :description=Description:
-         *    Printer3:
-         *            :bsdaddr=ServerName,Printer3:\
-         *            :description=Description:
-         *    ...
-         *    PrinterN:
-         *            :bsdaddr=ServerName,PrinterN:\
-         *            :description=Description:
-         */
+       for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
+            p;
+            p = (cupsd_printer_t *)cupsArrayNext(Printers))
+       {
+         cupsFilePuts(fp, "\t<dict>\n"
+                          "\t\t<key>printer-name</key>\n"
+                          "\t\t<string>");
+         write_xml_string(fp, p->name);
+         cupsFilePuts(fp, "</string>\n"
+                          "\t\t<key>printer-info</key>\n"
+                          "\t\t<string>");
+         write_xml_string(fp, p->info);
+         cupsFilePrintf(fp, "</string>\n"
+                            "\t\t<key>printer-is-accepting-jobs</key>\n"
+                            "\t\t<%s/>\n"
+                            "\t\t<key>printer-location</key>\n"
+                            "\t\t<string>", p->accepting ? "true" : "false");
+         write_xml_string(fp, p->location);
+         cupsFilePuts(fp, "</string>\n"
+                          "\t\t<key>printer-make-and-model</key>\n"
+                          "\t\t<string>");
+         write_xml_string(fp, p->make_model);
+         cupsFilePrintf(fp, "</string>\n"
+                            "\t\t<key>printer-state</key>\n"
+                            "\t\t<integer>%d</integer>\n"
+                            "\t\t<key>printer-state-reasons</key>\n"
+                            "\t\t<array>\n", p->state);
+         for (i = 0; i < p->num_reasons; i ++)
+         {
+           cupsFilePuts(fp, "\t\t\t<string>");
+           write_xml_string(fp, p->reasons[i]);
+           cupsFilePuts(fp, "</string>\n");
+         }
+         cupsFilePrintf(fp, "\t\t</array>\n"
+                            "\t\t<key>printer-type</key>\n"
+                            "\t\t<integer>%d</integer>\n"
+                            "\t\t<key>device-uri</key>\n"
+                            "\t\t<string>", p->type);
+         write_xml_string(fp, p->sanitized_device_uri);
+         cupsFilePuts(fp, "</string>\n"
+                          "\t</dict>\n");
+       }
+       cupsFilePuts(fp, "</array>\n"
+                        "</plist>\n");
+       break;
 
-          cupsFilePuts(fp, "_all:all=");
-         for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
-              p;
-              p = (cupsd_printer_t *)cupsArrayCurrent(Printers))
-           cupsFilePrintf(fp, "%s%c", p->name,
-                          cupsArrayNext(Printers) ? ',' : '\n');
-
-          if (DefaultPrinter)
-           cupsFilePrintf(fp, "_default:use=%s\n", DefaultPrinter->name);
-
-         for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
-              p;
-              p = (cupsd_printer_t *)cupsArrayNext(Printers))
-           cupsFilePrintf(fp, "%s:\\\n"
-                              "\t:bsdaddr=%s,%s:\\\n"
-                              "\t:description=%s:\n",
-                          p->name, ServerName, p->name,
-                          p->info ? p->info : "");
-          break;
-    }
+    case PRINTCAP_SOLARIS :
+       /*
+       * Each printer is put in the file as:
+       *
+       *    _all:all=Printer1,Printer2,Printer3,...,PrinterN
+       *    _default:use=DefaultPrinter
+       *    Printer1:\
+       *            :bsdaddr=ServerName,Printer1:\
+       *            :description=Description:
+       *    Printer2:
+       *            :bsdaddr=ServerName,Printer2:\
+       *            :description=Description:
+       *    Printer3:
+       *            :bsdaddr=ServerName,Printer3:\
+       *            :description=Description:
+       *    ...
+       *    PrinterN:
+       *            :bsdaddr=ServerName,PrinterN:\
+       *            :description=Description:
+       */
+
+       cupsFilePuts(fp, "_all:all=");
+       for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
+            p;
+            p = (cupsd_printer_t *)cupsArrayCurrent(Printers))
+         cupsFilePrintf(fp, "%s%c", p->name,
+                        cupsArrayNext(Printers) ? ',' : '\n');
+
+       if (DefaultPrinter)
+         cupsFilePrintf(fp, "_default:use=%s\n", DefaultPrinter->name);
+
+       for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
+            p;
+            p = (cupsd_printer_t *)cupsArrayNext(Printers))
+         cupsFilePrintf(fp, "%s:\\\n"
+                            "\t:bsdaddr=%s,%s:\\\n"
+                            "\t:description=%s:\n",
+                        p->name, ServerName, p->name,
+                        p->info ? p->info : "");
+       break;
   }
 
  /*
@@ -3436,7 +3504,9 @@ add_printer_filter(
                program[1024];          /* Program/filter name */
   int          cost;                   /* Cost of filter */
   mime_type_t  *temptype;              /* MIME type looping var */
-  char         filename[1024];         /* Full filter filename */
+  char         filename[1024],         /* Full filter filename */
+               *dirsep;                /* Pointer to directory separator */
+  struct stat  fileinfo;               /* File information */
 
 
  /*
@@ -3465,26 +3535,74 @@ add_printer_filter(
     else
       snprintf(filename, sizeof(filename), "%s/filter/%s", ServerBin, program);
 
-    if (access(filename, X_OK))
+    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",
-              program, p->name, strerror(errno));
-      cupsdSetPrinterReasons(p, "+cups-missing-filter-error");
-      cupsdSetPrinterState(p, IPP_PRINTER_STOPPED, 0);
+              filename, p->name, strerror(errno));
+      cupsdSetPrinterReasons(p, "+cups-missing-filter-warning");
 
       cupsdLogMessage(CUPSD_LOG_ERROR, "%s", p->state_message);
     }
-  }
 
- /*
-  * Mark the CUPS_PRINTER_COMMANDS bit if we have a filter for
-  * application/vnd.cups-command...
-  */
+   /*
+    * When running as root, do additional security checks...
+    */
+
+    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_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);
+
+       cupsdSetPrinterReasons(p, "+cups-insecure-filter-warning");
 
-  if (!strcasecmp(super, "application") &&
-      !strcasecmp(type, "vnd.cups-command"))
-    p->type |= CUPS_PRINTER_COMMANDS;
+       cupsdLogMessage(CUPSD_LOG_ERROR, "%s", p->state_message);
+      }
+      else if (fileinfo.st_mode)
+      {
+       /*
+       * 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) &&
+           (fileinfo.st_uid ||
+            (fileinfo.st_mode & (S_ISUID | 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
+           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);
+
+         cupsdSetPrinterReasons(p, "+cups-insecure-filter-warning");
+
+         cupsdLogMessage(CUPSD_LOG_ERROR, "%s", p->state_message);
+       }
+      }
+    }
+  }
 
  /*
   * Add the filter to the MIME database, supporting wildcards as needed...
@@ -3712,6 +3830,9 @@ delete_printer_filters(
 
       mimeDeleteFilter(MimeDatabase, filter);
     }
+
+  cupsdSetPrinterReasons(p, "-cups-insecure-filter-warning"
+                            ",cups-missing-filter-warning");
 }
 
 
@@ -3750,14 +3871,16 @@ load_ppd(cupsd_printer_t *p)            /* I - Printer */
   char         ppd_name[1024];         /* PPD filename */
   struct stat  ppd_info;               /* PPD file info */
   int          num_media;              /* Number of media options */
-  ppd_option_t *input_slot,            /* InputSlot options */
-               *media_type,            /* MediaType options */
-               *page_size,             /* PageSize options */
-               *output_bin,            /* OutputBin options */
-               *media_quality,         /* EFMediaQualityMode 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_attr_t   *ppd_attr;              /* PPD attribute */
+  _cups_pwg_media_t *pwgmedia;         /* Matching PWG size name */
   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 */
@@ -3771,8 +3894,7 @@ load_ppd(cupsd_printer_t *p)              /* I - Printer */
                {                       /* Standard CUPS commands */
                  "AutoConfigure",
                  "Clean",
-                 "PrintSelfTestPage",
-                 "ReportLevels"
+                 "PrintSelfTestPage"
                };
 
 
@@ -3814,6 +3936,8 @@ load_ppd(cupsd_printer_t *p)              /* I - Printer */
   * Reload PPD attributes from disk...
   */
 
+  cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
+
   cupsdLogMessage(CUPSD_LOG_DEBUG, "load_ppd: Loading %s...", ppd_name);
 
   delete_string_array(&(p->filters));
@@ -3831,6 +3955,8 @@ load_ppd(cupsd_printer_t *p)              /* I - Printer */
     * Add make/model and other various attributes...
     */
 
+    ppdMarkDefaults(ppd);
+
     if (ppd->color_device)
       p->type |= CUPS_PRINTER_COLOR;
     if (ppd->variable_sizes)
@@ -3876,66 +4002,112 @@ load_ppd(cupsd_printer_t *p)           /* I - Printer */
     * Add media options from the PPD file...
     */
 
-    if ((input_slot = ppdFindOption(ppd, "InputSlot")) != NULL)
-      num_media = input_slot->num_choices;
-    else
-      num_media = 0;
-
-    if ((media_type = ppdFindOption(ppd, "MediaType")) != NULL)
-      num_media += media_type->num_choices;
-
-    if ((page_size = ppdFindOption(ppd, "PageSize")) != NULL)
-      num_media += page_size->num_choices;
-
-    if ((media_quality = ppdFindOption(ppd, "EFMediaQualityMode")) != NULL)
-      num_media += media_quality->num_choices;
-
-    if (num_media == 0)
+    if (ppd->num_sizes == 0)
     {
-      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-supported", NULL, "unknown");
     }
     else
     {
+      num_media = ppd->num_sizes;
+      if (ppd->variable_sizes)
+       num_media ++;
+
       attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
                           "media-supported", num_media, NULL, NULL);
       if (attr != NULL)
       {
        val = attr->values;
 
-       if (input_slot != NULL)
-         for (i = 0; i < input_slot->num_choices; i ++, val ++)
-           val->string.text = _cupsStrAlloc(input_slot->choices[i].choice);
+        for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++)
+       {
+         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);
+           }
 
-       if (media_type != NULL)
-         for (i = 0; i < media_type->num_choices; i ++, val ++)
-           val->string.text = _cupsStrAlloc(media_type->choices[i].choice);
+           if (size->marked)
+           {
+            /*
+             * Add media-default...
+             */
+
+             ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                          "media-default", NULL, val->string.text);
+
+             /*
+             * 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);
+           }
 
-       if (media_quality != NULL)
-         for (i = 0; i < media_quality->num_choices; i ++, val ++)
-           val->string.text = _cupsStrAlloc(media_quality->choices[i].choice);
+            val ++;
+         }
+       }
 
-       if (page_size != NULL)
+        if (ppd->variable_sizes)
        {
-         for (i = 0; i < page_size->num_choices; i ++, val ++)
-           val->string.text = _cupsStrAlloc(page_size->choices[i].choice);
-
-         ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-                      "media-default", NULL, page_size->defchoice);
+         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);
+         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);
        }
-       else if (input_slot != NULL)
-         ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-                      "media-default", NULL, input_slot->defchoice);
-       else if (media_type != NULL)
-         ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-                      "media-default", NULL, media_type->defchoice);
-       else if (media_quality != NULL)
-         ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-                      "media-default", NULL, media_quality->defchoice);
-       else
-         ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-                      "media-default", NULL, "none");
       }
     }
 
@@ -3956,6 +4128,9 @@ load_ppd(cupsd_printer_t *p)              /* I - Printer */
             i ++, val ++)
          val->string.text = _cupsStrAlloc(output_bin->choices[i].choice);
       }
+
+      ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                  "output-bin-default", NULL, output_bin->defchoice);
     }
 
    /*
@@ -4010,6 +4185,16 @@ load_ppd(cupsd_printer_t *p)             /* I - Printer */
       else
        p->type |= CUPS_PRINTER_SMALL;
 
+    if ((ppd_attr = ppdFindAttr(ppd, "APICADriver", NULL)) != NULL &&
+        ppd_attr->value && !strcasecmp(ppd_attr->value, "true"))
+    {
+      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 a filter from application/vnd.cups-raw to printer/name to
     * handle "raw" printing by users.
@@ -4037,6 +4222,10 @@ load_ppd(cupsd_printer_t *p)             /* I - Printer */
     {
       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)
@@ -4060,7 +4249,8 @@ load_ppd(cupsd_printer_t *p)              /* I - Printer */
 
       for (i = 0; i < ppd->num_filters; i ++)
        if (!strncasecmp(ppd->filters[i],
-                        "application/vnd.cups-postscript", 31))
+                        "application/vnd.cups-postscript", 31) &&
+            isspace(ppd->filters[i][31] & 255))
          break;
 
       if (i < ppd->num_filters)