]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/printers.c
Merge changes from CUPS 1.4svn-r7614.
[thirdparty/cups.git] / scheduler / printers.c
index 936c9d0be988ace9461c04fb0bef53b140c530b2..3a63fc2e668b80f792abfb5989521fec35af40d6 100644 (file)
@@ -1,31 +1,23 @@
 /*
- * "$Id: printers.c 5970 2006-09-19 20:11:08Z mike $"
+ * "$Id: printers.c 7608 2008-05-21 01:37:21Z mike $"
  *
  *   Printer routines for the Common UNIX Printing System (CUPS).
  *
- *   Copyright 1997-2006 by Easy Software Products, all rights reserved.
+ *   Copyright 2007-2008 by Apple Inc.
+ *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  *   These coded instructions, statements, and computer programs are the
- *   property of Easy Software Products and are protected by Federal
- *   copyright law.  Distribution and use rights are outlined in the file
- *   "LICENSE.txt" which should have been included with this file.  If this
- *   file is missing or damaged please contact Easy Software Products
- *   at:
- *
- *       Attn: CUPS Licensing Information
- *       Easy Software Products
- *       44141 Airport View Drive, Suite 204
- *       Hollywood, Maryland 20636 USA
- *
- *       Voice: (301) 373-9600
- *       EMail: cups-info@cups.org
- *         WWW: http://www.cups.org
+ *   property of Apple Inc. and are protected by Federal copyright
+ *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+ *   which should have been included with this file.  If this file is
+ *   file is missing or damaged, see the license at "http://www.cups.org/".
  *
  * 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.
@@ -34,6 +26,8 @@
  *   cupsdRenamePrinter()        - Rename a printer.
  *   cupsdSaveAllPrinters()      - Save all printer definitions to the
  *                                 printers.conf file.
+ *   cupsdSetAuthInfoRequired()  - Set the required authentication info.
+ *   cupsdSetPrinterAttr()       - Set a printer attribute.
  *   cupsdSetPrinterAttrs()      - Set printer attributes based upon the PPD
  *                                 file.
  *   cupsdSetPrinterReasons()    - Set/update the reasons strings.
@@ -70,7 +64,8 @@
  */
 
 static void    add_printer_defaults(cupsd_printer_t *p);
-static void    add_printer_filter(cupsd_printer_t *p, const char *filter);
+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 int     compare_printers(void *first, void *second, void *data);
 static void    delete_printer_filters(cupsd_printer_t *p);
@@ -123,7 +118,7 @@ cupsdAddPrinter(const char *name)   /* I - Name of printer */
   cupsdSetString(&p->job_sheets[0], "none");
   cupsdSetString(&p->job_sheets[1], "none");
 
-  cupsdSetString(&p->error_policy, "stop-printer");
+  cupsdSetString(&p->error_policy, ErrorPolicy);
   cupsdSetString(&p->op_policy, DefaultPolicy);
 
   p->op_policy_ptr = DefaultPolicyPtr;
@@ -263,9 +258,10 @@ cupsdCreateCommonData(void)
   cups_array_t         *notifiers;     /* Notifier array */
   char                 filename[1024], /* Filename */
                        *notifier;      /* Current notifier */
+  cupsd_policy_t       *p;             /* Current policy */
   static const int nups[] =            /* number-up-supported values */
                { 1, 2, 4, 6, 9, 16 };
-  static const ipp_orient_t orients[4] =/* orientation-requested-supported values */
+  static const int orients[4] =/* orientation-requested-supported values */
                {
                  IPP_PORTRAIT,
                  IPP_LANDSCAPE,
@@ -288,7 +284,7 @@ cupsdCreateCommonData(void)
                  "1.0",
                  "1.1"
                };
-  static const ipp_op_t        ops[] =         /* operations-supported values */
+  static const int     ops[] =         /* operations-supported values */
                {
                  IPP_PRINT_JOB,
                  IPP_VALIDATE_JOB,
@@ -327,6 +323,8 @@ cupsdCreateCommonData(void)
                  CUPS_GET_PPDS,
                  CUPS_MOVE_JOB,
                  CUPS_AUTHENTICATE_JOB,
+                 CUPS_GET_PPD,
+                 CUPS_GET_DOCUMENT,
                  IPP_RESTART_JOB
                };
   static const char * const charsets[] =/* charset-supported values */
@@ -412,9 +410,9 @@ cupsdCreateCommonData(void)
   /* copies-supported */
   ippAddRange(CommonData, IPP_TAG_PRINTER, "copies-supported", 1, MaxCopies);
 
-  /* document-format-default */
-  ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE,
-               "document-format-default", NULL, "application/octet-stream");
+  /* cups-version */
+  ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_TEXT, "cups-version",
+               NULL, CUPS_SVERSION + 6);
 
   /* generated-natural-language-supported */
   ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE,
@@ -495,10 +493,6 @@ cupsdCreateCommonData(void)
                (int)(sizeof(notify_attrs) / sizeof(notify_attrs[0])),
                NULL, notify_attrs);
 
-  /* notify-lease-duration-default */
-  ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
-               "notify-lease-duration-default", DefaultLeaseDuration);
-
   /* notify-lease-duration-supported */
   ippAddRange(CommonData, IPP_TAG_PRINTER,
               "notify-lease-duration-supported", 0,
@@ -508,10 +502,6 @@ cupsdCreateCommonData(void)
   ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
                "notify-max-events-supported", MaxEvents);
 
-  /* notify-events-default */
-  ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-               "notify-events-default", NULL, "job-completed");
-
   /* notify-events-supported */
   ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
                 "notify-events-supported",
@@ -556,11 +546,11 @@ cupsdCreateCommonData(void)
   /* operations-supported */
   ippAddIntegers(CommonData, IPP_TAG_PRINTER, IPP_TAG_ENUM,
                  "operations-supported",
-                 sizeof(ops) / sizeof(ops[0]) + JobFiles - 1, (int *)ops);
+                 sizeof(ops) / sizeof(ops[0]) + JobFiles - 1, ops);
 
   /* orientation-requested-supported */
   ippAddIntegers(CommonData, IPP_TAG_PRINTER, IPP_TAG_ENUM,
-                 "orientation-requested-supported", 4, (int *)orients);
+                 "orientation-requested-supported", 4, orients);
 
   /* page-ranges-supported */
   ippAddBoolean(CommonData, IPP_TAG_PRINTER, "page-ranges-supported", 1);
@@ -576,9 +566,15 @@ cupsdCreateCommonData(void)
 
   /* printer-op-policy-supported */
   attr = ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_NAME,
-                       "printer-op-policy-supported", NumPolicies, NULL, NULL);
-  for (i = 0; i < NumPolicies; i ++)
-    attr->values[i].string.text = _cupsStrAlloc(Policies[i]->name);
+                       "printer-op-policy-supported", cupsArrayCount(Policies),
+                      NULL, NULL);
+  for (i = 0, p = (cupsd_policy_t *)cupsArrayFirst(Policies);
+       p;
+       i ++, p = (cupsd_policy_t *)cupsArrayNext(Policies))
+    attr->values[i].string.text = _cupsStrAlloc(p->name);
+
+  ippAddBoolean(CommonData, IPP_TAG_PRINTER, "server-is-sharing-printers",
+                BrowseLocalProtocols != 0 && Browsing);
 }
 
 
@@ -703,13 +699,18 @@ cupsdDeletePrinter(
   }
 
  /*
-  * Remove this printer from any classes and send a browse delete message...
+  * Remove this printer from any classes...
   */
 
   if (!(p->type & CUPS_PRINTER_IMPLICIT))
   {
     cupsdDeletePrinterFromClasses(p);
-    cupsdSendBrowseDelete(p);
+
+   /*
+    * Deregister from any browse protocols...
+    */
+
+    cupsdDeregisterPrinter(p, 1);
   }
 
  /*
@@ -735,6 +736,7 @@ cupsdDeletePrinter(
   delete_printer_filters(p);
 
   mimeDeleteType(MimeDatabase, p->filetype);
+  mimeDeleteType(MimeDatabase, p->prefiltertype);
 
   cupsdFreePrinterUsers(p);
   cupsdFreeQuotas(p);
@@ -752,6 +754,14 @@ cupsdDeletePrinter(
   cupsdClearString(&p->op_policy);
   cupsdClearString(&p->error_policy);
 
+  cupsdClearString(&p->alert);
+  cupsdClearString(&p->alert_description);
+
+#ifdef HAVE_DNSSD
+  cupsdClearString(&p->product);
+  cupsdClearString(&p->pdl);
+#endif /* HAVE_DNSSD */
+
   cupsArrayDelete(p->filetypes);
 
   if (p->browse_attrs)
@@ -900,7 +910,7 @@ cupsdLoadAllPrinters(void)
       {
         cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-        return;
+       break;
       }
     }
     else if (!strcasecmp(line, "</Printer>"))
@@ -945,14 +955,21 @@ cupsdLoadAllPrinters(void)
       {
         cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-        return;
+       break;
       }
     }
     else if (!p)
     {
       cupsdLogMessage(CUPSD_LOG_ERROR,
                       "Syntax error on line %d of printers.conf.", linenum);
-      return;
+      break;
+    }
+    else if (!strcasecmp(line, "AuthInfoRequired"))
+    {
+      if (!cupsdSetAuthInfoRequired(p, value, NULL))
+       cupsdLogMessage(CUPSD_LOG_ERROR,
+                       "Bad AuthInfoRequired on line %d of printers.conf.",
+                       linenum);
     }
     else if (!strcasecmp(line, "Info"))
     {
@@ -972,7 +989,7 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
       }
     }
     else if (!strcasecmp(line, "Option") && value)
@@ -1004,7 +1021,7 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
       }
     }
     else if (!strcasecmp(line, "State"))
@@ -1021,7 +1038,7 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
       }
     }
     else if (!strcasecmp(line, "StateMessage"))
@@ -1062,7 +1079,7 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
       }
     }
     else if (!strcasecmp(line, "Shared"))
@@ -1085,7 +1102,7 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
       }
     }
     else if (!strcasecmp(line, "JobSheets"))
@@ -1120,7 +1137,7 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
       }
     }
     else if (!strcasecmp(line, "AllowUser"))
@@ -1134,7 +1151,7 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
       }
     }
     else if (!strcasecmp(line, "DenyUser"))
@@ -1148,7 +1165,7 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
       }
     }
     else if (!strcasecmp(line, "QuotaPeriod"))
@@ -1159,7 +1176,7 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
       }
     }
     else if (!strcasecmp(line, "PageLimit"))
@@ -1170,7 +1187,7 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
       }
     }
     else if (!strcasecmp(line, "KLimit"))
@@ -1181,18 +1198,31 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
       }
     }
     else if (!strcasecmp(line, "OpPolicy"))
     {
       if (value)
-        cupsdSetString(&p->op_policy, value);
+      {
+        cupsd_policy_t *pol;           /* Policy */
+
+
+        if ((pol = cupsdFindPolicy(value)) != NULL)
+       {
+          cupsdSetString(&p->op_policy, value);
+         p->op_policy_ptr = pol;
+       }
+       else
+         cupsdLogMessage(CUPSD_LOG_ERROR,
+                         "Bad policy \"%s\" on line %d of printers.conf",
+                         value, linenum);
+      }
       else
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
       }
     }
     else if (!strcasecmp(line, "ErrorPolicy"))
@@ -1203,7 +1233,27 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
+      }
+    }
+    else if (!strcasecmp(line, "Attribute") && value)
+    {
+      for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++);
+
+      if (!*valueptr)
+        cupsdLogMessage(CUPSD_LOG_ERROR,
+                       "Syntax error on line %d of printers.conf.", linenum);
+      else
+      {
+        for (; *valueptr && isspace(*valueptr & 255); *valueptr++ = '\0');
+
+        if (!p->attrs)
+         cupsdSetPrinterAttrs(p);
+
+        cupsdSetPrinterAttr(p, value, valueptr);
+
+       if (!strncmp(value, "marker-", 7))
+         p->marker_time = time(NULL);
       }
     }
     else
@@ -1247,6 +1297,9 @@ cupsdRenamePrinter(
   mimeDeleteType(MimeDatabase, p->filetype);
   p->filetype = mimeAddType(MimeDatabase, "printer", name);
 
+  mimeDeleteType(MimeDatabase, p->prefiltertype);
+  p->prefiltertype = mimeAddType(MimeDatabase, "prefilter", name);
+
  /*
   * Rename the printer...
   */
@@ -1286,6 +1339,8 @@ cupsdSaveAllPrinters(void)
   time_t               curtime;        /* Current time */
   struct tm            *curdate;       /* Current date */
   cups_option_t                *option;        /* Current option */
+  const char           *ptr;           /* Pointer into info/location */
+  ipp_attribute_t      *marker;        /* Current marker attribute */
 
 
  /*
@@ -1345,7 +1400,7 @@ cupsdSaveAllPrinters(void)
     * Skip remote destinations and printer classes...
     */
 
-    if ((printer->type & CUPS_PRINTER_REMOTE) ||
+    if ((printer->type & CUPS_PRINTER_DISCOVERED) ||
         (printer->type & CUPS_PRINTER_CLASS) ||
        (printer->type & CUPS_PRINTER_IMPLICIT))
       continue;
@@ -1359,12 +1414,49 @@ cupsdSaveAllPrinters(void)
     else
       cupsFilePrintf(fp, "<Printer %s>\n", printer->name);
 
+    if (printer->num_auth_info_required > 0)
+    {
+      cupsFilePrintf(fp, "AuthInfoRequired %s", printer->auth_info_required[0]);
+      for (i = 1; i < printer->num_auth_info_required; i ++)
+        cupsFilePrintf(fp, ",%s", printer->auth_info_required[i]);
+      cupsFilePutChar(fp, '\n');
+    }
+
     if (printer->info)
-      cupsFilePrintf(fp, "Info %s\n", printer->info);
+    {
+      if ((ptr = strchr(printer->info, '#')) != NULL)
+      {
+       /*
+        * Need to quote the first # in the info string...
+       */
+
+        cupsFilePuts(fp, "Info ");
+       cupsFileWrite(fp, printer->info, ptr - printer->info);
+       cupsFilePutChar(fp, '\\');
+       cupsFilePuts(fp, ptr);
+       cupsFilePutChar(fp, '\n');
+      }
+      else
+        cupsFilePrintf(fp, "Info %s\n", printer->info);
+    }
 
     if (printer->location)
-      cupsFilePrintf(fp, "Location %s\n", printer->location);
+    {
+      if ((ptr = strchr(printer->info, '#')) != NULL)
+      {
+       /*
+        * Need to quote the first # in the location string...
+       */
 
+        cupsFilePuts(fp, "Location ");
+       cupsFileWrite(fp, printer->location, ptr - printer->location);
+       cupsFilePutChar(fp, '\\');
+       cupsFilePuts(fp, ptr);
+       cupsFilePutChar(fp, '\n');
+      }
+      else
+        cupsFilePrintf(fp, "Location %s\n", printer->location);
+    }
     if (printer->device_uri)
       cupsFilePrintf(fp, "DeviceURI %s\n", printer->device_uri);
 
@@ -1399,8 +1491,24 @@ cupsdSaveAllPrinters(void)
     cupsFilePrintf(fp, "KLimit %d\n", printer->k_limit);
 
     for (i = 0; i < printer->num_users; i ++)
-      cupsFilePrintf(fp, "%sUser %s\n", printer->deny_users ? "Deny" : "Allow",
-              printer->users[i]);
+    {
+      if ((ptr = strchr(printer->users[i], '#')) != NULL)
+      {
+       /*
+        * Need to quote the first # in the user string...
+       */
+
+        cupsFilePrintf(fp, "%sUser ", printer->deny_users ? "Deny" : "Allow");
+       cupsFileWrite(fp, printer->users[i], ptr - printer->users[i]);
+       cupsFilePutChar(fp, '\\');
+       cupsFilePuts(fp, ptr);
+       cupsFilePutChar(fp, '\n');
+      }
+      else
+        cupsFilePrintf(fp, "%sUser %s\n",
+                      printer->deny_users ? "Deny" : "Allow",
+                       printer->users[i]);
+    }
 
     if (printer->op_policy)
       cupsFilePrintf(fp, "OpPolicy %s\n", printer->op_policy);
@@ -1410,7 +1518,104 @@ cupsdSaveAllPrinters(void)
     for (i = printer->num_options, option = printer->options;
          i > 0;
         i --, option ++)
-      cupsFilePrintf(fp, "Option %s %s\n", option->name, option->value);
+    {
+      if ((ptr = strchr(option->value, '#')) != NULL)
+      {
+       /*
+        * Need to quote the first # in the option string...
+       */
+
+        cupsFilePrintf(fp, "Option %s ", option->name);
+       cupsFileWrite(fp, option->value, ptr - option->value);
+       cupsFilePutChar(fp, '\\');
+       cupsFilePuts(fp, ptr);
+       cupsFilePutChar(fp, '\n');
+      }
+      else
+        cupsFilePrintf(fp, "Option %s %s\n", option->name, option->value);
+    }
+
+    if ((marker = ippFindAttribute(printer->attrs, "marker-colors",
+                                   IPP_TAG_NAME)) != NULL)
+    {
+      cupsFilePrintf(fp, "Attribute %s ", marker->name);
+
+      for (i = 0, ptr = NULL; i < marker->num_values; i ++)
+      {
+        if (i)
+         cupsFilePutChar(fp, ',');
+
+        if (!ptr && (ptr = strchr(marker->values[i].string.text, '#')) != NULL)
+       {
+         cupsFileWrite(fp, marker->values[i].string.text,
+                       ptr - marker->values[i].string.text);
+         cupsFilePutChar(fp, '\\');
+         cupsFilePuts(fp, ptr);
+       }
+       else
+          cupsFilePuts(fp, marker->values[i].string.text);
+      }
+
+      cupsFilePuts(fp, "\n");
+    }
+
+    if ((marker = ippFindAttribute(printer->attrs, "marker-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-names",
+                                   IPP_TAG_NAME)) != NULL)
+    {
+      cupsFilePrintf(fp, "Attribute %s ", marker->name);
+
+      for (i = 0, ptr = NULL; i < marker->num_values; i ++)
+      {
+        if (i)
+         cupsFilePutChar(fp, ',');
+
+        if (!ptr && (ptr = strchr(marker->values[i].string.text, '#')) != NULL)
+       {
+         cupsFileWrite(fp, marker->values[i].string.text,
+                       ptr - marker->values[i].string.text);
+         cupsFilePutChar(fp, '\\');
+         cupsFilePuts(fp, ptr);
+       }
+       else
+          cupsFilePuts(fp, marker->values[i].string.text);
+      }
+
+      cupsFilePuts(fp, "\n");
+    }
+
+    if ((marker = ippFindAttribute(printer->attrs, "marker-types",
+                                   IPP_TAG_KEYWORD)) != NULL)
+    {
+      cupsFilePrintf(fp, "Attribute %s ", marker->name);
+
+      for (i = 0, ptr = NULL; i < marker->num_values; i ++)
+      {
+        if (i)
+         cupsFilePutChar(fp, ',');
+
+        if (!ptr && (ptr = strchr(marker->values[i].string.text, '#')) != NULL)
+       {
+         cupsFileWrite(fp, marker->values[i].string.text,
+                       ptr - marker->values[i].string.text);
+         cupsFilePutChar(fp, '\\');
+         cupsFilePuts(fp, ptr);
+       }
+       else
+          cupsFilePuts(fp, marker->values[i].string.text);
+      }
+
+      cupsFilePuts(fp, "\n");
+    }
 
     cupsFilePuts(fp, "</Printer>\n");
 
@@ -1427,6 +1632,275 @@ cupsdSaveAllPrinters(void)
 }
 
 
+/*
+ * 'cupsdSetAuthInfoRequired()' - Set the required authentication info.
+ */
+
+int                                    /* O - 1 if value OK, 0 otherwise */
+cupsdSetAuthInfoRequired(
+    cupsd_printer_t *p,                        /* I - Printer */
+    const char      *values,           /* I - Plain text value (or NULL) */
+    ipp_attribute_t *attr)             /* I - IPP attribute value (or NULL) */
+{
+  int  i;                              /* Looping var */
+
+
+  p->num_auth_info_required = 0;
+
+ /*
+  * Do we have a plain text value?
+  */
+
+  if (values)
+  {
+   /*
+    * Yes, grab the keywords...
+    */
+
+    const char *end;                   /* End of current value */
+
+
+    while (*values && p->num_auth_info_required < 4)
+    {
+      if ((end = strchr(values, ',')) == NULL)
+        end = values + strlen(values);
+
+      if ((end - values) == 4 && !strncmp(values, "none", 4))
+      {
+        if (p->num_auth_info_required != 0 || *end)
+         return (0);
+
+        p->auth_info_required[p->num_auth_info_required] = "none";
+       p->num_auth_info_required ++;
+
+       return (1);
+      }
+      else if ((end - values) == 9 && !strncmp(values, "negotiate", 9))
+      {
+        if (p->num_auth_info_required != 0 || *end)
+         return (0);
+
+        p->auth_info_required[p->num_auth_info_required] = "negotiate";
+       p->num_auth_info_required ++;
+      }
+      else if ((end - values) == 6 && !strncmp(values, "domain", 6))
+      {
+        p->auth_info_required[p->num_auth_info_required] = "domain";
+       p->num_auth_info_required ++;
+      }
+      else if ((end - values) == 8 && !strncmp(values, "password", 8))
+      {
+        p->auth_info_required[p->num_auth_info_required] = "password";
+       p->num_auth_info_required ++;
+      }
+      else if ((end - values) == 8 && !strncmp(values, "username", 8))
+      {
+        p->auth_info_required[p->num_auth_info_required] = "username";
+       p->num_auth_info_required ++;
+      }
+      else
+        return (0);
+
+      values = (*end) ? end + 1 : end;
+    }
+
+    if (p->num_auth_info_required == 0)
+    {
+      p->auth_info_required[0]  = "none";
+      p->num_auth_info_required = 1;
+    }
+
+   /*
+    * Update the printer-type value as needed...
+    */
+
+    if (p->num_auth_info_required > 1 ||
+        strcmp(p->auth_info_required[0], "none"))
+      p->type |= CUPS_PRINTER_AUTHENTICATED;
+    else
+      p->type &= ~CUPS_PRINTER_AUTHENTICATED;
+
+    return (1);
+  }
+
+ /*
+  * Grab values from an attribute instead...
+  */
+
+  if (!attr || attr->num_values > 4)
+    return (0);
+
+ /*
+  * Update the printer-type value as needed...
+  */
+
+  if (attr->num_values > 1 ||
+      strcmp(attr->values[0].string.text, "none"))
+    p->type |= CUPS_PRINTER_AUTHENTICATED;
+  else
+    p->type &= ~CUPS_PRINTER_AUTHENTICATED;
+
+  for (i = 0; i < attr->num_values; i ++)
+  {
+    if (!strcmp(attr->values[i].string.text, "none"))
+    {
+      if (p->num_auth_info_required != 0 || attr->num_values != 1)
+       return (0);
+
+      p->auth_info_required[p->num_auth_info_required] = "none";
+      p->num_auth_info_required ++;
+
+      return (1);
+    }
+    else if (!strcmp(attr->values[i].string.text, "negotiate"))
+    {
+      if (p->num_auth_info_required != 0 || attr->num_values != 1)
+       return (0);
+
+      p->auth_info_required[p->num_auth_info_required] = "negotiate";
+      p->num_auth_info_required ++;
+
+      return (1);
+    }
+    else if (!strcmp(attr->values[i].string.text, "domain"))
+    {
+      p->auth_info_required[p->num_auth_info_required] = "domain";
+      p->num_auth_info_required ++;
+    }
+    else if (!strcmp(attr->values[i].string.text, "password"))
+    {
+      p->auth_info_required[p->num_auth_info_required] = "password";
+      p->num_auth_info_required ++;
+    }
+    else if (!strcmp(attr->values[i].string.text, "username"))
+    {
+      p->auth_info_required[p->num_auth_info_required] = "username";
+      p->num_auth_info_required ++;
+    }
+    else
+      return (0);
+  }
+
+  return (1);
+}
+
+
+/*
+ * 'cupsdSetPrinterAttr()' - Set a printer attribute.
+ */
+
+void
+cupsdSetPrinterAttr(
+    cupsd_printer_t *p,                        /* I - Printer */
+    const char      *name,             /* I - Attribute name */
+    char            *value)            /* I - Attribute value string */
+{
+  ipp_attribute_t      *attr;          /* Attribute */
+  int                  i,              /* Looping var */
+                       count;          /* Number of values */
+  char                 *ptr;           /* Pointer into value */
+  ipp_tag_t            value_tag;      /* Value tag for this attribute */
+
+
+ /*
+  * Count the number of values...
+  */
+
+  for (count = 1, ptr = value;
+       (ptr = strchr(ptr, ',')) != NULL;
+       ptr ++, count ++);
+
+ /*
+  * Then add or update the attribute as needed...
+  */
+
+  if (!strcmp(name, "marker-levels"))
+  {
+   /*
+    * Integer values...
+    */
+
+    if ((attr = ippFindAttribute(p->attrs, name, IPP_TAG_INTEGER)) != NULL &&
+        attr->num_values < count)
+    {
+      ippDeleteAttribute(p->attrs, attr);
+      attr = NULL;
+    }
+
+    if (attr)
+      attr->num_values = count;
+    else
+      attr = ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, name,
+                            count, NULL);
+
+    if (!attr)
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                      "Unable to allocate memory for printer attribute "
+                     "(%d values)", count);
+      return;
+    }
+
+    for (i = 0; i < count; i ++)
+    {
+      if ((ptr = strchr(value, ',')) != NULL)
+        *ptr++ = '\0';
+
+      attr->values[i].integer = strtol(value, NULL, 10);
+
+      if (ptr)
+        value = ptr;
+    }
+  }
+  else
+  {
+   /*
+    * Name or keyword values...
+    */
+
+    if (!strcmp(name, "marker-types"))
+      value_tag = IPP_TAG_KEYWORD;
+    else
+      value_tag = IPP_TAG_NAME;
+
+    if ((attr = ippFindAttribute(p->attrs, name, value_tag)) != NULL &&
+        attr->num_values < count)
+    {
+      ippDeleteAttribute(p->attrs, attr);
+      attr = NULL;
+    }
+
+    if (attr)
+      attr->num_values = count;
+    else
+      attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, value_tag, name,
+                           count, NULL, NULL);
+
+    if (!attr)
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                      "Unable to allocate memory for printer attribute "
+                     "(%d values)", count);
+      return;
+    }
+
+    for (i = 0; i < count; i ++)
+    {
+      if ((ptr = strchr(value, ',')) != NULL)
+        *ptr++ = '\0';
+
+      _cupsStrFree(attr->values[i].string.text);
+      attr->values[i].string.text = _cupsStrAlloc(value);
+
+      if (ptr)
+        value = ptr;
+    }
+  }
+
+  cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
+}
+
+
 /*
  * 'cupsdSetPrinterAttrs()' - Set printer attributes based upon the PPD file.
  */
@@ -1439,10 +1913,11 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
   char         uri[HTTP_MAX_URI];      /* URI for printer */
   char         resource[HTTP_MAX_URI]; /* Resource portion of URI */
   char         filename[1024];         /* Name of PPD file */
+  int          num_air;                /* Number of auth-info-required values */
+  const char   * const *air;           /* auth-info-required values */
   int          num_media;              /* Number of media options */
   cupsd_location_t *auth;              /* Pointer to authentication element */
   const char   *auth_supported;        /* Authentication supported */
-  cups_ptype_t printer_type;           /* Printer type data */
   ppd_file_t   *ppd;                   /* PPD file data */
   ppd_option_t *input_slot,            /* InputSlot options */
                *media_type,            /* MediaType options */
@@ -1451,10 +1926,11 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
                *media_quality,         /* EFMediaQualityMode options */
                *duplex;                /* Duplex options */
   ppd_attr_t   *ppdattr;               /* PPD attribute */
+  ipp_t                *oldattrs;              /* Old printer attributes */
   ipp_attribute_t *attr;               /* Attribute data */
   ipp_value_t  *val;                   /* Attribute value */
   int          num_finishings;         /* Number of finishings */
-  ipp_finish_t finishings[5];          /* finishings-supported values */
+  int          finishings[5];          /* finishings-supported values */
   cups_option_t        *option;                /* Current printer option */
   static const char * const sides[3] = /* sides-supported values */
                {
@@ -1462,6 +1938,28 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
                  "two-sided-long-edge",
                  "two-sided-short-edge"
                };
+  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"
+               };
+  static const char * const standard_commands[] =
+               {                       /* Standard CUPS commands */
+                 "AutoConfigure",
+                 "Clean",
+                 "PrintSelfTestPage",
+                 "ReportLevels"
+               };
 
 
   DEBUG_printf(("cupsdSetPrinterAttrs: entering name = %s, type = %x\n", p->name,
@@ -1485,7 +1983,20 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
   */
 
   auth_supported = "requesting-user-name";
-  if (!(p->type & CUPS_PRINTER_REMOTE))
+  num_air        = 1;
+  air            = air_none;
+
+  if (p->num_auth_info_required > 0 && strcmp(p->auth_info_required[0], "none"))
+  {
+    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))
   {
     if (p->type & CUPS_PRINTER_CLASS)
       snprintf(resource, sizeof(resource), "/classes/%s", p->name);
@@ -1493,17 +2004,33 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
       snprintf(resource, sizeof(resource), "/printers/%s", p->name);
 
     if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL ||
-        auth->type == AUTH_NONE)
+        auth->type == CUPSD_AUTH_NONE)
       auth = cupsdFindPolicyOp(p->op_policy_ptr, IPP_PRINT_JOB);
 
     if (auth)
     {
-      if (auth->type == AUTH_BASIC || auth->type == AUTH_BASICDIGEST)
+      if (auth->type == CUPSD_AUTH_BASIC || auth->type == CUPSD_AUTH_BASICDIGEST)
+      {
        auth_supported = "basic";
-      else if (auth->type == AUTH_DIGEST)
+       num_air        = 2;
+       air            = air_userpass;
+      }
+      else if (auth->type == CUPSD_AUTH_DIGEST)
+      {
        auth_supported = "digest";
+       num_air        = 2;
+       air            = air_userpass;
+      }
+#ifdef HAVE_GSSAPI
+      else if (auth->type == CUPSD_AUTH_NEGOTIATE)
+      {
+       auth_supported = "negotiate";
+       num_air        = 1;
+       air            = air_negotiate;
+      }
+#endif /* HAVE_GSSAPI */
 
-      if (auth->type != AUTH_NONE)
+      if (auth->type != CUPSD_AUTH_NONE)
         p->type |= CUPS_PRINTER_AUTHENTICATED;
       else
         p->type &= ~CUPS_PRINTER_AUTHENTICATED;
@@ -1511,14 +2038,17 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
     else
       p->type &= ~CUPS_PRINTER_AUTHENTICATED;
   }
+  else if (p->type & CUPS_PRINTER_AUTHENTICATED)
+  {
+    num_air = 2;
+    air     = air_userpass;
+  }
 
  /*
   * Create the required IPP attributes for a printer...
   */
 
-  if (p->attrs)
-    ippDelete(p->attrs);
-
+  oldattrs = p->attrs;
   p->attrs = ippNew();
 
   ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
@@ -1552,8 +2082,10 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
                 "job-k-limit", p->k_limit);
   ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
                 "job-page-limit", p->page_limit);
+  ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+               "auth-info-required", num_air, NULL, air);
 
-  if (cupsArrayCount(Banners) > 0 && !(p->type & CUPS_PRINTER_REMOTE))
+  if (cupsArrayCount(Banners) > 0 && !(p->type & CUPS_PRINTER_DISCOVERED))
   {
    /*
     * Setup the job-sheets-default attribute...
@@ -1571,12 +2103,10 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
     }
   }
 
-  printer_type = p->type;
-
   p->raw    = 0;
   p->remote = 0;
 
-  if (p->type & CUPS_PRINTER_REMOTE)
+  if (p->type & CUPS_PRINTER_DISCOVERED)
   {
    /*
     * Tell the client this is a remote printer of some type...
@@ -1880,7 +2410,20 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
        * handle "raw" printing by users.
        */
 
-        add_printer_filter(p, "application/vnd.cups-raw 0 -");
+        add_printer_filter(p, p->filetype, "application/vnd.cups-raw 0 -");
+
+       /*
+       * Add any pre-filters in the PPD file...
+       */
+
+       if ((ppdattr = ppdFindAttr(ppd, "cupsPreFilter", NULL)) != NULL)
+       {
+         p->prefiltertype = mimeAddType(MimeDatabase, "prefilter", p->name);
+
+         for (; ppdattr; ppdattr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL))
+           if (ppdattr->value)
+             add_printer_filter(p, p->prefiltertype, ppdattr->value);
+       }
 
        /*
        * Add any filters in the PPD file...
@@ -1890,26 +2433,134 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
        for (i = 0; i < ppd->num_filters; i ++)
        {
           DEBUG_printf(("ppd->filters[%d] = \"%s\"\n", i, ppd->filters[i]));
-          add_printer_filter(p, ppd->filters[i]);
+          add_printer_filter(p, p->filetype, ppd->filters[i]);
        }
 
        if (ppd->num_filters == 0)
        {
         /*
-         * If there are no filters, add a PostScript printing filter.
+         * If there are no filters, add PostScript printing filters.
          */
 
-          add_printer_filter(p, "application/vnd.cups-postscript 0 -");
+          add_printer_filter(p, p->filetype,
+                            "application/vnd.cups-command 0 commandtops");
+          add_printer_filter(p, p->filetype,
+                            "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))
+             break;
+
+          if (i < ppd->num_filters)
+         {
+          /*
+           * Add the generic PostScript command filter...
+           */
+
+           add_printer_filter(p, p->filetype,
+                              "application/vnd.cups-command 0 commandtops");
+           p->type |= CUPS_PRINTER_COMMANDS;
+         }
+       }
+
+        if (p->type & CUPS_PRINTER_COMMANDS)
+       {
+         char  *commands,              /* Copy of commands */
+               *start,                 /* Start of name */
+               *end;                   /* End of name */
+          int  count;                  /* Number of commands */
+
+
+          if ((ppdattr = ppdFindAttr(ppd, "cupsCommands", NULL)) != NULL &&
+             ppdattr->value && ppdattr->value[0])
+         {
+           for (count = 0, start = ppdattr->value; *start; count ++)
+           {
+             while (isspace(*start & 255))
+               start ++;
+
+             if (!*start)
+               break;
+
+             while (*start && !isspace(*start & 255))
+               start ++;
+           }
+         }
+         else
+           count = 0;
+
+          if (count > 0)
+         {
+          /*
+           * Make a copy of the commands string and count how many ...
+           */
+
+           attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                                "printer-commands", count, NULL, NULL);
+
+           commands = strdup(ppdattr->value);
+
+           for (count = 0, start = commands; *start; count ++)
+           {
+             while (isspace(*start & 255))
+               start ++;
+
+             if (!*start)
+               break;
+
+              end = start;
+             while (*end && !isspace(*end & 255))
+               end ++;
+
+              if (*end)
+               *end++ = '\0';
+
+              attr->values[count].string.text = _cupsStrAlloc(start);
+
+              start = end;
+           }
+
+           free(commands);
+          }
+         else
+         {
+          /*
+           * Add the standard list of commands...
+           */
+
+           ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                         "printer-commands",
+                         (int)(sizeof(standard_commands) /
+                               sizeof(standard_commands[0])), NULL,
+                         standard_commands);
+         }
+       }
+       else
+       {
+        /*
+         * No commands supported...
+         */
+
+         ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                      "printer-commands", NULL, "none");
         }
 
        /*
        * Show current and available port monitors for this printer...
        */
 
-       ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "port-monitor",
+       ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, "port-monitor",
                      NULL, p->port_monitor ? p->port_monitor : "none");
 
-
         for (i = 1, ppdattr = ppdFindAttr(ppd, "cupsPortMonitor", NULL);
             ppdattr;
             i ++, ppdattr = ppdFindNextAttr(ppd, "cupsPortMonitor", NULL));
@@ -1922,7 +2573,7 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
            i ++;
        }
 
-        attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+        attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
                             "port-monitor-supported", i, NULL, NULL);
 
         attr->values[0].string.text = _cupsStrAlloc("none");
@@ -1940,13 +2591,18 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
            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;
+
        /*
         * Close the PPD and set the type...
        */
 
        ppdClose(ppd);
-
-        printer_type = p->type;
       }
       else if (!access(filename, 0))
       {
@@ -1974,13 +2630,14 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
        * handle "raw" printing by users.
        */
 
-        add_printer_filter(p, "application/vnd.cups-raw 0 -");
+        add_printer_filter(p, p->filetype, "application/vnd.cups-raw 0 -");
 
        /*
         * Add a PostScript filter, since this is still possibly PS printer.
        */
 
-       add_printer_filter(p, "application/vnd.cups-postscript 0 -");
+       add_printer_filter(p, p->filetype,
+                          "application/vnd.cups-postscript 0 -");
       }
       else
       {
@@ -1997,11 +2654,12 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
          */
 
          ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
-                       "printer-make-and-model", NULL, "Local System V Printer");
+                       "printer-make-and-model", NULL,
+                      "Local System V Printer");
 
          snprintf(filename, sizeof(filename), "*/* 0 %s/interfaces/%s",
                   ServerRoot, p->name);
-         add_printer_filter(p, filename);
+         add_printer_filter(p, p->filetype, filename);
        }
        else if (p->device_uri &&
                 !strncmp(p->device_uri, "ipp://", 6) &&
@@ -2012,7 +2670,7 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
          * Tell the client this is really a hard-wired remote printer.
          */
 
-          printer_type |= CUPS_PRINTER_REMOTE;
+          p->type |= CUPS_PRINTER_REMOTE;
 
          /*
          * Point the printer-uri-supported attribute to the
@@ -2051,18 +2709,87 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
       }
 
       ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
-                     "finishings-supported", num_finishings, (int *)finishings);
+                     "finishings-supported", num_finishings, finishings);
       ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
                     "finishings-default", IPP_FINISHINGS_NONE);
     }
   }
 
  /*
-  * Copy the printer options into a browse attributes string we can re-use.
+  * Copy marker attributes as needed...
+  */
+
+  if (oldattrs)
+  {
+    ipp_attribute_t *oldattr;          /* Old attribute */
+
+
+    if ((oldattr = ippFindAttribute(oldattrs, "marker-colors",
+                                    IPP_TAG_NAME)) != NULL)
+    {
+      if ((attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
+                                "marker-colors", oldattr->num_values, NULL,
+                               NULL)) != NULL)
+      {
+       for (i = 0; i < oldattr->num_values; i ++)
+         attr->values[i].string.text =
+             _cupsStrAlloc(oldattr->values[i].string.text);
+      }
+    }
+
+    if ((oldattr = ippFindAttribute(oldattrs, "marker-levels",
+                                    IPP_TAG_INTEGER)) != NULL)
+    {
+      if ((attr = ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                                 "marker-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 =
+             _cupsStrAlloc(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,
+                                "marker-types", oldattr->num_values, NULL,
+                               NULL)) != NULL)
+      {
+       for (i = 0; i < oldattr->num_values; i ++)
+         attr->values[i].string.text =
+             _cupsStrAlloc(oldattr->values[i].string.text);
+      }
+    }
+
+    ippDelete(oldattrs);
+  }
+
+ /*
+  * Force sharing off for remote queues...
   */
 
-  if (!(p->type & CUPS_PRINTER_REMOTE))
+  if (p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT))
+    p->shared = 0;
+  else
   {
+   /*
+    * Copy the printer options into a browse attributes string we can re-use.
+    */
+
     const char *valptr;                /* Pointer into value */
     char       *attrptr;               /* Pointer into attribute string */
 
@@ -2098,12 +2825,20 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
     length += 13 + strlen(p->job_sheets[0]) + strlen(p->job_sheets[1]);
     length += 32;
     if (BrowseLocalOptions)
-      length += 12 + strlen(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...
     */
+
     if ((p->browse_attrs = calloc(1, length)) == NULL)
       cupsdLogMessage(CUPSD_LOG_ERROR,
                       "Unable to allocate %d bytes for browse data!",
@@ -2146,7 +2881,20 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
        }
       }
 
-      *attrptr = '\0';
+      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';
     }
   }
 
@@ -2173,6 +2921,12 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
   write_irix_config(p);
   write_irix_state(p);
 #endif /* __sgi */
+
+ /*
+  * Let the browse protocols reflect the change
+  */
+
+  cupsdRegisterPrinter(p);
 }
 
 
@@ -2213,6 +2967,9 @@ cupsdSetPrinterReasons(
     p->num_reasons = 0;
   }
 
+  if (!strcmp(s, "none"))
+    return;
+
  /*
   * Loop through all of the reasons...
   */
@@ -2256,6 +3013,9 @@ cupsdSetPrinterReasons(
                    (p->num_reasons - i) * sizeof(char *));
 
          i --;
+
+          if (!strcmp(reason, "paused") && p->state == IPP_PRINTER_STOPPED)
+           cupsdSetPrinterState(p, IPP_PRINTER_IDLE, 1);
        }
     }
     else if (p->num_reasons < (int)(sizeof(p->reasons) / sizeof(p->reasons[0])))
@@ -2272,6 +3032,9 @@ cupsdSetPrinterReasons(
       {
         p->reasons[i] = strdup(reason);
        p->num_reasons ++;
+
+       if (!strcmp(reason, "paused") && p->state != IPP_PRINTER_STOPPED)
+         cupsdSetPrinterState(p, IPP_PRINTER_STOPPED, 1);
       }
     }
   }
@@ -2295,7 +3058,7 @@ cupsdSetPrinterState(
   * Can't set status of remote printers...
   */
 
-  if (p->type & CUPS_PRINTER_REMOTE)
+  if (p->type & CUPS_PRINTER_DISCOVERED)
     return;
 
  /*
@@ -2307,7 +3070,8 @@ cupsdSetPrinterState(
 
   if (old_state != s)
   {
-    cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE_CHANGED, p, NULL,
+    cupsdAddEvent(s == IPP_PRINTER_STOPPED ? CUPSD_EVENT_PRINTER_STOPPED :
+                      CUPSD_EVENT_PRINTER_STATE, p, NULL,
                  "%s \"%s\" state changed.",
                  (p->type & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
                  p->name);
@@ -2327,6 +3091,13 @@ cupsdSetPrinterState(
 
   cupsdAddPrinterHistory(p);
 
+ /*
+  * Let the browse protocols reflect the change...
+  */
+
+  if (update)
+    cupsdRegisterPrinter(p);
+
  /*
   * Save the printer configuration if a printer goes from idle or processing
   * to stopped (or visa-versa)...
@@ -2336,9 +3107,9 @@ cupsdSetPrinterState(
       update)
   {
     if (p->type & CUPS_PRINTER_CLASS)
-      cupsdSaveAllClasses();
+      cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
     else
-      cupsdSaveAllPrinters();
+      cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
   }
 }
 
@@ -2384,8 +3155,9 @@ cupsdStopPrinter(cupsd_printer_t *p,      /* I - Printer to stop */
 
     job->state->values[0].integer = IPP_JOB_PENDING;
     job->state_value              = IPP_JOB_PENDING;
+    job->dirty                    = 1;
 
-    cupsdSaveJob(job);
+    cupsdMarkDirty(CUPSD_DIRTY_JOBS);
 
     cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, p, job,
                  "Job stopped due to printer being paused");
@@ -2393,6 +3165,118 @@ cupsdStopPrinter(cupsd_printer_t *p,    /* I - Printer to stop */
 }
 
 
+/*
+ * 'cupsdUpdatePrinterPPD()' - Update keywords in a printer's PPD file.
+ */
+
+int                                    /* O - 1 if successful, 0 otherwise */
+cupsdUpdatePrinterPPD(
+    cupsd_printer_t *p,                        /* I - Printer */
+    int             num_keywords,      /* I - Number of keywords */
+    cups_option_t   *keywords)         /* I - Keywords */
+{
+  int          i;                      /* Looping var */
+  cups_file_t  *src,                   /* Original file */
+               *dst;                   /* New file */
+  char         srcfile[1024],          /* Original filename */
+               dstfile[1024],          /* New filename */
+               line[1024],             /* Line from file */
+               keystring[41];          /* Keyword from line */
+  cups_option_t        *keyword;               /* Current keyword */
+
+
+  cupsdLogMessage(CUPSD_LOG_INFO, "Updating keywords in PPD file for %s...",
+                  p->name);
+
+ /*
+  * Get the old and new PPD filenames...
+  */
+
+  snprintf(srcfile, sizeof(srcfile), "%s/ppd/%s.ppd.O", ServerRoot, p->name);
+  snprintf(dstfile, sizeof(srcfile), "%s/ppd/%s.ppd", ServerRoot, p->name);
+
+ /*
+  * Rename the old file and open the old and new...
+  */
+
+  if (rename(dstfile, srcfile))
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to backup PPD file for %s: %s",
+                    p->name, strerror(errno));
+    return (0);
+  }
+
+  if ((src = cupsFileOpen(srcfile, "r")) == NULL)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open PPD file \"%s\": %s",
+                    srcfile, strerror(errno));
+    rename(srcfile, dstfile);
+    return (0);
+  }
+
+  if ((dst = cupsFileOpen(dstfile, "w")) == NULL)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create PPD file \"%s\": %s",
+                    dstfile, strerror(errno));
+    cupsFileClose(src);
+    rename(srcfile, dstfile);
+    return (0);
+  }
+
+ /*
+  * Copy the first line and then write out all of the keywords...
+  */
+
+  if (!cupsFileGets(src, line, sizeof(line)))
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to read PPD file \"%s\": %s",
+                    srcfile, strerror(errno));
+    cupsFileClose(src);
+    cupsFileClose(dst);
+    rename(srcfile, dstfile);
+    return (0);
+  }
+
+  cupsFilePrintf(dst, "%s\n", line);
+
+  for (i = num_keywords, keyword = keywords; i > 0; i --, keyword ++)
+  {
+    cupsdLogMessage(CUPSD_LOG_DEBUG, "*%s: %s", keyword->name, keyword->value);
+    cupsFilePrintf(dst, "*%s: %s\n", keyword->name, keyword->value);
+  }
+
+ /*
+  * Then copy the rest of the PPD file, dropping any keywords we changed.
+  */
+
+  while (cupsFileGets(src, line, sizeof(line)))
+  {
+   /*
+    * Skip keywords we've already set...
+    */
+
+    if (sscanf(line, "*%40[^:]:", keystring) == 1 &&
+        cupsGetOption(keystring, num_keywords, keywords))
+      continue;
+
+   /*
+    * Otherwise write the line...
+    */
+
+    cupsFilePrintf(dst, "%s\n", line);
+  }
+
+ /*
+  * Close files and return...
+  */
+
+  cupsFileClose(src);
+  cupsFileClose(dst);
+
+  return (1);
+}
+
+
 /*
  * 'cupsdUpdatePrinters()' - Update printers after a partial reload.
  */
@@ -2418,7 +3302,8 @@ cupsdUpdatePrinters(void)
     * Remove remote printers if we are no longer browsing...
     */
 
-    if (!Browsing && (p->type & (CUPS_PRINTER_IMPLICIT | CUPS_PRINTER_REMOTE)))
+    if (!Browsing &&
+        (p->type & (CUPS_PRINTER_IMPLICIT | CUPS_PRINTER_DISCOVERED)))
     {
       if (p->type & CUPS_PRINTER_IMPLICIT)
         cupsArrayRemove(ImplicitPrinters, p);
@@ -2440,7 +3325,7 @@ cupsdUpdatePrinters(void)
     * Update printer attributes as needed...
     */
 
-    if (!(p->type & CUPS_PRINTER_REMOTE))
+    if (!(p->type & CUPS_PRINTER_DISCOVERED))
       cupsdSetPrinterAttrs(p);
   }
 }
@@ -2452,18 +3337,25 @@ cupsdUpdatePrinters(void)
 
 const char *                           /* O - Printer or class name */
 cupsdValidateDest(
-    const char      *hostname,         /* I - Host name */
-    const char      *resource,         /* I - Resource name */
+    const char      *uri,              /* I - Printer URI */
     cups_ptype_t    *dtype,            /* O - Type (printer or class) */
     cupsd_printer_t **printer)         /* O - Printer pointer */
 {
   cupsd_printer_t      *p;             /* Current printer */
   char                 localname[1024],/* Localized hostname */
                        *lptr,          /* Pointer into localized hostname */
-                       *sptr;          /* Pointer into server name */
-
-
-  DEBUG_printf(("cupsdValidateDest(\"%s\", \"%s\", %p, %p)\n", hostname, resource,
+                       *sptr,          /* Pointer into server name */
+                       *rptr,          /* Pointer into resource */
+                       scheme[32],     /* Scheme portion of URI */
+                       username[64],   /* Username portion of URI */
+                       hostname[HTTP_MAX_HOST],
+                                       /* Host portion of URI */
+                       resource[HTTP_MAX_URI];
+                                       /* Resource portion of URI */
+  int                  port;           /* Port portion of URI */
+
+
+  DEBUG_printf(("cupsdValidateDest(uri=\"%s\", dtype=%p, printer=%p)\n", uri,
                 dtype, printer));
 
  /*
@@ -2473,7 +3365,16 @@ cupsdValidateDest(
   if (printer)
     *printer = NULL;
 
-  *dtype = (cups_ptype_t)0;
+  if (dtype)
+    *dtype = (cups_ptype_t)0;
+
+ /*
+  * Pull the hostname and resource from the URI...
+  */
+
+  httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme),
+                  username, sizeof(username), hostname, sizeof(hostname),
+                 &port, resource, sizeof(resource));
 
  /*
   * See if the resource is a class or printer...
@@ -2485,7 +3386,7 @@ cupsdValidateDest(
     * Class...
     */
 
-    resource += 9;
+    rptr = resource + 9;
   }
   else if (!strncmp(resource, "/printers/", 10))
   {
@@ -2493,7 +3394,7 @@ cupsdValidateDest(
     * Printer...
     */
 
-    resource += 10;
+    rptr = resource + 10;
   }
   else
   {
@@ -2508,17 +3409,19 @@ cupsdValidateDest(
   * See if the printer or class name exists...
   */
 
-  p = cupsdFindDest(resource);
+  p = cupsdFindDest(rptr);
 
-  if (p == NULL && strchr(resource, '@') == NULL)
+  if (p == NULL && strchr(rptr, '@') == NULL)
     return (NULL);
   else if (p != NULL)
   {
     if (printer)
       *printer = p;
 
-    *dtype = p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT |
-                        CUPS_PRINTER_REMOTE);
+    if (dtype)
+      *dtype = p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT |
+                          CUPS_PRINTER_REMOTE | CUPS_PRINTER_DISCOVERED);
+
     return (p->name);
   }
 
@@ -2527,7 +3430,7 @@ cupsdValidateDest(
   */
 
   if (!strcasecmp(hostname, "localhost"))
-    hostname = ServerName;
+    strlcpy(hostname, ServerName, sizeof(hostname));
 
   strlcpy(localname, hostname, sizeof(localname));
 
@@ -2569,13 +3472,15 @@ cupsdValidateDest(
        p;
        p = (cupsd_printer_t *)cupsArrayNext(Printers))
     if (!strcasecmp(p->hostname, localname) &&
-        !strcasecmp(p->name, resource))
+        !strcasecmp(p->name, rptr))
     {
       if (printer)
         *printer = p;
 
-      *dtype = p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT |
-                          CUPS_PRINTER_REMOTE);
+      if (dtype)
+       *dtype = p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT |
+                            CUPS_PRINTER_REMOTE | CUPS_PRINTER_DISCOVERED);
+
       return (p->name);
     }
 
@@ -2624,7 +3529,8 @@ cupsdWritePrintcap(void)
   * data has come from...
   */
 
-  cupsFilePuts(fp, "# This file was automatically generated by cupsd(8) from the\n");
+  cupsFilePuts(fp,
+               "# This file was automatically generated by cupsd(8) from the\n");
   cupsFilePrintf(fp, "# %s/printers.conf file.  All changes to this file\n",
                  ServerRoot);
   cupsFilePuts(fp, "# will be lost.\n");
@@ -2650,14 +3556,15 @@ cupsdWritePrintcap(void)
 
           if (DefaultPrinter)
            cupsFilePrintf(fp, "%s|%s:rm=%s:rp=%s:\n", DefaultPrinter->name,
-                   DefaultPrinter->info, ServerName, 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);
+                            ServerName, p->name);
           break;
 
       case PRINTCAP_SOLARIS:
@@ -2695,9 +3602,10 @@ cupsdWritePrintcap(void)
               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 : "");
+                              "\t:bsdaddr=%s,%s:\\\n"
+                              "\t:description=%s:\n",
+                          p->name, ServerName, p->name,
+                          p->info ? p->info : "");
           break;
     }
   }
@@ -2792,6 +3700,27 @@ add_printer_defaults(cupsd_printer_t *p)/* I - Printer */
   char         name[256];              /* name-default */
 
 
+ /*
+  * Maintain a common array of default attribute names...
+  */
+
+  if (!CommonDefaults)
+  {
+    CommonDefaults = cupsArrayNew((cups_array_func_t)strcmp, NULL);
+
+    cupsArrayAdd(CommonDefaults, _cupsStrAlloc("copies-default"));
+    cupsArrayAdd(CommonDefaults, _cupsStrAlloc("document-format-default"));
+    cupsArrayAdd(CommonDefaults, _cupsStrAlloc("finishings-default"));
+    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("number-up-default"));
+    cupsArrayAdd(CommonDefaults,
+                 _cupsStrAlloc("orientation-requested-default"));
+    cupsArrayAdd(CommonDefaults, _cupsStrAlloc("sides-default"));
+  }
+
  /*
   * Add all of the default options from the .conf files...
   */
@@ -2806,6 +3735,9 @@ add_printer_defaults(cupsd_printer_t *p)/* I - Printer */
     {
       snprintf(name, sizeof(name), "%s-default", option->name);
       num_options = cupsAddOption(name, option->value, num_options, &options);
+
+      if (!cupsArrayFind(CommonDefaults, name))
+        cupsArrayAdd(CommonDefaults, _cupsStrAlloc(name));
     }
   }
 
@@ -2824,6 +3756,10 @@ add_printer_defaults(cupsd_printer_t *p)/* I - Printer */
     ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "copies-default",
                   1);
 
+  if (!cupsGetOption("document-format", p->num_options, p->options))
+    ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE,
+                "document-format-default", NULL, "application/octet-stream");
+
   if (!cupsGetOption("job-hold-until", p->num_options, p->options))
     ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
                  "job-hold-until-default", NULL, "no-hold");
@@ -2837,8 +3773,16 @@ add_printer_defaults(cupsd_printer_t *p)/* I - Printer */
                   "number-up-default", 1);
 
   if (!cupsGetOption("orientation-requested", p->num_options, p->options))
-    ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
-                  "orientation-requested-default", IPP_PORTRAIT);
+    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);
+
+  if (!cupsGetOption("notify-events", p->num_options, p->options))
+    ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                "notify-events-default", NULL, "job-completed");
 }
 
 
@@ -2849,6 +3793,7 @@ add_printer_defaults(cupsd_printer_t *p)/* I - Printer */
 static void
 add_printer_filter(
     cupsd_printer_t  *p,               /* I - Printer to add to */
+    mime_type_t             *filtertype,       /* I - Filter or prefilter MIME type */
     const char       *filter)          /* I - Filter to add */
 {
   char         super[MIME_MAX_SUPER],  /* Super-type for filter */
@@ -2919,9 +3864,9 @@ add_printer_filter(
       cupsdLogMessage(CUPSD_LOG_DEBUG2,
                       "add_printer_filter: %s: adding filter %s/%s %s/%s %d %s",
                       p->name, temptype->super, temptype->type,
-                     p->filetype->super, p->filetype->type,
+                     filtertype->super, filtertype->type,
                       cost, program);
-      mimeAddFilter(MimeDatabase, temptype, p->filetype, cost, program);
+      mimeAddFilter(MimeDatabase, temptype, filtertype, cost, program);
     }
 }
 
@@ -3017,6 +3962,54 @@ add_printer_formats(cupsd_printer_t *p)  /* I - Printer */
 
     attr->values[i].string.text = _cupsStrAlloc(mimetype);
   }
+
+#ifdef HAVE_DNSSD
+  {
+    char               pdl[1024];      /* Buffer to build pdl list */
+    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...
+    */
+
+    for (filter = (mime_filter_t *)cupsArrayFirst(MimeDatabase->filters);
+        filter;
+        filter = (mime_filter_t *)cupsArrayNext(MimeDatabase->filters))
+    {
+      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...
+    */
+
+    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));
+
+    if (pdl[0])
+      pdl[strlen(pdl) - 1] = '\0';     /* Remove trailing comma */
+
+    cupsdSetString(&p->pdl, pdl);
+  }
+#endif /* HAVE_DNSSD */
 }
 
 
@@ -3327,5 +4320,5 @@ write_irix_state(cupsd_printer_t *p)      /* I - Printer to update */
 
 
 /*
- * End of "$Id: printers.c 5970 2006-09-19 20:11:08Z mike $".
+ * End of "$Id: printers.c 7608 2008-05-21 01:37:21Z mike $".
  */