]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/printers.c
Load cups into easysw/current.
[thirdparty/cups.git] / scheduler / printers.c
index 74a893b3c2f1364ca0aa569260744fe7a9c8869a..b3ffa3891b1a3b440221ef75abb07b3da7d8c964 100644 (file)
@@ -1,40 +1,32 @@
 /*
- * "$Id: printers.c 4989 2006-01-26 00:59:45Z mike $"
+ * "$Id: printers.c 6678 2007-07-16 18:03:35Z mike $"
  *
  *   Printer routines for the Common UNIX Printing System (CUPS).
  *
- *   Copyright 1997-2006 by Easy Software Products, all rights reserved.
+ *   Copyright 2007 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.
- *   cupsdAddPrinterFilter()     - Add a MIME filter for a printer.
  *   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.
- *   cupsdDeletePrinterFilters() - Delete all MIME filters for a printer.
  *   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.
  *   cupsdSetPrinterAttrs()      - Set printer attributes based upon the PPD
  *                                 file.
  *   cupsdSetPrinterReasons()    - Set/update the reasons strings.
  *   cupsdWritePrintcap()        - Write a pseudo-printcap file for older
  *                                 applications that need it...
  *   cupsdSanitizeURI()          - Sanitize a device URI...
+ *   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
  */
 
 #include "cupsd.h"
+#include <cups/dir.h>
 
 
 /*
  * Local functions...
  */
 
+static void    add_printer_defaults(cupsd_printer_t *p);
+static void    add_printer_filter(cupsd_printer_t *p, mime_type_t *type,
+                                  const char *filter);
+static void    add_printer_formats(cupsd_printer_t *p);
 static int     compare_printers(void *first, void *second, void *data);
+static void    delete_printer_filters(cupsd_printer_t *p);
 #ifdef __sgi
 static void    write_irix_config(cupsd_printer_t *p);
 static void    write_irix_state(cupsd_printer_t *p);
@@ -113,7 +117,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;
@@ -141,63 +145,6 @@ cupsdAddPrinter(const char *name)  /* I - Name of printer */
 }
 
 
-/*
- * 'cupsdAddPrinterFilter()' - Add a MIME filter for a printer.
- */
-
-void
-cupsdAddPrinterFilter(
-    cupsd_printer_t  *p,               /* I - Printer to add to */
-    const char       *filter)          /* I - Filter to add */
-{
-  char         super[MIME_MAX_SUPER],  /* Super-type for filter */
-               type[MIME_MAX_TYPE],    /* Type for filter */
-               program[1024];          /* Program/filter name */
-  int          cost;                   /* Cost of filter */
-  mime_type_t  *temptype;              /* MIME type looping var */
-
-
- /*
-  * Range check input...
-  */
-
-  if (p == NULL || p->filetype == NULL || filter == NULL)
-    return;
-
- /*
-  * Parse the filter string; it should be in the following format:
-  *
-  *     super/type cost program
-  */
-
-  if (sscanf(filter, "%15[^/]/%31s%d%1023s", super, type, &cost, program) != 4)
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "cupsdAddPrinterFilter: Invalid filter string \"%s\"!",
-                    filter);
-    return;
-  }
-
- /*
-  * Add the filter to the MIME database, supporting wildcards as needed...
-  */
-
-  for (temptype = mimeFirstType(MimeDatabase);
-       temptype;
-       temptype = mimeNextType(MimeDatabase))
-    if (((super[0] == '*' && strcasecmp(temptype->super, "printer")) ||
-         !strcasecmp(temptype->super, super)) &&
-        (type[0] == '*' || !strcasecmp(temptype->type, type)))
-    {
-      cupsdLogMessage(CUPSD_LOG_DEBUG2, "Adding filter %s/%s %s/%s %d %s",
-                      temptype->super, temptype->type,
-                     p->filetype->super, p->filetype->type,
-                      cost, program);
-      mimeAddFilter(MimeDatabase, temptype, p->filetype, cost, program);
-    }
-}
-
-
 /*
  * 'cupsdAddPrinterHistory()' - Add the current printer state to the history.
  */
@@ -242,6 +189,11 @@ cupsdAddPrinterHistory(
   ippAddBoolean(history, IPP_TAG_PRINTER, "printer-is-shared", p->shared);
   ippAddString(history, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-state-message",
                NULL, p->state_message);
+#ifdef __APPLE__
+  if (p->recoverable)
+    ippAddString(history, IPP_TAG_PRINTER, IPP_TAG_TEXT,
+                 "com.apple.print.recoverable-message", NULL, p->recoverable);
+#endif /* __APPLE__ */
   if (p->num_reasons == 0)
     ippAddString(history, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
                  "printer-state-reasons", NULL,
@@ -300,9 +252,14 @@ cupsdCreateCommonData(void)
 {
   int                  i;              /* Looping var */
   ipp_attribute_t      *attr;          /* Attribute data */
+  cups_dir_t           *dir;           /* Notifier directory */
+  cups_dentry_t                *dent;          /* Notifier directory entry */
+  cups_array_t         *notifiers;     /* Notifier array */
+  char                 filename[1024], /* Filename */
+                       *notifier;      /* Current notifier */
   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,
@@ -325,7 +282,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,
@@ -446,23 +403,12 @@ cupsdCreateCommonData(void)
                sizeof(compressions) / sizeof(compressions[0]),
                NULL, compressions);
 
-  /* TODO: move to printer-specific section! */
-  /* copies-default */
-  ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
-                "copies-default", 1);
-
   /* copies-supported */
   ippAddRange(CommonData, IPP_TAG_PRINTER, "copies-supported", 1, MaxCopies);
 
-  /* TODO: move to printer-specific section! */
-  /* document-format-default */
-  ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE,
-               "document-format-default", NULL, "application/octet-stream");
-
-  /* document-format-supported */
-  ippAddStrings(CommonData, IPP_TAG_PRINTER,
-                (ipp_tag_t)(IPP_TAG_MIMETYPE | IPP_TAG_COPY),
-                "document-format-supported", NumMimeTypes, NULL, MimeTypes);
+  /* 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,
@@ -473,21 +419,11 @@ cupsdCreateCommonData(void)
                 "ipp-versions-supported", sizeof(versions) / sizeof(versions[0]),
                NULL, versions);
 
-  /* TODO: move to printer-specific section! */
-  /* job-hold-until-default */
-  ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-               "job-hold-until-default", NULL, "no-hold");
-
   /* job-hold-until-supported */
   ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
                 "job-hold-until-supported", sizeof(holds) / sizeof(holds[0]),
                NULL, holds);
 
-  /* TODO: move to printer-specific section! */
-  /* job-priority-default */
-  ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
-                "job-priority-default", 50);
-
   /* job-priority-supported */
   ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
                 "job-priority-supported", 100);
@@ -509,19 +445,19 @@ cupsdCreateCommonData(void)
 
     if (attr == NULL)
       cupsdLogMessage(CUPSD_LOG_EMERG,
-                      "cupsdSetPrinterAttrs: Unable to allocate memory for "
+                      "Unable to allocate memory for "
                       "job-sheets-supported attribute: %s!", strerror(errno));
     else if (!Classification || ClassifyOverride)
     {
       cupsd_banner_t   *banner;        /* Current banner */
 
 
-      attr->values[0].string.text = strdup("none");
+      attr->values[0].string.text = _cupsStrAlloc("none");
 
       for (i = 1, banner = (cupsd_banner_t *)cupsArrayFirst(Banners);
           banner;
           i ++, banner = (cupsd_banner_t *)cupsArrayNext(Banners))
-       attr->values[i].string.text = strdup(banner->name);
+       attr->values[i].string.text = _cupsStrAlloc(banner->name);
     }
   }
   else
@@ -553,11 +489,6 @@ cupsdCreateCommonData(void)
                (int)(sizeof(notify_attrs) / sizeof(notify_attrs[0])),
                NULL, notify_attrs);
 
-  /* TODO: move to printer-specific section! */
-  /* 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,
@@ -567,11 +498,7 @@ cupsdCreateCommonData(void)
   ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
                "notify-max-events-supported", MaxEvents);
 
-  /* notify-notify-events-default */
-  ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-               "notify-events-default", NULL, "job-completed");
-
-  /* notify-notify-events-supported */
+  /* notify-events-supported */
   ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
                 "notify-events-supported",
                (int)(sizeof(notify_events) / sizeof(notify_events[0])),
@@ -581,15 +508,32 @@ cupsdCreateCommonData(void)
   ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
                "notify-pull-method-supported", NULL, "ippget");
 
-  /* TODO: scan notifier directory */
   /* notify-schemes-supported */
-  ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-               "notify-schemes-supported", NULL, "mailto");
+  snprintf(filename, sizeof(filename), "%s/notifier", ServerBin);
+  if ((dir = cupsDirOpen(filename)) != NULL)
+  {
+    notifiers = cupsArrayNew((cups_array_func_t)strcmp, NULL);
 
-  /* TODO: move to printer-specific section! */
-  /* number-up-default */
-  ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
-                "number-up-default", 1);
+    while ((dent = cupsDirRead(dir)) != NULL)
+      if (S_ISREG(dent->fileinfo.st_mode) &&
+          (dent->fileinfo.st_mode & S_IXOTH) != 0)
+        cupsArrayAdd(notifiers, _cupsStrAlloc(dent->filename));
+
+    if (cupsArrayCount(notifiers) > 0)
+    {
+      attr = ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                          "notify-schemes-supported",
+                          cupsArrayCount(notifiers), NULL, NULL);
+
+      for (i = 0, notifier = (char *)cupsArrayFirst(notifiers);
+           notifier;
+          i ++, notifier = (char *)cupsArrayNext(notifiers))
+       attr->values[i].string.text = notifier;
+    }
+
+    cupsArrayDelete(notifiers);
+    cupsDirClose(dir);
+  }
 
   /* number-up-supported */
   ippAddIntegers(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
@@ -598,16 +542,11 @@ cupsdCreateCommonData(void)
   /* operations-supported */
   ippAddIntegers(CommonData, IPP_TAG_PRINTER, IPP_TAG_ENUM,
                  "operations-supported",
-                 sizeof(ops) / sizeof(ops[0]) + JobFiles - 1, (int *)ops);
-
-  /* TODO: move to printer-specific section! */
-  /* orientation-requested-default */
-  ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_ENUM,
-                "orientation-requested-default", IPP_PORTRAIT);
+                 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);
@@ -625,7 +564,7 @@ cupsdCreateCommonData(void)
   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 = strdup(Policies[i]->name);
+    attr->values[i].string.text = _cupsStrAlloc(Policies[i]->name);
 }
 
 
@@ -693,6 +632,9 @@ cupsdDeletePrinter(
 
   cupsArrayRemove(Printers, p);
 
+  if (p->type & CUPS_PRINTER_IMPLICIT)
+    cupsArrayRemove(ImplicitPrinters, p);
+
  /*
   * Remove the dummy interface/icon/option files under IRIX...
   */
@@ -719,21 +661,46 @@ cupsdDeletePrinter(
 #endif /* __sgi */
 
  /*
-  * If p is the default printer, assign the next one...
-  * TODO: use next network default printer or NULL...
+  * If p is the default printer, assign a different one...
   */
 
   if (p == DefaultPrinter)
-    DefaultPrinter = (cupsd_printer_t *)cupsArrayFirst(Printers);
+  {
+    DefaultPrinter = NULL;
+
+    if (UseNetworkDefault)
+    {
+     /*
+      * Find the first network default printer and use it...
+      */
+
+      cupsd_printer_t  *dp;            /* New default printer */
+
+
+      for (dp = (cupsd_printer_t *)cupsArrayFirst(Printers);
+          dp;
+          dp = (cupsd_printer_t *)cupsArrayNext(Printers))
+       if (dp != p && (dp->type & CUPS_PRINTER_DEFAULT))
+       {
+         DefaultPrinter = dp;
+         break;
+       }
+    }
+  }
 
  /*
-  * 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);
   }
 
  /*
@@ -756,9 +723,10 @@ cupsdDeletePrinter(
 
   ippDelete(p->attrs);
 
-  cupsdDeletePrinterFilters(p);
+  delete_printer_filters(p);
 
   mimeDeleteType(MimeDatabase, p->filetype);
+  mimeDeleteType(MimeDatabase, p->prefiltertype);
 
   cupsdFreePrinterUsers(p);
   cupsdFreeQuotas(p);
@@ -776,50 +744,32 @@ cupsdDeletePrinter(
   cupsdClearString(&p->op_policy);
   cupsdClearString(&p->error_policy);
 
-  free(p);
-
- /*
-  * Restore the previous position in the Printers array...
-  */
-
-  cupsArrayRestore(Printers);
-}
+  cupsdClearString(&p->alert);
+  cupsdClearString(&p->alert_description);
 
+#ifdef HAVE_DNSSD
+  cupsdClearString(&p->product);
+  cupsdClearString(&p->pdl);
+#endif /* HAVE_DNSSD */
 
-/*
- * 'cupsdDeletePrinterFilters()' - Delete all MIME filters for a printer.
- */
+  cupsArrayDelete(p->filetypes);
 
-void
-cupsdDeletePrinterFilters(
-    cupsd_printer_t *p)                        /* I - Printer to remove from */
-{
-  mime_filter_t        *filter;                /* MIME filter looping var */
+  if (p->browse_attrs)
+    free(p->browse_attrs);
 
+#ifdef __APPLE__
+  cupsdClearString(&p->recoverable);
+#endif /* __APPLE__ */
 
- /*
-  * Range check input...
-  */
+  cupsFreeOptions(p->num_options, p->options);
 
-  if (p == NULL)
-    return;
+  free(p);
 
  /*
-  * Remove all filters from the MIME database that have a destination
-  * type == printer...
+  * Restore the previous position in the Printers array...
   */
 
-  for (filter = mimeFirstFilter(MimeDatabase);
-       filter;
-       filter = mimeNextFilter(MimeDatabase))
-    if (filter->dst == p->filetype)
-    {
-     /*
-      * Delete the current filter...
-      */
-
-      mimeDeleteFilter(MimeDatabase, filter);
-    }
+  cupsArrayRestore(Printers);
 }
 
 
@@ -902,8 +852,7 @@ cupsdLoadAllPrinters(void)
   if ((fp = cupsFileOpen(line, "r")) == NULL)
   {
     if (errno != ENOENT)
-      cupsdLogMessage(CUPSD_LOG_ERROR,
-                     "cupsdLoadAllPrinters: Unable to open %s - %s", line,
+      cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open %s - %s", line,
                      strerror(errno));
     return;
   }
@@ -934,8 +883,7 @@ cupsdLoadAllPrinters(void)
         * Add the printer and a base file type...
        */
 
-        cupsdLogMessage(CUPSD_LOG_DEBUG,
-                       "cupsdLoadAllPrinters: Loading printer %s...", value);
+        cupsdLogMessage(CUPSD_LOG_DEBUG, "Loading printer %s...", value);
 
         p = cupsdAddPrinter(value);
        p->accepting = 1;
@@ -1006,6 +954,13 @@ cupsdLoadAllPrinters(void)
                       "Syntax error on line %d of printers.conf.", linenum);
       return;
     }
+    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"))
     {
       if (value)
@@ -1027,6 +982,25 @@ cupsdLoadAllPrinters(void)
        return;
       }
     }
+    else if (!strcasecmp(line, "Option") && value)
+    {
+     /*
+      * Option name 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');
+
+        p->num_options = cupsAddOption(value, valueptr, p->num_options,
+                                      &(p->options));
+      }
+    }
     else if (!strcasecmp(line, "PortMonitor"))
     {
       if (value && strcmp(value, "none"))
@@ -1220,7 +1194,20 @@ cupsdLoadAllPrinters(void)
     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,
@@ -1255,6 +1242,57 @@ cupsdLoadAllPrinters(void)
 }
 
 
+/*
+ * 'cupsdRenamePrinter()' - Rename a printer.
+ */
+
+void
+cupsdRenamePrinter(
+    cupsd_printer_t *p,                        /* I - Printer */
+    const char      *name)             /* I - New name */
+{
+ /*
+  * Remove the printer from the array(s) first...
+  */
+
+  cupsArrayRemove(Printers, p);
+
+  if (p->type & CUPS_PRINTER_IMPLICIT)
+    cupsArrayRemove(ImplicitPrinters, p);
+
+ /*
+  * Rename the printer type...
+  */
+
+  mimeDeleteType(MimeDatabase, p->filetype);
+  p->filetype = mimeAddType(MimeDatabase, "printer", name);
+
+  mimeDeleteType(MimeDatabase, p->prefiltertype);
+  p->prefiltertype = mimeAddType(MimeDatabase, "prefilter", name);
+
+ /*
+  * Rename the printer...
+  */
+
+  cupsdSetString(&p->name, name);
+
+ /*
+  * Reset printer attributes...
+  */
+
+  cupsdSetPrinterAttrs(p);
+
+ /*
+  * Add the printer back to the printer array(s)...
+  */
+
+  cupsArrayAdd(Printers, p);
+
+  if (p->type & CUPS_PRINTER_IMPLICIT)
+    cupsArrayAdd(ImplicitPrinters, p);
+}
+
+
 /*
  * 'cupsdSaveAllPrinters()' - Save all printer definitions to the printers.conf
  *                            file.
@@ -1270,6 +1308,8 @@ cupsdSaveAllPrinters(void)
   cupsd_printer_t      *printer;       /* Current printer class */
   time_t               curtime;        /* Current time */
   struct tm            *curdate;       /* Current date */
+  cups_option_t                *option;        /* Current option */
+  const char           *ptr;           /* Pointer into info/location */
 
 
  /*
@@ -1329,7 +1369,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;
@@ -1343,12 +1383,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);
 
@@ -1391,6 +1468,11 @@ cupsdSaveAllPrinters(void)
     if (printer->error_policy)
       cupsFilePrintf(fp, "ErrorPolicy %s\n", printer->error_policy);
 
+    for (i = printer->num_options, option = printer->options;
+         i > 0;
+        i --, option ++)
+      cupsFilePrintf(fp, "Option %s %s\n", option->name, option->value);
+
     cupsFilePuts(fp, "</Printer>\n");
 
 #ifdef __sgi
@@ -1407,88 +1489,290 @@ cupsdSaveAllPrinters(void)
 
 
 /*
- * 'cupsdSetPrinterAttrs()' - Set printer attributes based upon the PPD file.
+ * 'cupsdSetAuthInfoRequired()' - Set the required authentication info.
  */
 
-void
-cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
+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) */
 {
-  char                 uri[HTTP_MAX_URI];
-                                       /* URI for printer */
-  char                 resource[HTTP_MAX_URI];
-                                       /* Resource portion of URI */
-  int                  i;              /* Looping var */
-  char                 filename[1024]; /* Name of PPD file */
-  int                  num_media;      /* Number of media options */
-  cupsd_location_t     *auth;          /* Pointer to authentication element */
-  const char           *auth_supported;/* Authentication supported */
-  cups_ptype_t         cupsd_printer_type;
-                                       /* Printer type data */
-  ppd_file_t           *ppd;           /* PPD file data */
-  ppd_option_t         *input_slot,    /* InputSlot options */
-                       *media_type,    /* MediaType options */
-                       *page_size,     /* PageSize options */
-                       *output_bin,    /* OutputBin options */
-                       *media_quality; /* EFMediaQualityMode options */
-  ppd_attr_t           *ppdattr;       /* PPD attribute */
-  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 */
-  static const char * const sides[3] = /* sides-supported values */
-               {
-                 "one",
-                 "two-long-edge",
-                 "two-short-edge"
-               };
+  int  i;                              /* Looping var */
 
 
-  DEBUG_printf(("cupsdSetPrinterAttrs: entering name = %s, type = %x\n", p->name,
-                p->type));
+  p->num_auth_info_required = 0;
 
  /*
-  * Make sure that we have the common attributes defined...
+  * Do we have a plain text value?
   */
 
-  if (!CommonData)
-    cupsdCreateCommonData();
+  if (values)
+  {
+   /*
+    * Yes, grab the keywords...
+    */
 
- /*
-  * Clear out old filters, if any...
-  */
+    const char *end;                   /* End of current value */
 
-  cupsdDeletePrinterFilters(p);
 
- /*
-  * Figure out the authentication that is required for the printer.
-  */
+    while (*values && p->num_auth_info_required < 4)
+    {
+      if ((end = strchr(values, ',')) == NULL)
+        end = values + strlen(values);
 
-  auth_supported = "requesting-user-name";
-  if (!(p->type & CUPS_PRINTER_REMOTE))
-  {
-    if (p->type & CUPS_PRINTER_CLASS)
-      snprintf(resource, sizeof(resource), "/classes/%s", p->name);
-    else
-      snprintf(resource, sizeof(resource), "/printers/%s", p->name);
+      if ((end - values) == 4 && !strncmp(values, "none", 4))
+      {
+        if (p->num_auth_info_required != 0 || *end)
+         return (0);
 
-    if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL)
-      auth = cupsdFindPolicyOp(p->op_policy_ptr, IPP_PRINT_JOB);
+        p->auth_info_required[p->num_auth_info_required] = "none";
+       p->num_auth_info_required ++;
 
-    if (auth)
-    {
-      if (auth->type == AUTH_BASIC || auth->type == AUTH_BASICDIGEST)
-       auth_supported = "basic";
-      else if (auth->type == AUTH_DIGEST)
-       auth_supported = "digest";
+       return (1);
+      }
+      else if ((end - values) == 9 && !strncmp(values, "negotiate", 9))
+      {
+        if (p->num_auth_info_required != 0 || *end)
+         return (0);
 
-      if (auth->type != AUTH_NONE)
-        p->type |= CUPS_PRINTER_AUTHENTICATED;
+        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
-        p->type &= ~CUPS_PRINTER_AUTHENTICATED;
+        return (0);
+
+      values = (*end) ? end + 1 : end;
     }
-    else
-      p->type &= ~CUPS_PRINTER_AUTHENTICATED;
-  }
+
+    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);
+}
+
+
+/*
+ * 'cupsdSetPrinterAttrs()' - Set printer attributes based upon the PPD file.
+ */
+
+void
+cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
+{
+  int          i,                      /* Looping var */
+               length;                 /* Length of browse attributes */
+  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 */
+  ppd_file_t   *ppd;                   /* PPD file data */
+  ppd_option_t *input_slot,            /* InputSlot options */
+               *media_type,            /* MediaType options */
+               *page_size,             /* PageSize options */
+               *output_bin,            /* OutputBin options */
+               *media_quality,         /* EFMediaQualityMode options */
+               *duplex;                /* Duplex options */
+  ppd_attr_t   *ppdattr;               /* PPD attribute */
+  ipp_attribute_t *attr;               /* Attribute data */
+  ipp_value_t  *val;                   /* Attribute value */
+  int          num_finishings;         /* Number of finishings */
+  int          finishings[5];          /* finishings-supported values */
+  cups_option_t        *option;                /* Current printer option */
+  static const char * const sides[3] = /* sides-supported values */
+               {
+                 "one-sided",
+                 "two-sided-long-edge",
+                 "two-sided-short-edge"
+               };
+  static const char * const air_userpass[] =
+               {                       /* Basic/Digest authentication */
+                 "username",
+                 "password"
+               };
+  static const char * const air_negotiate[] =
+               {                       /* Kerberos authentication */
+                 "negotiate"
+               };
+  static const char * const air_none[] =
+               {                       /* No authentication */
+                 "none"
+               };
+
+
+  DEBUG_printf(("cupsdSetPrinterAttrs: entering name = %s, type = %x\n", p->name,
+                p->type));
+
+ /*
+  * Make sure that we have the common attributes defined...
+  */
+
+  if (!CommonData)
+    cupsdCreateCommonData();
+
+ /*
+  * Clear out old filters, if any...
+  */
+
+  delete_printer_filters(p);
+
+ /*
+  * Figure out the authentication that is required for the printer.
+  */
+
+  auth_supported = "requesting-user-name";
+  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);
+    else
+      snprintf(resource, sizeof(resource), "/printers/%s", p->name);
+
+    if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL ||
+        auth->type == AUTH_NONE)
+      auth = cupsdFindPolicyOp(p->op_policy_ptr, IPP_PRINT_JOB);
+
+    if (auth)
+    {
+      if (auth->type == AUTH_BASIC || auth->type == AUTH_BASICDIGEST)
+      {
+       auth_supported = "basic";
+       num_air        = 2;
+       air            = air_userpass;
+      }
+      else if (auth->type == AUTH_DIGEST)
+      {
+       auth_supported = "digest";
+       num_air        = 2;
+       air            = air_userpass;
+      }
+#ifdef HAVE_GSSAPI
+      else if (auth->type == AUTH_NEGOTIATE)
+      {
+       auth_supported = "negotiate";
+       num_air        = 1;
+       air            = air_negotiate;
+      }
+#endif /* HAVE_GSSAPI */
+
+      if (auth->type != AUTH_NONE)
+        p->type |= CUPS_PRINTER_AUTHENTICATED;
+      else
+        p->type &= ~CUPS_PRINTER_AUTHENTICATED;
+    }
+    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...
@@ -1530,8 +1814,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...
@@ -1542,18 +1828,17 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
 
     if (attr != NULL)
     {
-      attr->values[0].string.text = strdup(Classification ?
+      attr->values[0].string.text = _cupsStrAlloc(Classification ?
                                           Classification : p->job_sheets[0]);
-      attr->values[1].string.text = strdup(Classification ?
+      attr->values[1].string.text = _cupsStrAlloc(Classification ?
                                           Classification : p->job_sheets[1]);
     }
   }
 
-  cupsd_printer_type = p->type;
-
-  p->raw = 0;
+  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...
@@ -1569,7 +1854,8 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
     ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "device-uri", NULL,
                 p->uri);
 
-    p->raw = 1;
+    p->raw    = 1;
+    p->remote = 1;
   }
   else
   {
@@ -1612,7 +1898,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 = strdup(p->printers[i]->uri);
+            attr->values[i].string.text = _cupsStrAlloc(p->printers[i]->uri);
 
          p->type &= ~CUPS_PRINTER_OPTIONS | p->printers[i]->type;
         }
@@ -1623,7 +1909,7 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
        if (attr != NULL)
        {
          for (i = 0; i < p->num_printers; i ++)
-            attr->values[i].string.text = strdup(p->printers[i]->name);
+            attr->values[i].string.text = _cupsStrAlloc(p->printers[i]->name);
         }
       }
     }
@@ -1690,15 +1976,29 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
                        "pages-per-minute", ppd->throughput);
 
         if (ppd->nickname)
+       {
+        /*
+         * The NickName can be localized in the character set specified
+         * by the LanugageEncoding attribute.  However, ppdOpen2() has
+         * already converted the ppd->nickname member to UTF-8 for us
+         * (the original attribute value is available separately)
+         */
+
           cupsdSetString(&p->make_model, ppd->nickname);
+       }
        else if (ppd->modelname)
+       {
+        /*
+         * Model name can only contain specific characters...
+         */
+
           cupsdSetString(&p->make_model, ppd->modelname);
+       }
        else
          cupsdSetString(&p->make_model, "Bad PPD File");
 
-        if (p->make_model)
-         ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
-                       "printer-make-and-model", NULL, p->make_model);
+       ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
+                     "printer-make-and-model", NULL, p->make_model);
 
        /*
        * Add media options from the PPD file...
@@ -1720,9 +2020,9 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
 
         if (num_media == 0)
        {
-         cupsdLogMessage(CUPSD_LOG_CRIT, "cupsdSetPrinterAttrs: The PPD file for printer %s "
-                            "contains no media options and is therefore "
-                            "invalid!", p->name);
+         cupsdLogMessage(CUPSD_LOG_CRIT,
+                         "The PPD file for printer %s contains no media "
+                         "options and is therefore invalid!", p->name);
        }
        else
        {
@@ -1734,36 +2034,36 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
 
            if (input_slot != NULL)
              for (i = 0; i < input_slot->num_choices; i ++, val ++)
-               val->string.text = strdup(input_slot->choices[i].choice);
+               val->string.text = _cupsStrAlloc(input_slot->choices[i].choice);
 
            if (media_type != NULL)
              for (i = 0; i < media_type->num_choices; i ++, val ++)
-               val->string.text = strdup(media_type->choices[i].choice);
+               val->string.text = _cupsStrAlloc(media_type->choices[i].choice);
 
            if (media_quality != NULL)
              for (i = 0; i < media_quality->num_choices; i ++, val ++)
-               val->string.text = strdup(media_quality->choices[i].choice);
+               val->string.text = _cupsStrAlloc(media_quality->choices[i].choice);
 
            if (page_size != NULL)
            {
              for (i = 0; i < page_size->num_choices; i ++, val ++)
-               val->string.text = strdup(page_size->choices[i].choice);
+               val->string.text = _cupsStrAlloc(page_size->choices[i].choice);
 
-             ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default",
-                          NULL, page_size->defchoice);
+             ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                          "media-default", NULL, page_size->defchoice);
             }
            else if (input_slot != NULL)
-             ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default",
-                          NULL, input_slot->defchoice);
+             ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                          "media-default", NULL, input_slot->defchoice);
            else if (media_type != NULL)
-             ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default",
-                          NULL, media_type->defchoice);
+             ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                          "media-default", NULL, media_type->defchoice);
            else if (media_quality != NULL)
-             ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default",
-                          NULL, media_quality->defchoice);
+             ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                          "media-default", NULL, media_quality->defchoice);
            else
-             ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default",
-                          NULL, "none");
+             ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                          "media-default", NULL, "none");
           }
         }
 
@@ -1782,7 +2082,7 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
            for (i = 0, val = attr->values;
                 i < output_bin->num_choices;
                 i ++, val ++)
-             val->string.text = strdup(output_bin->choices[i].choice);
+             val->string.text = _cupsStrAlloc(output_bin->choices[i].choice);
           }
        }
 
@@ -1790,14 +2090,28 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
         * Duplexing, etc...
        */
 
-       if (ppdFindOption(ppd, "Duplex") != NULL)
+       if ((duplex = ppdFindOption(ppd, "Duplex")) == NULL)
+         if ((duplex = ppdFindOption(ppd, "EFDuplex")) == NULL)
+           if ((duplex = ppdFindOption(ppd, "EFDuplexing")) == NULL)
+             if ((duplex = ppdFindOption(ppd, "KD03Duplex")) == NULL)
+               duplex = ppdFindOption(ppd, "JCLDuplex");
+
+       if (duplex && duplex->num_choices > 1)
        {
          p->type |= CUPS_PRINTER_DUPLEX;
 
-         ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "sides-supported",
-                       3, NULL, sides);
-         ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "sides-default",
-                       NULL, "one");
+         ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                       "sides-supported", 3, NULL, sides);
+
+          if (!strcasecmp(duplex->defchoice, "DuplexTumble"))
+           ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                        "sides-default", NULL, "two-sided-short-edge");
+          else if (!strcasecmp(duplex->defchoice, "DuplexNoTumble"))
+           ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                        "sides-default", NULL, "two-sided-long-edge");
+         else
+           ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                        "sides-default", NULL, "one-sided");
        }
 
        if (ppdFindOption(ppd, "Collate") != NULL)
@@ -1828,7 +2142,20 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
        * handle "raw" printing by users.
        */
 
-        cupsdAddPrinterFilter(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...
@@ -1838,7 +2165,7 @@ 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]));
-          cupsdAddPrinterFilter(p, ppd->filters[i]);
+          add_printer_filter(p, p->filetype, ppd->filters[i]);
        }
 
        if (ppd->num_filters == 0)
@@ -1847,17 +2174,17 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
          * If there are no filters, add a PostScript printing filter.
          */
 
-          cupsdAddPrinterFilter(p, "application/vnd.cups-postscript 0 -");
+          add_printer_filter(p, p->filetype,
+                            "application/vnd.cups-postscript 0 -");
         }
 
        /*
        * 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));
@@ -1870,31 +2197,36 @@ 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 = strdup("none");
+        attr->values[0].string.text = _cupsStrAlloc("none");
 
         for (i = 1, ppdattr = ppdFindAttr(ppd, "cupsPortMonitor", NULL);
             ppdattr;
             i ++, ppdattr = ppdFindNextAttr(ppd, "cupsPortMonitor", NULL))
-         attr->values[i].string.text = strdup(ppdattr->value);
+         attr->values[i].string.text = _cupsStrAlloc(ppdattr->value);
 
         if (ppd->protocols)
        {
          if (strstr(ppd->protocols, "TBCP"))
-           attr->values[i].string.text = strdup("tbcp");
+           attr->values[i].string.text = _cupsStrAlloc("tbcp");
          else if (strstr(ppd->protocols, "BCP"))
-           attr->values[i].string.text = strdup("bcp");
+           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);
-
-        cupsd_printer_type = p->type;
       }
       else if (!access(filename, 0))
       {
@@ -1904,29 +2236,32 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
 
         pstatus = ppdLastError(&pline);
 
-       cupsdLogMessage(CUPSD_LOG_ERROR, "PPD file for %s cannot be loaded!", p->name);
+       cupsdLogMessage(CUPSD_LOG_ERROR, "PPD file for %s cannot be loaded!",
+                       p->name);
 
        if (pstatus <= PPD_ALLOC_ERROR)
          cupsdLogMessage(CUPSD_LOG_ERROR, "%s", strerror(errno));
         else
-         cupsdLogMessage(CUPSD_LOG_ERROR, "%s on line %d.", ppdErrorString(pstatus),
-                    pline);
+         cupsdLogMessage(CUPSD_LOG_ERROR, "%s on line %d.",
+                         ppdErrorString(pstatus), pline);
 
-        cupsdLogMessage(CUPSD_LOG_INFO, "Hint: Run \"cupstestppd %s\" and fix any errors.",
-                  filename);
+        cupsdLogMessage(CUPSD_LOG_INFO,
+                       "Hint: Run \"cupstestppd %s\" and fix any errors.",
+                       filename);
 
        /*
        * Add a filter from application/vnd.cups-raw to printer/name to
        * handle "raw" printing by users.
        */
 
-        cupsdAddPrinterFilter(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.
        */
 
-       cupsdAddPrinterFilter(p, "application/vnd.cups-postscript 0 -");
+       add_printer_filter(p, p->filetype,
+                          "application/vnd.cups-postscript 0 -");
       }
       else
       {
@@ -1936,18 +2271,19 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
 
        snprintf(filename, sizeof(filename), "%s/interfaces/%s", ServerRoot,
                 p->name);
-       if (access(filename, X_OK) == 0)
+       if (!access(filename, X_OK))
        {
         /*
          * Yes, we have a System V style interface script; use it!
          */
 
          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);
-         cupsdAddPrinterFilter(p, filename);
+         add_printer_filter(p, p->filetype, filename);
        }
        else if (p->device_uri &&
                 !strncmp(p->device_uri, "ipp://", 6) &&
@@ -1958,7 +2294,7 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
          * Tell the client this is really a hard-wired remote printer.
          */
 
-          cupsd_printer_type |= CUPS_PRINTER_REMOTE;
+          p->type |= CUPS_PRINTER_REMOTE;
 
          /*
          * Point the printer-uri-supported attribute to the
@@ -1979,7 +2315,8 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
          * Print all files directly...
          */
 
-         p->raw = 1;
+         p->raw    = 1;
+         p->remote = 1;
        }
        else
        {
@@ -1996,27 +2333,147 @@ 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);
     }
   }
 
  /*
-  * Add the CUPS-specific printer-type attribute...
+  * Force sharing off for remote queues...
   */
 
-  if (!p->shared)
-    p->type |= CUPS_PRINTER_NOT_SHARED;
+  if (p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT))
+    p->shared = 0;
   else
-    p->type &= ~CUPS_PRINTER_NOT_SHARED;
+  {
+   /*
+    * 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 */
+
+
+   /*
+    * Free the old browse attributes as needed...
+    */
+
+    if (p->browse_attrs)
+      free(p->browse_attrs);
+
+   /*
+    * Compute the length of all attributes + job-sheets, lease-duration,
+    * and BrowseLocalOptions.
+    */
+
+    for (length = 1, i = p->num_options, option = p->options;
+         i > 0;
+        i --, option ++)
+    {
+      length += strlen(option->name) + 2;
+
+      if (option->value)
+      {
+        for (valptr = option->value; *valptr; valptr ++)
+         if (strchr(" \"\'\\", *valptr))
+           length += 2;
+         else
+           length ++;
+      }
+    }
+
+    length += 13 + strlen(p->job_sheets[0]) + strlen(p->job_sheets[1]);
+    length += 32;
+    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...
+    */
+
+    if ((p->browse_attrs = calloc(1, length)) == NULL)
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                      "Unable to allocate %d bytes for browse data!",
+                     length);
+    else
+    {
+     /*
+      * Got the allocated string, now copy the options and attributes over...
+      */
+
+      sprintf(p->browse_attrs, "job-sheets=%s,%s lease-duration=%d",
+              p->job_sheets[0], p->job_sheets[1], BrowseTimeout);
+      attrptr = p->browse_attrs + strlen(p->browse_attrs);
+
+      if (BrowseLocalOptions)
+      {
+        sprintf(attrptr, " ipp-options=%s", BrowseLocalOptions);
+        attrptr += strlen(attrptr);
+      }
+
+      for (i = p->num_options, option = p->options;
+           i > 0;
+          i --, option ++)
+      {
+        *attrptr++ = ' ';
+       strcpy(attrptr, option->name);
+       attrptr += strlen(attrptr);
+
+       if (option->value)
+       {
+         *attrptr++ = '=';
+
+          for (valptr = option->value; *valptr; valptr ++)
+         {
+           if (strchr(" \"\'\\", *valptr))
+             *attrptr++ = '\\';
+
+           *attrptr++ = *valptr;
+         }
+       }
+      }
+
+      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';
+    }
+  }
+
+ /*
+  * Populate the document-format-supported attribute...
+  */
 
-  ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-type",
-                cupsd_printer_type);
+  add_printer_formats(p);
 
   DEBUG_printf(("cupsdSetPrinterAttrs: leaving name = %s, type = %x\n", p->name,
                 p->type));
 
+ /*
+  * Add name-default attributes...
+  */
+
+  add_printer_defaults(p);
+
 #ifdef __sgi
  /*
   * Write the IRIX printer config and status files...
@@ -2025,6 +2482,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);
 }
 
 
@@ -2065,6 +2528,9 @@ cupsdSetPrinterReasons(
     p->num_reasons = 0;
   }
 
+  if (!strcmp(s, "none"))
+    return;
+
  /*
   * Loop through all of the reasons...
   */
@@ -2108,6 +2574,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])))
@@ -2124,6 +2593,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);
       }
     }
   }
@@ -2147,7 +2619,7 @@ cupsdSetPrinterState(
   * Can't set status of remote printers...
   */
 
-  if (p->type & CUPS_PRINTER_REMOTE)
+  if (p->type & CUPS_PRINTER_DISCOVERED)
     return;
 
  /*
@@ -2159,6 +2631,11 @@ cupsdSetPrinterState(
 
   if (old_state != s)
   {
+    cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE_CHANGED, p, NULL,
+                 "%s \"%s\" state changed.",
+                 (p->type & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
+                 p->name);
+
    /*
     * Let the browse code know this needs to be updated...
     */
@@ -2174,6 +2651,12 @@ cupsdSetPrinterState(
 
   cupsdAddPrinterHistory(p);
 
+ /*
+  * Let the browse protocols reflect the change...
+  */
+
+  cupsdRegisterPrinter(p);
+
  /*
   * Save the printer configuration if a printer goes from idle or processing
   * to stopped (or visa-versa)...
@@ -2230,8 +2713,12 @@ cupsdStopPrinter(cupsd_printer_t *p,     /* I - Printer to stop */
     */
 
     job->state->values[0].integer = IPP_JOB_PENDING;
+    job->state_value              = IPP_JOB_PENDING;
 
     cupsdSaveJob(job);
+
+    cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, p, job,
+                 "Job stopped due to printer being paused");
   }
 }
 
@@ -2257,7 +2744,12 @@ cupsdUpdatePrinters(void)
        p;
        p = (cupsd_printer_t *)cupsArrayNext(Printers))
   {
-    if (!Browsing && (p->type & (CUPS_PRINTER_IMPLICIT | CUPS_PRINTER_REMOTE)))
+   /*
+    * Remove remote printers if we are no longer browsing...
+    */
+
+    if (!Browsing &&
+        (p->type & (CUPS_PRINTER_IMPLICIT | CUPS_PRINTER_DISCOVERED)))
     {
       if (p->type & CUPS_PRINTER_IMPLICIT)
         cupsArrayRemove(ImplicitPrinters, p);
@@ -2267,8 +2759,6 @@ cupsdUpdatePrinters(void)
       cupsArrayRestore(Printers);
       continue;
     }
-    else if (!(p->type & CUPS_PRINTER_REMOTE))
-      cupsdSetPrinterAttrs(p);
 
    /*
     * Update the operation policy pointer...
@@ -2276,6 +2766,13 @@ cupsdUpdatePrinters(void)
 
     if ((p->op_policy_ptr = cupsdFindPolicy(p->op_policy)) == NULL)
       p->op_policy_ptr = DefaultPolicyPtr;
+
+   /*
+    * Update printer attributes as needed...
+    */
+
+    if (!(p->type & CUPS_PRINTER_DISCOVERED))
+      cupsdSetPrinterAttrs(p);
   }
 }
 
@@ -2286,18 +2783,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 */
+                       *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(\"%s\", \"%s\", %p, %p)\n", hostname, resource,
+  DEBUG_printf(("cupsdValidateDest(uri=\"%s\", dtype=%p, printer=%p)\n", uri,
                 dtype, printer));
 
  /*
@@ -2307,7 +2811,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...
@@ -2319,7 +2832,7 @@ cupsdValidateDest(
     * Class...
     */
 
-    resource += 9;
+    rptr = resource + 9;
   }
   else if (!strncmp(resource, "/printers/", 10))
   {
@@ -2327,7 +2840,7 @@ cupsdValidateDest(
     * Printer...
     */
 
-    resource += 10;
+    rptr = resource + 10;
   }
   else
   {
@@ -2342,17 +2855,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);
   }
 
@@ -2361,7 +2876,7 @@ cupsdValidateDest(
   */
 
   if (!strcasecmp(hostname, "localhost"))
-    hostname = ServerName;
+    strlcpy(hostname, ServerName, sizeof(hostname));
 
   strlcpy(localname, hostname, sizeof(localname));
 
@@ -2403,13 +2918,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);
     }
 
@@ -2612,6 +3129,333 @@ cupsdSanitizeURI(const char *uri,       /* I - Original device URI */
 }
 
 
+/*
+ * 'add_printer_defaults()' - Add name-default attributes to the printer attributes.
+ */
+
+static void
+add_printer_defaults(cupsd_printer_t *p)/* I - Printer */
+{
+  int          i;                      /* Looping var */
+  int          num_options;            /* Number of default options */
+  cups_option_t        *options,               /* Default options */
+               *option;                /* Current option */
+  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...
+  */
+
+  for (num_options = 0, i = p->num_options, option = p->options;
+       i > 0;
+       i --, option ++)
+  {
+    if (strcmp(option->name, "ipp-options") &&
+       strcmp(option->name, "job-sheets") &&
+        strcmp(option->name, "lease-duration"))
+    {
+      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));
+    }
+  }
+
+ /*
+  * Convert options to IPP attributes...
+  */
+
+  cupsEncodeOptions2(p->attrs, num_options, options, IPP_TAG_PRINTER);
+  cupsFreeOptions(num_options, options);
+
+ /*
+  * Add standard -default attributes as needed...
+  */
+
+  if (!cupsGetOption("copies", p->num_options, p->options))
+    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");
+
+  if (!cupsGetOption("job-priority", p->num_options, p->options))
+    ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                  "job-priority-default", 50);
+
+  if (!cupsGetOption("number-up", p->num_options, p->options))
+    ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                  "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);
+
+  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");
+}
+
+
+/*
+ * 'add_printer_filter()' - Add a MIME filter for a 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 */
+               type[MIME_MAX_TYPE],    /* Type for 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 */
+
+
+ /*
+  * Parse the filter string; it should be in the following format:
+  *
+  *     super/type cost program
+  */
+
+  if (sscanf(filter, "%15[^/]/%31s%d%1023s", super, type, &cost, program) != 4)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "%s: invalid filter string \"%s\"!",
+                    p->name, filter);
+    return;
+  }
+
+ /*
+  * See if the filter program exists; if not, stop the printer and flag
+  * the error!
+  */
+
+  if (strcmp(program, "-"))
+  {
+    if (program[0] == '/')
+      strlcpy(filename, program, sizeof(filename));
+    else
+      snprintf(filename, sizeof(filename), "%s/filter/%s", ServerBin, program);
+
+    if (access(filename, X_OK))
+    {
+      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);
+
+      cupsdLogMessage(CUPSD_LOG_ERROR, "%s", p->state_message);
+    }
+  }
+
+ /*
+  * Mark the CUPS_PRINTER_COMMANDS bit if we have a filter for
+  * application/vnd.cups-command...
+  */
+
+  if (!strcasecmp(super, "application") &&
+      !strcasecmp(type, "vnd.cups-command"))
+    p->type |= CUPS_PRINTER_COMMANDS;
+
+ /*
+  * Add the filter to the MIME database, supporting wildcards as needed...
+  */
+
+  for (temptype = mimeFirstType(MimeDatabase);
+       temptype;
+       temptype = mimeNextType(MimeDatabase))
+    if (((super[0] == '*' && strcasecmp(temptype->super, "printer")) ||
+         !strcasecmp(temptype->super, super)) &&
+        (type[0] == '*' || !strcasecmp(temptype->type, type)))
+    {
+      cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                      "add_printer_filter: %s: adding filter %s/%s %s/%s %d %s",
+                      p->name, temptype->super, temptype->type,
+                     filtertype->super, filtertype->type,
+                      cost, program);
+      mimeAddFilter(MimeDatabase, temptype, filtertype, cost, program);
+    }
+}
+
+
+/*
+ * 'add_printer_formats()' - Add document-format-supported values for a printer.
+ */
+
+static void
+add_printer_formats(cupsd_printer_t *p)        /* I - Printer */
+{
+  int          i;                      /* Looping var */
+  mime_type_t  *type;                  /* Current MIME type */
+  cups_array_t *filters;               /* Filters */
+  ipp_attribute_t *attr;               /* document-format-supported attribute */
+  char         mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
+                                       /* MIME type name */
+
+
+ /*
+  * Raw (and remote) queues advertise all of the supported MIME
+  * types...
+  */
+
+  cupsArrayDelete(p->filetypes);
+  p->filetypes = NULL;
+
+  if (p->raw)
+  {
+    ippAddStrings(p->attrs, IPP_TAG_PRINTER,
+                  (ipp_tag_t)(IPP_TAG_MIMETYPE | IPP_TAG_COPY),
+                  "document-format-supported", NumMimeTypes, NULL, MimeTypes);
+    return;
+  }
+
+ /*
+  * Otherwise, loop through the supported MIME types and see if there
+  * are filters for them...
+  */
+
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_printer_formats: %d types, %d filters",
+                  mimeNumTypes(MimeDatabase), mimeNumFilters(MimeDatabase));
+
+  p->filetypes = cupsArrayNew(NULL, NULL);
+
+  for (type = mimeFirstType(MimeDatabase);
+       type;
+       type = mimeNextType(MimeDatabase))
+  {
+    snprintf(mimetype, sizeof(mimetype), "%s/%s", type->super, type->type);
+
+    if ((filters = mimeFilter(MimeDatabase, type, p->filetype, NULL)) != NULL)
+    {
+      cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                      "add_printer_formats: %s: %s needs %d filters",
+                      p->name, mimetype, cupsArrayCount(filters));
+
+      cupsArrayDelete(filters);
+      cupsArrayAdd(p->filetypes, type);
+    }
+    else
+      cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                      "add_printer_formats: %s: %s not supported",
+                      p->name, mimetype);
+  }
+
+  cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                  "add_printer_formats: %s: %d supported types",
+                 p->name, cupsArrayCount(p->filetypes) + 1);
+
+ /*
+  * Add the file formats that can be filtered...
+  */
+
+  if ((type = mimeType(MimeDatabase, "application", "octet-stream")) == NULL ||
+      !cupsArrayFind(p->filetypes, type))
+    i = 1;
+  else
+    i = 0;
+
+  attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE,
+                       "document-format-supported",
+                      cupsArrayCount(p->filetypes) + 1, NULL, NULL);
+
+  if (i)
+    attr->values[0].string.text = _cupsStrAlloc("application/octet-stream");
+
+  for (type = (mime_type_t *)cupsArrayFirst(p->filetypes);
+       type;
+       i ++, type = (mime_type_t *)cupsArrayNext(p->filetypes))
+  {
+    snprintf(mimetype, sizeof(mimetype), "%s/%s", type->super, type->type);
+
+    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 */
+}
+
+
 /*
  * 'compare_printers()' - Compare two printers.
  */
@@ -2626,6 +3470,43 @@ compare_printers(void *first,            /* I - First printer */
 }
 
 
+/*
+ * 'delete_printer_filters()' - Delete all MIME filters for a printer.
+ */
+
+static void
+delete_printer_filters(
+    cupsd_printer_t *p)                        /* I - Printer to remove from */
+{
+  mime_filter_t        *filter;                /* MIME filter looping var */
+
+
+ /*
+  * Range check input...
+  */
+
+  if (p == NULL)
+    return;
+
+ /*
+  * Remove all filters from the MIME database that have a destination
+  * type == printer...
+  */
+
+  for (filter = mimeFirstFilter(MimeDatabase);
+       filter;
+       filter = mimeNextFilter(MimeDatabase))
+    if (filter->dst == p->filetype)
+    {
+     /*
+      * Delete the current filter...
+      */
+
+      mimeDeleteFilter(MimeDatabase, filter);
+    }
+}
+
+
 #ifdef __sgi
 /*
  * 'write_irix_config()' - Update the config files used by the IRIX
@@ -2637,7 +3518,7 @@ write_irix_config(cupsd_printer_t *p)     /* I - Printer to update */
 {
   char         filename[1024];         /* Interface script filename */
   cups_file_t  *fp;                    /* Interface script file */
-
+  ipp_attribute_t *attr;               /* Attribute data */
 
 
  /*
@@ -2882,5 +3763,5 @@ write_irix_state(cupsd_printer_t *p)      /* I - Printer to update */
 
 
 /*
- * End of "$Id: printers.c 4989 2006-01-26 00:59:45Z mike $".
+ * End of "$Id: printers.c 6678 2007-07-16 18:03:35Z mike $".
  */