]> 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 c6465aba955ac54f4b276bd72453974fdde1b54f..c5008eec81cf455a4f621657c77488fd89b03d26 100644 (file)
@@ -118,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);
@@ -211,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,
@@ -371,13 +371,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",
@@ -622,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),
@@ -693,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...
@@ -804,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);
 
@@ -927,6 +920,7 @@ cupsdFreePrinterUsers(
 void
 cupsdLoadAllPrinters(void)
 {
+  int                  i;              /* Looping var */
   cups_file_t          *fp;            /* printers.conf file */
   int                  linenum;        /* Current line number */
   char                 line[4096],     /* Line from file */
@@ -1100,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,
@@ -1118,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);
@@ -1580,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);
@@ -1998,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;
@@ -2126,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,
@@ -2171,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...
@@ -2378,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))
@@ -2429,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)
     {
@@ -2507,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...
     */
@@ -2560,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';
     }
   }
 
@@ -2614,35 +2613,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 */
 {
-  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] == '+')
   {
@@ -2664,6 +2648,7 @@ cupsdSetPrinterReasons(
       _cupsStrFree(p->reasons[i]);
 
     p->num_reasons = 0;
+    changed        = 1;
 
     cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
 
@@ -2672,7 +2657,7 @@ cupsdSetPrinterReasons(
   }
 
   if (!strcmp(s, "none"))
-    return;
+    return (changed);
 
  /*
   * Loop through all of the reasons...
@@ -2709,11 +2694,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)
@@ -2724,11 +2706,12 @@ 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);
-
+           if (PrintcapFormat == PRINTCAP_PLIST)
+             cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
+          }
          break;
        }
     }
@@ -2749,38 +2732,28 @@ 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);
+         if (PrintcapFormat == PRINTCAP_PLIST)
+           cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
+       }
       }
     }
   }
 
-  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);
 }
 
 
@@ -2835,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);
 
  /*
@@ -2849,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);
@@ -2868,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...
   */
@@ -2881,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.");
 }
 
 
@@ -3538,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 */
 
 
  /*
@@ -3567,16 +3535,73 @@ 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);
     }
+
+   /*
+    * 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");
+
+       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);
+       }
+      }
+    }
   }
 
  /*
@@ -3805,6 +3830,9 @@ delete_printer_filters(
 
       mimeDeleteFilter(MimeDatabase, filter);
     }
+
+  cupsdSetPrinterReasons(p, "-cups-insecure-filter-warning"
+                            ",cups-missing-filter-warning");
 }
 
 
@@ -3976,9 +4004,13 @@ load_ppd(cupsd_printer_t *p)             /* I - Printer */
 
     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
     {
@@ -4097,8 +4129,8 @@ load_ppd(cupsd_printer_t *p)              /* I - Printer */
          val->string.text = _cupsStrAlloc(output_bin->choices[i].choice);
       }
 
-      attr = ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-                         "output-bin-default", NULL, output_bin->defchoice);
+      ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                  "output-bin-default", NULL, output_bin->defchoice);
     }
 
    /*
@@ -4153,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.