]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/dirsvc.c
Load cups into easysw/current.
[thirdparty/cups.git] / scheduler / dirsvc.c
index 9cb76dfd4bee4eb14ad70f772efc80dd93d9c4b3..1c0be6a3b13ce5f1d85263a8120728cc2bf945e7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * "$Id: dirsvc.c 5223 2006-03-04 01:10:17Z mike $"
+ * "$Id: dirsvc.c 5305 2006-03-18 03:05:12Z mike $"
  *
  *   Directory services routines for the Common UNIX Printing System (CUPS).
  *
@@ -28,9 +28,6 @@
  *   cupsdSendBrowseDelete()       - Send a "browse delete" message for a
  *                                   printer.
  *   cupsdSendBrowseList()         - Send new browsing information as necessary.
- *   cupsdSendCUPSBrowse()         - Send new browsing information using the
- *                                   CUPS protocol.
- *   cupsdSendSLPBrowse()          - Register the specified printer with SLP.
  *   cupsdStartBrowsing()          - Start sending and receiving broadcast
  *                                   information.
  *   cupsdStartPolling()           - Start polling servers as needed.
  *   dequote()                     - Remote quotes from a string.
  *   process_browse_data()         - Process new browse data.
  *   process_implicit_classes()    - Create/update implicit classes as needed.
+ *   send_cups_browse()            - Send new browsing information using the
+ *                                   CUPS protocol.
+ *   send_ldap_browse()            - Send LDAP printer registrations.
+ *   send_slp_browse()             - Register the specified printer with SLP.
  *   slp_attr_callback()           - SLP attribute callback 
  *   slp_dereg_printer()           - SLPDereg() the specified printer
  *   slp_get_attr()                - Get an attribute from an SLP registration.
@@ -72,6 +73,11 @@ static void  process_browse_data(const char *uri, const char *host,
                                    const char *info, const char *make_model,
                                    int num_attrs, cups_option_t *attrs);
 static void    process_implicit_classes(void);
+static void    send_cups_browse(cupsd_printer_t *p);
+#ifdef HAVE_LDAP
+static void    send_ldap_browse(cupsd_printer_t *p);
+#endif /* HAVE_LDAP */
+static void    send_slp_browse(cupsd_printer_t *p);
 
 
 #ifdef HAVE_OPENLDAP
@@ -659,7 +665,7 @@ cupsdSendBrowseDelete(
   */
 
   if (BrowseLocalProtocols & BROWSE_CUPS)
-    cupsdSendCUPSBrowse(p);
+    send_cups_browse(p);
 #ifdef HAVE_LIBSLP
   if (BrowseLocalProtocols & BROWSE_SLP)
     slp_dereg_printer(p);
@@ -750,16 +756,16 @@ cupsdSendBrowseList(void)
        p->browse_time = time(NULL);
 
        if (BrowseLocalProtocols & BROWSE_CUPS)
-          cupsdSendCUPSBrowse(p);
+          send_cups_browse(p);
 
 #ifdef HAVE_LIBSLP
        if (BrowseLocalProtocols & BROWSE_SLP)
-          cupsdSendSLPBrowse(p);
+          send_slp_browse(p);
 #endif /* HAVE_LIBSLP */
 
 #ifdef HAVE_LDAP
        if (BrowseLocalProtocols & BROWSE_LDAP)
-          cupsdSendLDAPBrowse(p);
+          send_ldap_browse(p);
 #endif /* HAVE_LDAP */
       }
     }
@@ -807,1591 +813,1108 @@ cupsdSendBrowseList(void)
 
 
 /*
- * 'cupsdSendCUPSBrowse()' - Send new browsing information using the CUPS
- *                           protocol.
+ * 'cupsdStartBrowsing()' - Start sending and receiving broadcast information.
  */
 
 void
-cupsdSendCUPSBrowse(cupsd_printer_t *p)        /* I - Printer to send */
+cupsdStartBrowsing(void)
 {
-  int                  i;              /* Looping var */
-  cups_ptype_t         type;           /* Printer type */
-  cupsd_dirsvc_addr_t  *b;             /* Browse address */
-  int                  bytes;          /* Length of packet */
-  char                 packet[1453],   /* Browse data packet */
-                       uri[1024],      /* Printer URI */
-                       location[1024], /* printer-location */
-                       info[1024],     /* printer-info */
-                       make_model[1024];
-                                       /* printer-make-and-model */
-  cupsd_netif_t                *iface;         /* Network interface */
-
-
- /*
-  * Figure out the printer type value...
-  */
-
-  type = p->type | CUPS_PRINTER_REMOTE;
+  int                  val;            /* Socket option value */
+  struct sockaddr_in   addr;           /* Broadcast address */
 
-  if (!p->accepting)
-    type |= CUPS_PRINTER_REJECTING;
 
-  if (p == DefaultPrinter)
-    type |= CUPS_PRINTER_DEFAULT;
+  BrowseNext = NULL;
 
- /*
-  * Remove quotes from printer-info, printer-location, and
-  * printer-make-and-model attributes...
-  */
+  if (!Browsing || !(BrowseLocalProtocols | BrowseRemoteProtocols))
+    return;
 
-  dequote(location, p->location, sizeof(location));
-  dequote(info, p->info, sizeof(info));
-  dequote(make_model, p->make_model ? p->make_model : "Unknown",
-          sizeof(make_model));
+  if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_CUPS)
+  {
+    if (BrowseSocket < 0)
+    {
+     /*
+      * Create the broadcast socket...
+      */
 
- /*
-  * Send a packet to each browse address...
-  */
+      if ((BrowseSocket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+      {
+       cupsdLogMessage(CUPSD_LOG_ERROR,
+                       "cupsdStartBrowsing: Unable to create broadcast "
+                       "socket - %s.", strerror(errno));
+       BrowseLocalProtocols &= ~BROWSE_CUPS;
+       BrowseRemoteProtocols &= ~BROWSE_CUPS;
+       return;
+      }
 
-  for (i = NumBrowsers, b = Browsers; i > 0; i --, b ++)
-    if (b->iface[0])
-    {
      /*
-      * Send the browse packet to one or more interfaces...
+      * Bind the socket to browse port...
       */
 
-      if (!strcmp(b->iface, "*"))
+      memset(&addr, 0, sizeof(addr));
+      addr.sin_addr.s_addr = htonl(INADDR_ANY);
+      addr.sin_family      = AF_INET;
+      addr.sin_port        = htons(BrowsePort);
+
+      if (bind(BrowseSocket, (struct sockaddr *)&addr, sizeof(addr)))
       {
-       /*
-        * Send to all local interfaces...
-       */
+       cupsdLogMessage(CUPSD_LOG_ERROR,
+                       "cupsdStartBrowsing: Unable to bind broadcast "
+                       "socket - %s.", strerror(errno));
 
-        cupsdNetIFUpdate();
+#ifdef WIN32
+       closesocket(BrowseSocket);
+#else
+       close(BrowseSocket);
+#endif /* WIN32 */
 
-       for (iface = (cupsd_netif_t *)cupsArrayFirst(NetIFList);
-            iface;
-            iface = (cupsd_netif_t *)cupsArrayNext(NetIFList))
-       {
-        /*
-         * Only send to local, IPv4 interfaces...
-         */
+       BrowseSocket = -1;
+       BrowseLocalProtocols &= ~BROWSE_CUPS;
+       BrowseRemoteProtocols &= ~BROWSE_CUPS;
+       return;
+      }
+    }
 
-         if (!iface->is_local || !iface->port ||
-             iface->address.addr.sa_family != AF_INET)
-           continue;
+   /*
+    * Set the "broadcast" flag...
+    */
 
-         httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
-                          iface->hostname, iface->port,
-                          (p->type & CUPS_PRINTER_CLASS) ? "/classes/%s%s" :
-                                                           "/printers/%s",
-                          p->name);
-         snprintf(packet, sizeof(packet), "%x %x %s \"%s\" \"%s\" \"%s\" %s\n",
-                  type, p->state, uri, location, info, make_model,
-                  p->browse_attrs ? p->browse_attrs : "");
+    val = 1;
+    if (setsockopt(BrowseSocket, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val)))
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                      "cupsdStartBrowsing: Unable to set broadcast mode - %s.",
+                     strerror(errno));
 
-         bytes = strlen(packet);
+#ifdef WIN32
+      closesocket(BrowseSocket);
+#else
+      close(BrowseSocket);
+#endif /* WIN32 */
 
-         cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                         "cupsdSendBrowseList: (%d bytes to \"%s\") %s", bytes,
-                         iface->name, packet);
+      BrowseSocket = -1;
+      BrowseLocalProtocols &= ~BROWSE_CUPS;
+      BrowseRemoteProtocols &= ~BROWSE_CUPS;
+      return;
+    }
 
-          iface->broadcast.ipv4.sin_port = htons(BrowsePort);
+   /*
+    * Close the socket on exec...
+    */
 
-         sendto(BrowseSocket, packet, bytes, 0,
-                (struct sockaddr *)&(iface->broadcast),
-                sizeof(struct sockaddr_in));
-        }
-      }
-      else if ((iface = cupsdNetIFFind(b->iface)) != NULL)
-      {
-       /*
-        * Send to the named interface using the IPv4 address...
-       */
+    fcntl(BrowseSocket, F_SETFD, fcntl(BrowseSocket, F_GETFD) | FD_CLOEXEC);
 
-        while (iface)
-         if (strcmp(b->iface, iface->name))
-         {
-           iface = NULL;
-           break;
-         }
-         else if (iface->address.addr.sa_family == AF_INET && iface->port)
-           break;
-         else
-            iface = (cupsd_netif_t *)cupsArrayNext(NetIFList);
+   /*
+    * Finally, add the socket to the input selection set...
+    */
 
-        if (iface)
-       {
-         httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
-                          iface->hostname, iface->port,
-                          (p->type & CUPS_PRINTER_CLASS) ? "/classes/%s%s" :
-                                                           "/printers/%s",
-                          p->name);
-         snprintf(packet, sizeof(packet), "%x %x %s \"%s\" \"%s\" \"%s\" %s\n",
-                  type, p->state, uri, location, info, make_model,
-                  p->browse_attrs ? p->browse_attrs : "");
+    cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                    "cupsdStartBrowsing: Adding fd %d to InputSet...",
+                    BrowseSocket);
 
-         bytes = strlen(packet);
+    FD_SET(BrowseSocket, InputSet);
+  }
+  else
+    BrowseSocket = -1;
 
-         cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                         "cupsdSendBrowseList: (%d bytes to \"%s\") %s", bytes,
-                         iface->name, packet);
+#ifdef HAVE_LIBSLP
+  if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_SLP)
+  {
+   /* 
+    * Open SLP handle...
+    */
 
-          iface->broadcast.ipv4.sin_port = htons(BrowsePort);
+    if (SLPOpen("en", SLP_FALSE, &BrowseSLPHandle) != SLP_OK)
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                      "Unable to open an SLP handle; disabling SLP browsing!");
+      BrowseLocalProtocols &= ~BROWSE_SLP;
+      BrowseRemoteProtocols &= ~BROWSE_SLP;
+    }
 
-         sendto(BrowseSocket, packet, bytes, 0,
-                (struct sockaddr *)&(iface->broadcast),
-                sizeof(struct sockaddr_in));
-        }
-      }
+    BrowseSLPRefresh = 0;
+  }
+#endif /* HAVE_LIBSLP */
+
+#ifdef HAVE_OPENLDAP
+  if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_LDAP)
+  {
+    if (!BrowseLDAPDN)
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                      "Need to set BrowseLDAPDN to use LDAP browsing!");
+      BrowseLocalProtocols &= ~BROWSE_LDAP;
+      BrowseRemoteProtocols &= ~BROWSE_LDAP;
     }
     else
     {
-     /*
-      * Send the browse packet to the indicated address using
-      * the default server name...
+     /* 
+      * Open LDAP handle...
       */
 
-      snprintf(packet, sizeof(packet), "%x %x %s \"%s\" \"%s\" \"%s\" %s\n",
-                      type, p->state, p->uri, location, info, make_model,
-              p->browse_attrs ? p->browse_attrs : "");
-
-      bytes = strlen(packet);
-      cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                      "cupsdSendBrowseList: (%d bytes) %s", bytes, packet);
+      int              rc;             /* LDAP API status */
+      int              version = 3;    /* LDAP version */
+      struct berval    bv = {0, ""};   /* SASL bind value */
 
-      if (sendto(BrowseSocket, packet, bytes, 0,
-                (struct sockaddr *)&(b->to),
-                sizeof(struct sockaddr_in)) <= 0)
-      {
-       /*
-        * Unable to send browse packet, so remove this address from the
-       * list...
-       */
 
-       cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "cupsdSendBrowseList: sendto failed for browser "
-                       "%d - %s.",
-                       b - Browsers + 1, strerror(errno));
+     /*
+      * LDAP stuff currently only supports ldapi EXTERNAL SASL binds...
+      */
 
-        if (i > 1)
-         memmove(b, b + 1, (i - 1) * sizeof(cupsd_dirsvc_addr_t));
+      if (!BrowseLDAPServer || !strcasecmp(BrowseLDAPServer, "localhost")) 
+        rc = ldap_initialize(&BrowseLDAPHandle, "ldapi:///");
+      else     
+       rc = ldap_initialize(&BrowseLDAPHandle, BrowseLDAPServer);
 
-       b --;
-       NumBrowsers --;
+      if (rc != LDAP_SUCCESS)
+      {
+       cupsdLogMessage(CUPSD_LOG_ERROR,
+                       "Unable to initialize LDAP; disabling LDAP browsing!");
+       BrowseLocalProtocols &= ~BROWSE_LDAP;
+       BrowseRemoteProtocols &= ~BROWSE_LDAP;
+      }
+      else if (ldap_set_option(BrowseLDAPHandle, LDAP_OPT_PROTOCOL_VERSION,
+                               (const void *)&version) != LDAP_SUCCESS)
+      {
+       ldap_unbind_ext(BrowseLDAPHandle, NULL, NULL);
+       BrowseLDAPHandle = NULL;
+       cupsdLogMessage(CUPSD_LOG_ERROR,
+                       "Unable to set LDAP protocol version; "
+                       "disabling LDAP browsing!");
+       BrowseLocalProtocols &= ~BROWSE_LDAP;
+       BrowseRemoteProtocols &= ~BROWSE_LDAP;
+      }
+      else
+      {
+       if (!BrowseLDAPServer || !strcasecmp(BrowseLDAPServer, "localhost"))
+         rc = ldap_sasl_bind_s(BrowseLDAPHandle, NULL, "EXTERNAL", &bv, NULL,
+                               NULL, NULL);
+       else
+         rc = ldap_bind_s(BrowseLDAPHandle, BrowseLDAPBindDN,
+                          BrowseLDAPPassword, LDAP_AUTH_SIMPLE);
+
+       if (rc != LDAP_SUCCESS)
+       {
+         cupsdLogMessage(CUPSD_LOG_ERROR,
+                         "Unable to bind to LDAP server; "
+                         "disabling LDAP browsing!");
+         ldap_unbind_ext(BrowseLDAPHandle, NULL, NULL);
+         BrowseLocalProtocols &= ~BROWSE_LDAP;
+         BrowseRemoteProtocols &= ~BROWSE_LDAP;
+       }
       }
     }
+
+    BrowseLDAPRefresh = 0;
+  }
+#endif /* HAVE_OPENLDAP */
 }
 
 
-#ifdef HAVE_OPENLDAP
 /*
- * cupsdSendLDAPBrowse()' - Send LDAP printer registrations.
+ * 'cupsdStartPolling()' - Start polling servers as needed.
  */
 
-void 
-cupsdSendLDAPBrowse(cupsd_printer_t *p)        /* I - Printer to register */
+void
+cupsdStartPolling(void)
 {
-  int          i;                      /* Looping var... */
-  LDAPMod      mods[7];                /* The 7 attributes we will be adding */
-  LDAPMod      *pmods[8];              /* Pointers to the 7 attributes + NULL */
-  LDAPMessage  *res;                   /* Search result token */
-  char         *cn_value[2],           /* Change records */
-               *uri[2],
-               *info[2],
-               *location[2],
-               *make_model[2],
-               *type[2],
-               typestring[255],        /* String to hold printer-type */
-               filter[256],            /* Search filter for possible UPDATEs */
-               dn[1024];               /* DN of the printer we are adding */
-  int          rc;                     /* LDAP status */
-  static const char * const objectClass_values[] =
-               {                       /* The 3 objectClass's we use in */
-                 "top",                /* our LDAP entries              */
-                 "device",
-                 "cupsPrinter",
-                 NULL
-               };
+  int                  i;              /* Looping var */
+  cupsd_dirsvc_poll_t  *pollp;         /* Current polling server */
+  char                 polld[1024];    /* Poll daemon path */
+  char                 sport[10];      /* Server port */
+  char                 bport[10];      /* Browser port */
+  char                 interval[10];   /* Poll interval */
+  int                  statusfds[2];   /* Status pipe */
+  char                 *argv[6];       /* Arguments */
+  char                 *envp[100];     /* Environment */
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSendLDAPBrowse: %s\n", p->name);
 
  /*
-  * Everything in ldap is ** so we fudge around it...
+  * Don't do anything if we aren't polling...
   */
 
-  sprintf(typestring, "%u", p->type);
+  if (NumPolled == 0)
+  {
+    PollPipe         = -1;
+    PollStatusBuffer = NULL;
+    return;
+  }
 
-  cn_value[0]   = p->info;
-  cn_value[1]   = NULL;
-  info[0]       = p->info;
-  info[1]       = NULL;
-  location[0]   = p->location;
-  location[1]   = NULL;
-  make_model[0] = p->make_model;
-  make_model[1] = NULL;
-  type[0]       = typestring;
-  type[1]       = NULL;
-  uri[0]        = p->uri;
-  uri[1]        = NULL;
+ /*
+  * Setup string arguments for polld, port and interval options.
+  */
 
-  snprintf(filter, sizeof(filter),
-           "(&(objectclass=cupsPrinter)(printerDescription~=%s))", p->info);
+  snprintf(polld, sizeof(polld), "%s/daemon/cups-polld", ServerBin);
 
-  ldap_search_s(BrowseLDAPHandle, BrowseLDAPDN, LDAP_SCOPE_SUBTREE,
-                filter, (char **)ldap_attrs, 0, &res);
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSendLDAPBrowse: Searching \"%s\"",
-                  filter);
+  sprintf(bport, "%d", BrowsePort);
 
-  mods[0].mod_type = "cn";
-  mods[0].mod_values = cn_value;
-  mods[1].mod_type = "printerDescription";
-  mods[1].mod_values = info;
-  mods[2].mod_type = "printerURI";
-  mods[2].mod_values = uri;
-  mods[3].mod_type = "printerLocation";
-  mods[3].mod_values = location;
-  mods[4].mod_type = "printerMakeAndModel";
-  mods[4].mod_values = make_model;
-  mods[5].mod_type = "printerType";
-  mods[5].mod_values = type;
-  mods[6].mod_type = "objectClass";
-  mods[6].mod_values = (char **)objectClass_values;
+  if (BrowseInterval)
+    sprintf(interval, "%d", BrowseInterval);
+  else
+    strcpy(interval, "30");
 
-  snprintf(dn, sizeof(dn), "cn=%s,ou=printers,%s", p->info, BrowseLDAPDN);
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSendLDAPBrowse: dn=\"%s\"", dn);
+  argv[0] = "cups-polld";
+  argv[2] = sport;
+  argv[3] = interval;
+  argv[4] = bport;
+  argv[5] = NULL;
 
-  if (ldap_count_entries(BrowseLDAPHandle, res) > 0)
+  cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
+
+ /*
+  * Create a pipe that receives the status messages from each
+  * polling daemon...
+  */
+
+  if (cupsdOpenPipe(statusfds))
   {
-   /*
-    * Printer has already been registered, modify the current
-    * registration...
-    */
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "Unable to create polling status pipes - %s.",
+                   strerror(errno));
+    PollPipe         = -1;
+    PollStatusBuffer = NULL;
+    return;
+  }
 
-    cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                    "cupsdSendLDAPBrowse: Replacing entry...");
+  PollPipe         = statusfds[0];
+  PollStatusBuffer = cupsdStatBufNew(PollPipe, "[Poll]");
 
-    for (i = 0; i < 7; i ++)
-    {
-      pmods[i]         = mods + i;
-      pmods[i]->mod_op = LDAP_MOD_REPLACE;
-    }
-    pmods[i] = NULL;
+ /*
+  * Run each polling daemon, redirecting stderr to the polling pipe...
+  */
 
-    if ((rc = ldap_modify_s(BrowseLDAPHandle, dn, pmods)) != LDAP_SUCCESS)
-      cupsdLogMessage(CUPSD_LOG_ERROR,
-                      "LDAP modify for %s failed with status %d: %s",
-                      p->name, rc, ldap_err2string(rc));
-  }
-  else 
+  for (i = 0, pollp = Polled; i < NumPolled; i ++, pollp ++)
   {
-   /*
-    * Printer has already been registered, modify the current
-    * registration...
-    */
+    sprintf(sport, "%d", pollp->port);
 
-    cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                    "cupsdSendLDAPBrowse: Adding entry...");
+    argv[1] = pollp->hostname;
 
-    for (i = 0; i < 7; i ++)
+    if (cupsdStartProcess(polld, argv, envp, -1, -1, statusfds[1], -1,
+                          0, &(pollp->pid)) < 0)
     {
-      pmods[i]         = mods + i;
-      pmods[i]->mod_op = LDAP_MOD_REPLACE;
-    }
-    pmods[i] = NULL;
-
-    if ((rc = ldap_modify_s(BrowseLDAPHandle, dn, pmods)) != LDAP_SUCCESS)
       cupsdLogMessage(CUPSD_LOG_ERROR,
-                      "LDAP add for %s failed with status %d: %s",
-                      p->name, rc, ldap_err2string(rc));
+                      "cupsdStartPolling: Unable to fork polling daemon - %s",
+                      strerror(errno));
+      pollp->pid = 0;
+      break;
+    }
+    else
+      cupsdLogMessage(CUPSD_LOG_DEBUG,
+                      "cupsdStartPolling: Started polling daemon for %s:%d, pid = %d",
+                      pollp->hostname, pollp->port, pollp->pid);
   }
+
+  close(statusfds[1]);
+
+ /*
+  * Finally, add the pipe to the input selection set...
+  */
+
+  cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                  "cupsdStartPolling: Adding fd %d to InputSet...", PollPipe);
+
+  FD_SET(PollPipe, InputSet);
 }
-#endif /* HAVE_OPENLDAP */
 
 
-#ifdef HAVE_LIBSLP
 /*
- * 'cupsdSendSLPBrowse()' - Register the specified printer with SLP.
+ * 'cupsdStopBrowsing()' - Stop sending and receiving broadcast information.
  */
 
-void 
-cupsdSendSLPBrowse(cupsd_printer_t *p) /* I - Printer to register */
+void
+cupsdStopBrowsing(void)
 {
-  char         srvurl[HTTP_MAX_URI],   /* Printer service URI */
-               attrs[8192],            /* Printer attributes */
-               finishings[1024],       /* Finishings to support */
-               make_model[IPP_MAX_NAME * 2],
-                                       /* Make and model, quoted */
-               location[IPP_MAX_NAME * 2],
-                                       /* Location, quoted */
-               info[IPP_MAX_NAME * 2], /* Info, quoted */
-               *src,                   /* Pointer to original string */
-               *dst;                   /* Pointer to destination string */
-  ipp_attribute_t *authentication;     /* uri-authentication-supported value */
-  SLPError     error;                  /* SLP error, if any */
-
+  if (!Browsing || !(BrowseLocalProtocols | BrowseRemoteProtocols))
+    return;
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSendSLPBrowse(%p = \"%s\")", p,
-                  p->name);
+  if (((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_CUPS) &&
+      BrowseSocket >= 0)
+  {
+   /*
+    * Close the socket and remove it from the input selection set.
+    */
 
- /*
-  * Make the SLP service URL that conforms to the IANA 
-  * 'printer:' template.
-  */
+#ifdef WIN32
+    closesocket(BrowseSocket);
+#else
+    close(BrowseSocket);
+#endif /* WIN32 */
 
-  snprintf(srvurl, sizeof(srvurl), SLP_CUPS_SRVTYPE ":%s", p->uri);
+    cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                   "cupsdStopBrowsing: Removing fd %d from InputSet...",
+                   BrowseSocket);
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "Service URL = \"%s\"", srvurl);
+    FD_CLR(BrowseSocket, InputSet);
+    BrowseSocket = -1;
+  }
 
- /*
-  * Figure out the finishings string...
-  */
+#ifdef HAVE_LIBSLP
+  if (((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_SLP) &&
+      BrowseSLPHandle)
+  {
+   /* 
+    * Close SLP handle...
+    */
 
-  if (p->type & CUPS_PRINTER_STAPLE)
-    strcpy(finishings, "staple");
-  else
-    finishings[0] = '\0';
-
-  if (p->type & CUPS_PRINTER_BIND)
-  {
-    if (finishings[0])
-      strlcat(finishings, ",bind", sizeof(finishings));
-    else
-      strcpy(finishings, "bind");
+    SLPClose(BrowseSLPHandle);
+    BrowseSLPHandle = NULL;
   }
+#endif /* HAVE_LIBSLP */
 
-  if (p->type & CUPS_PRINTER_PUNCH)
+#ifdef HAVE_OPENDAP
+  if (((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_LDAP) &&
+      BrowseLDAPHandle)
   {
-    if (finishings[0])
-      strlcat(finishings, ",punch", sizeof(finishings));
-    else
-      strcpy(finishings, "punch");
+    ldap_unbind(BrowseLDAPHandle);
+    BrowseLDAPHandle = NULL;
   }
+#endif /* HAVE_OPENLDAP */
+}
 
-  if (p->type & CUPS_PRINTER_COVER)
-  {
-    if (finishings[0])
-      strlcat(finishings, ",cover", sizeof(finishings));
-    else
-      strcpy(finishings, "cover");
-  }
 
-  if (p->type & CUPS_PRINTER_SORT)
-  {
-    if (finishings[0])
-      strlcat(finishings, ",sort", sizeof(finishings));
-    else
-      strcpy(finishings, "sort");
-  }
+/*
+ * 'cupsdStopPolling()' - Stop polling servers as needed.
+ */
 
-  if (!finishings[0])
-    strcpy(finishings, "none");
+void
+cupsdStopPolling(void)
+{
+  int                  i;              /* Looping var */
+  cupsd_dirsvc_poll_t  *pollp;         /* Current polling server */
 
- /*
-  * Quote any commas in the make and model, location, and info strings...
-  */
 
-  for (src = p->make_model, dst = make_model;
-       src && *src && dst < (make_model + sizeof(make_model) - 2);)
+  if (PollPipe >= 0)
   {
-    if (*src == ',' || *src == '\\' || *src == ')')
-      *dst++ = '\\';
+    cupsdStatBufDelete(PollStatusBuffer);
+    close(PollPipe);
 
-    *dst++ = *src++;
+    cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                    "cupsdStopPolling: removing fd %d from InputSet.", PollPipe);
+    FD_CLR(PollPipe, InputSet);
+
+    PollPipe         = -1;
+    PollStatusBuffer = NULL;
   }
 
-  *dst = '\0';
+  for (i = 0, pollp = Polled; i < NumPolled; i ++, pollp ++)
+    if (pollp->pid)
+      cupsdEndProcess(pollp->pid, 0);
+}
 
-  if (!make_model[0])
-    strcpy(make_model, "Unknown");
 
-  for (src = p->location, dst = location;
-       src && *src && dst < (location + sizeof(location) - 2);)
-  {
-    if (*src == ',' || *src == '\\' || *src == ')')
-      *dst++ = '\\';
+/*
+ * 'cupsdUpdateCUPSBrowse()' - Update the browse lists using the CUPS protocol.
+ */
 
-    *dst++ = *src++;
-  }
+void
+cupsdUpdateCUPSBrowse(void)
+{
+  int          i;                      /* Looping var */
+  int          auth;                   /* Authorization status */
+  int          len;                    /* Length of name string */
+  int          bytes;                  /* Number of bytes left */
+  char         packet[1541],           /* Broadcast packet */
+               *pptr;                  /* Pointer into packet */
+  socklen_t    srclen;                 /* Length of source address */
+  http_addr_t  srcaddr;                /* Source address */
+  char         srcname[1024];          /* Source hostname */
+  unsigned     address[4];             /* Source address */
+  unsigned     type;                   /* Printer type */
+  unsigned     state;                  /* Printer state */
+  char         uri[HTTP_MAX_URI],      /* Printer URI */
+               host[HTTP_MAX_URI],     /* Host portion of URI */
+               resource[HTTP_MAX_URI], /* Resource portion of URI */
+               info[IPP_MAX_NAME],     /* Information string */
+               location[IPP_MAX_NAME], /* Location string */
+               make_model[IPP_MAX_NAME];/* Make and model string */
+  int          num_attrs;              /* Number of attributes */
+  cups_option_t        *attrs;                 /* Attributes */
 
-  *dst = '\0';
 
-  if (!location[0])
-    strcpy(location, "Unknown");
+ /*
+  * Read a packet from the browse socket...
+  */
 
-  for (src = p->info, dst = info;
-       src && *src && dst < (info + sizeof(info) - 2);)
+  srclen = sizeof(srcaddr);
+  if ((bytes = recvfrom(BrowseSocket, packet, sizeof(packet) - 1, 0, 
+                        (struct sockaddr *)&srcaddr, &srclen)) < 0)
   {
-    if (*src == ',' || *src == '\\' || *src == ')')
-      *dst++ = '\\';
-
-    *dst++ = *src++;
-  }
+   /*
+    * "Connection refused" is returned under Linux if the destination port
+    * or address is unreachable from a previous sendto(); check for the
+    * error here and ignore it for now...
+    */
 
-  *dst = '\0';
+    if (errno != ECONNREFUSED && errno != EAGAIN)
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR, "Browse recv failed - %s.",
+                      strerror(errno));
+      cupsdLogMessage(CUPSD_LOG_ERROR, "Browsing turned off.");
 
-  if (!info[0])
-    strcpy(info, "Unknown");
+      cupsdStopBrowsing();
+      Browsing = 0;
+    }
 
- /*
-  * Get the authentication value...
-  */
+    return;
+  }
 
-  authentication = ippFindAttribute(p->attrs, "uri-authentication-supported",
-                                    IPP_TAG_KEYWORD);
+  packet[bytes] = '\0';
 
  /*
-  * Make the SLP attribute string list that conforms to
-  * the IANA 'printer:' template.
+  * If we're about to sleep, ignore incoming browse packets.
   */
 
-  snprintf(attrs, sizeof(attrs),
-           "(printer-uri-supported=%s),"
-           "(uri-authentication-supported=%s>),"
-#ifdef HAVE_SSL
-           "(uri-security-supported=tls>),"
-#else
-           "(uri-security-supported=none>),"
-#endif /* HAVE_SSL */
-           "(printer-name=%s),"
-           "(printer-location=%s),"
-           "(printer-info=%s),"
-           "(printer-more-info=%s),"
-           "(printer-make-and-model=%s),"
-          "(printer-type=%d),"
-          "(charset-supported=utf-8),"
-          "(natural-language-configured=%s),"
-          "(natural-language-supported=de,en,es,fr,it),"
-           "(color-supported=%s),"
-           "(finishings-supported=%s),"
-           "(sides-supported=one-sided%s),"
-          "(multiple-document-jobs-supported=true)"
-          "(ipp-versions-supported=1.0,1.1)",
-          p->uri, authentication->values[0].string.text, p->name, location,
-          info, p->uri, make_model, p->type, DefaultLanguage,
-           p->type & CUPS_PRINTER_COLOR ? "true" : "false",
-           finishings,
-           p->type & CUPS_PRINTER_DUPLEX ?
-              ",two-sided-long-edge,two-sided-short-edge" : "");
-
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "Attributes = \"%s\"", attrs);
+  if (Sleeping)
+    return;
 
  /*
-  * Register the printer with the SLP server...
+  * Figure out where it came from...
   */
 
-  error = SLPReg(BrowseSLPHandle, srvurl, BrowseTimeout,
-                SLP_CUPS_SRVTYPE, attrs, SLP_TRUE, slp_reg_callback, 0);
-
-  if (error != SLP_OK)
-    cupsdLogMessage(CUPSD_LOG_ERROR, "SLPReg of \"%s\" failed with status %d!", p->name,
-                    error);
-}
-#endif /* HAVE_LIBSLP */
-
-
-/*
- * 'cupsdStartBrowsing()' - Start sending and receiving broadcast information.
- */
-
-void
-cupsdStartBrowsing(void)
-{
-  int                  val;            /* Socket option value */
-  struct sockaddr_in   addr;           /* Broadcast address */
+#ifdef AF_INET6
+  if (srcaddr.addr.sa_family == AF_INET6)
+  {
+    address[0] = ntohl(srcaddr.ipv6.sin6_addr.s6_addr32[0]);
+    address[1] = ntohl(srcaddr.ipv6.sin6_addr.s6_addr32[1]);
+    address[2] = ntohl(srcaddr.ipv6.sin6_addr.s6_addr32[2]);
+    address[3] = ntohl(srcaddr.ipv6.sin6_addr.s6_addr32[3]);
+  }
+  else
+#endif /* AF_INET6 */
+  {
+    address[0] = 0;
+    address[1] = 0;
+    address[2] = 0;
+    address[3] = ntohl(srcaddr.ipv4.sin_addr.s_addr);
+  }
 
+  if (HostNameLookups)
+    httpAddrLookup(&srcaddr, srcname, sizeof(srcname));
+  else
+    httpAddrString(&srcaddr, srcname, sizeof(srcname));
 
-  BrowseNext = NULL;
+  len = strlen(srcname);
 
-  if (!Browsing || !(BrowseLocalProtocols | BrowseRemoteProtocols))
-    return;
+ /*
+  * Do ACL stuff...
+  */
 
-  if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_CUPS)
+  if (BrowseACL)
   {
-    if (BrowseSocket < 0)
+    if (httpAddrLocalhost(&srcaddr) || !strcasecmp(srcname, "localhost"))
     {
      /*
-      * Create the broadcast socket...
+      * Access from localhost (127.0.0.1) is always allowed...
       */
 
-      if ((BrowseSocket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
-      {
-       cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "cupsdStartBrowsing: Unable to create broadcast "
-                       "socket - %s.", strerror(errno));
-       BrowseLocalProtocols &= ~BROWSE_CUPS;
-       BrowseRemoteProtocols &= ~BROWSE_CUPS;
-       return;
-      }
-
+      auth = AUTH_ALLOW;
+    }
+    else
+    {
      /*
-      * Bind the socket to browse port...
+      * Do authorization checks on the domain/address...
       */
 
-      memset(&addr, 0, sizeof(addr));
-      addr.sin_addr.s_addr = htonl(INADDR_ANY);
-      addr.sin_family      = AF_INET;
-      addr.sin_port        = htons(BrowsePort);
-
-      if (bind(BrowseSocket, (struct sockaddr *)&addr, sizeof(addr)))
+      switch (BrowseACL->order_type)
       {
-       cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "cupsdStartBrowsing: Unable to bind broadcast "
-                       "socket - %s.", strerror(errno));
+        default :
+           auth = AUTH_DENY;   /* anti-compiler-warning-code */
+           break;
 
-#ifdef WIN32
-       closesocket(BrowseSocket);
-#else
-       close(BrowseSocket);
-#endif /* WIN32 */
+       case AUTH_ALLOW : /* Order Deny,Allow */
+            auth = AUTH_ALLOW;
 
-       BrowseSocket = -1;
-       BrowseLocalProtocols &= ~BROWSE_CUPS;
-       BrowseRemoteProtocols &= ~BROWSE_CUPS;
-       return;
-      }
-    }
+            if (cupsdCheckAuth(address, srcname, len,
+                         BrowseACL->num_deny, BrowseACL->deny))
+             auth = AUTH_DENY;
 
-   /*
-    * Set the "broadcast" flag...
-    */
+            if (cupsdCheckAuth(address, srcname, len,
+                         BrowseACL->num_allow, BrowseACL->allow))
+             auth = AUTH_ALLOW;
+           break;
 
-    val = 1;
-    if (setsockopt(BrowseSocket, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val)))
-    {
-      cupsdLogMessage(CUPSD_LOG_ERROR,
-                      "cupsdStartBrowsing: Unable to set broadcast mode - %s.",
-                     strerror(errno));
+       case AUTH_DENY : /* Order Allow,Deny */
+            auth = AUTH_DENY;
 
-#ifdef WIN32
-      closesocket(BrowseSocket);
-#else
-      close(BrowseSocket);
-#endif /* WIN32 */
+            if (cupsdCheckAuth(address, srcname, len,
+                         BrowseACL->num_allow, BrowseACL->allow))
+             auth = AUTH_ALLOW;
 
-      BrowseSocket = -1;
-      BrowseLocalProtocols &= ~BROWSE_CUPS;
-      BrowseRemoteProtocols &= ~BROWSE_CUPS;
-      return;
+            if (cupsdCheckAuth(address, srcname, len,
+                         BrowseACL->num_deny, BrowseACL->deny))
+             auth = AUTH_DENY;
+           break;
+      }
     }
+  }
+  else
+    auth = AUTH_ALLOW;
 
-   /*
-    * Close the socket on exec...
-    */
-
-    fcntl(BrowseSocket, F_SETFD, fcntl(BrowseSocket, F_GETFD) | FD_CLOEXEC);
+  if (auth == AUTH_DENY)
+  {
+    cupsdLogMessage(CUPSD_LOG_DEBUG,
+                    "cupsdUpdateCUPSBrowse: Refused %d bytes from %s", bytes,
+                    srcname);
+    return;
+  }
 
-   /*
-    * Finally, add the socket to the input selection set...
-    */
+  cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                  "cupsdUpdateCUPSBrowse: (%d bytes from %s) %s", bytes,
+                 srcname, packet);
 
-    cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                    "cupsdStartBrowsing: Adding fd %d to InputSet...",
-                    BrowseSocket);
+ /*
+  * Parse packet...
+  */
 
-    FD_SET(BrowseSocket, InputSet);
+  if (sscanf(packet, "%x%x%1023s", &type, &state, uri) < 3)
+  {
+    cupsdLogMessage(CUPSD_LOG_WARN,
+                    "cupsdUpdateCUPSBrowse: Garbled browse packet - %s", packet);
+    return;
   }
-  else
-    BrowseSocket = -1;
 
-#ifdef HAVE_LIBSLP
-  if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_SLP)
+  strcpy(location, "Location Unknown");
+  strcpy(info, "No Information Available");
+  make_model[0] = '\0';
+  num_attrs     = 0;
+  attrs         = NULL;
+
+  if ((pptr = strchr(packet, '\"')) != NULL)
   {
-   /* 
-    * Open SLP handle...
+   /*
+    * Have extended information; can't use sscanf for it because not all
+    * sscanf's allow empty strings with %[^\"]...
     */
 
-    if (SLPOpen("en", SLP_FALSE, &BrowseSLPHandle) != SLP_OK)
-    {
-      cupsdLogMessage(CUPSD_LOG_ERROR,
-                      "Unable to open an SLP handle; disabling SLP browsing!");
-      BrowseLocalProtocols &= ~BROWSE_SLP;
-      BrowseRemoteProtocols &= ~BROWSE_SLP;
-    }
+    for (i = 0, pptr ++;
+         i < (sizeof(location) - 1) && *pptr && *pptr != '\"';
+         i ++, pptr ++)
+      location[i] = *pptr;
 
-    BrowseSLPRefresh = 0;
-  }
-#endif /* HAVE_LIBSLP */
+    if (i)
+      location[i] = '\0';
 
-#ifdef HAVE_OPENLDAP
-  if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_LDAP)
-  {
-    if (!BrowseLDAPDN)
-    {
-      cupsdLogMessage(CUPSD_LOG_ERROR,
-                      "Need to set BrowseLDAPDN to use LDAP browsing!");
-      BrowseLocalProtocols &= ~BROWSE_LDAP;
-      BrowseRemoteProtocols &= ~BROWSE_LDAP;
-    }
-    else
-    {
-     /* 
-      * Open LDAP handle...
-      */
+    if (*pptr == '\"')
+      pptr ++;
 
-      int              rc;             /* LDAP API status */
-      int              version = 3;    /* LDAP version */
-      struct berval    bv = {0, ""};   /* SASL bind value */
+    while (*pptr && isspace(*pptr & 255))
+      pptr ++;
 
+    if (*pptr == '\"')
+    {
+      for (i = 0, pptr ++;
+           i < (sizeof(info) - 1) && *pptr && *pptr != '\"';
+           i ++, pptr ++)
+       info[i] = *pptr;
 
-     /*
-      * LDAP stuff currently only supports ldapi EXTERNAL SASL binds...
-      */
+      info[i] = '\0';
 
-      if (!BrowseLDAPServer || !strcasecmp(BrowseLDAPServer, "localhost")) 
-        rc = ldap_initialize(&BrowseLDAPHandle, "ldapi:///");
-      else     
-       rc = ldap_initialize(&BrowseLDAPHandle, BrowseLDAPServer);
+      if (*pptr == '\"')
+       pptr ++;
 
-      if (rc != LDAP_SUCCESS)
-      {
-       cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "Unable to initialize LDAP; disabling LDAP browsing!");
-       BrowseLocalProtocols &= ~BROWSE_LDAP;
-       BrowseRemoteProtocols &= ~BROWSE_LDAP;
-      }
-      else if (ldap_set_option(BrowseLDAPHandle, LDAP_OPT_PROTOCOL_VERSION,
-                               (const void *)&version) != LDAP_SUCCESS)
-      {
-       ldap_unbind_ext(BrowseLDAPHandle, NULL, NULL);
-       BrowseLDAPHandle = NULL;
-       cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "Unable to set LDAP protocol version; "
-                       "disabling LDAP browsing!");
-       BrowseLocalProtocols &= ~BROWSE_LDAP;
-       BrowseRemoteProtocols &= ~BROWSE_LDAP;
-      }
-      else
+      while (*pptr && isspace(*pptr & 255))
+       pptr ++;
+
+      if (*pptr == '\"')
       {
-       if (!BrowseLDAPServer || !strcasecmp(BrowseLDAPServer, "localhost"))
-         rc = ldap_sasl_bind_s(BrowseLDAPHandle, NULL, "EXTERNAL", &bv, NULL,
-                               NULL, NULL);
-       else
-         rc = ldap_bind_s(BrowseLDAPHandle, BrowseLDAPBindDN,
-                          BrowseLDAPPassword, LDAP_AUTH_SIMPLE);
+       for (i = 0, pptr ++;
+             i < (sizeof(make_model) - 1) && *pptr && *pptr != '\"';
+             i ++, pptr ++)
+         make_model[i] = *pptr;
 
-       if (rc != LDAP_SUCCESS)
-       {
-         cupsdLogMessage(CUPSD_LOG_ERROR,
-                         "Unable to bind to LDAP server; "
-                         "disabling LDAP browsing!");
-         ldap_unbind_ext(BrowseLDAPHandle, NULL, NULL);
-         BrowseLocalProtocols &= ~BROWSE_LDAP;
-         BrowseRemoteProtocols &= ~BROWSE_LDAP;
-       }
+       if (*pptr == '\"')
+         pptr ++;
+
+       make_model[i] = '\0';
+
+        if (*pptr)
+         num_attrs = cupsParseOptions(pptr, num_attrs, &attrs);
       }
     }
-
-    BrowseLDAPRefresh = 0;
   }
-#endif /* HAVE_OPENLDAP */
-}
-
-
-/*
- * 'cupsdStartPolling()' - Start polling servers as needed.
- */
-
-void
-cupsdStartPolling(void)
-{
-  int                  i;              /* Looping var */
-  cupsd_dirsvc_poll_t  *pollp;         /* Current polling server */
-  char                 polld[1024];    /* Poll daemon path */
-  char                 sport[10];      /* Server port */
-  char                 bport[10];      /* Browser port */
-  char                 interval[10];   /* Poll interval */
-  int                  statusfds[2];   /* Status pipe */
-  char                 *argv[6];       /* Arguments */
-  char                 *envp[100];     /* Environment */
 
+  DEBUG_puts(packet);
+  DEBUG_printf(("type=%x, state=%x, uri=\"%s\"\n"
+                "location=\"%s\", info=\"%s\", make_model=\"%s\"\n",
+               type, state, uri, location, info, make_model));
 
  /*
-  * Don't do anything if we aren't polling...
+  * Pull the URI apart to see if this is a local or remote printer...
   */
 
-  if (NumPolled == 0)
+  if (is_local_queue(uri, host, sizeof(host), resource, sizeof(resource)))
   {
-    PollPipe         = -1;
-    PollStatusBuffer = NULL;
+    cupsFreeOptions(num_attrs, attrs);
     return;
   }
 
  /*
-  * Setup string arguments for polld, port and interval options.
+  * Do relaying...
   */
 
-  snprintf(polld, sizeof(polld), "%s/daemon/cups-polld", ServerBin);
+  for (i = 0; i < NumRelays; i ++)
+    if (cupsdCheckAuth(address, srcname, len, 1, &(Relays[i].from)))
+      if (sendto(BrowseSocket, packet, bytes, 0,
+                 (struct sockaddr *)&(Relays[i].to),
+                sizeof(http_addr_t)) <= 0)
+      {
+       cupsdLogMessage(CUPSD_LOG_ERROR,
+                       "cupsdUpdateCUPSBrowse: sendto failed for relay %d - %s.",
+                       i + 1, strerror(errno));
+       cupsFreeOptions(num_attrs, attrs);
+       return;
+      }
 
-  sprintf(bport, "%d", BrowsePort);
+ /*
+  * Process the browse data...
+  */
 
-  if (BrowseInterval)
-    sprintf(interval, "%d", BrowseInterval);
-  else
-    strcpy(interval, "30");
+  process_browse_data(uri, host, resource, (cups_ptype_t)type,
+                      (ipp_pstate_t)state, location, info, make_model,
+                     num_attrs, attrs);
+}
 
-  argv[0] = "cups-polld";
-  argv[2] = sport;
-  argv[3] = interval;
-  argv[4] = bport;
-  argv[5] = NULL;
 
-  cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
+#ifdef HAVE_OPENLDAP
+/*
+ * 'cupsdUpdateLDAPBrowse()' - Scan for new printers via LDAP...
+ */
+
+void
+cupsdUpdateLDAPBrowse(void)
+{
+  char         uri[HTTP_MAX_URI],      /* Printer URI */
+               host[HTTP_MAX_URI],     /* Hostname */
+               resource[HTTP_MAX_URI], /* Resource path */
+               location[1024],         /* Printer location */
+               info[1024],             /* Printer information */
+               make_model[1024],       /* Printer make and model */
+               **value;                /* Holds the returned data from LDAP */
+  int          type;                   /* Printer type */
+  int          rc;                     /* LDAP status */
+  int          limit;                  /* Size limit */
+  LDAPMessage  *res,                   /* LDAP search results */
+                 *e;                   /* Current entry from search */
+
 
  /*
-  * Create a pipe that receives the status messages from each
-  * polling daemon...
+  * Search for printers...
   */
 
-  if (cupsdOpenPipe(statusfds))
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "UpdateLDAPBrowse: %s", ServerName);
+
+  BrowseLDAPRefresh = time(NULL) + BrowseInterval;
+
+  rc = ldap_search_s(BrowseLDAPHandle, BrowseLDAPDN, LDAP_SCOPE_SUBTREE,
+                     "(objectclass=cupsPrinter)", (char **)ldap_attrs, 0, &res);
+  if (rc != LDAP_SUCCESS) 
   {
     cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "Unable to create polling status pipes - %s.",
-                   strerror(errno));
-    PollPipe         = -1;
-    PollStatusBuffer = NULL;
+                    "LDAP search returned error %d: %s", rc,
+                   ldap_err2string(rc));
     return;
   }
 
-  PollPipe         = statusfds[0];
-  PollStatusBuffer = cupsdStatBufNew(PollPipe, "[Poll]");
+  limit = ldap_count_entries(BrowseLDAPHandle, res);
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "LDAP search returned %d entries", limit);
+  if (limit < 1)
+    return;
 
  /*
-  * Run each polling daemon, redirecting stderr to the polling pipe...
+  * Loop through the available printers...
   */
 
-  for (i = 0, pollp = Polled; i < NumPolled; i ++, pollp ++)
+  if ((e = ldap_first_entry(BrowseLDAPHandle, res)) == NULL)
   {
-    sprintf(sport, "%d", pollp->port);
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to get LDAP printer entry!");
+    return;
+  }
 
-    argv[1] = pollp->hostname;
+  while (e)
+  {
+    value = ldap_get_values(BrowseLDAPHandle, e, "printerDescription");
+    strlcpy(info, *value, sizeof(info));
+    ldap_value_free(value);
 
-    if (cupsdStartProcess(polld, argv, envp, -1, -1, statusfds[1], -1,
-                          0, &(pollp->pid)) < 0)
-    {
-      cupsdLogMessage(CUPSD_LOG_ERROR,
-                      "cupsdStartPolling: Unable to fork polling daemon - %s",
-                      strerror(errno));
-      pollp->pid = 0;
-      break;
-    }
-    else
-      cupsdLogMessage(CUPSD_LOG_DEBUG,
-                      "cupsdStartPolling: Started polling daemon for %s:%d, pid = %d",
-                      pollp->hostname, pollp->port, pollp->pid);
-  }
+    value = ldap_get_values(BrowseLDAPHandle, e, "printerLocation");
+    strlcpy(location, *value, sizeof(location));
+    ldap_value_free(value);
 
-  close(statusfds[1]);
+    value = ldap_get_values(BrowseLDAPHandle, e, "printerMakeAndModel");
+    strlcpy(make_model, *value, sizeof(make_model));
+    ldap_value_free(value);
 
- /*
-  * Finally, add the pipe to the input selection set...
-  */
+    value = ldap_get_values(BrowseLDAPHandle, e, "printerType");
+    type = atoi(*value);
+    ldap_value_free(value);
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "cupsdStartPolling: Adding fd %d to InputSet...", PollPipe);
+    value = ldap_get_values(BrowseLDAPHandle, e, "printerURI");
+    strlcpy(uri, *value, sizeof(uri));
+    ldap_value_free(value);
 
-  FD_SET(PollPipe, InputSet);
+    if (!is_local_queue(uri, host, sizeof(host), resource, sizeof(resource)))
+      process_browse_data(uri, host, resource, type, IPP_PRINTER_IDLE,
+                          location, info, make_model, 0, NULL);
+
+    e = ldap_next_entry(BrowseLDAPHandle, e);
+  }
 }
+#endif /* HAVE_OPENLDAP */
 
 
 /*
- * 'cupsdStopBrowsing()' - Stop sending and receiving broadcast information.
+ * 'cupsdUpdatePolling()' - Read status messages from the poll daemons.
  */
 
 void
-cupsdStopBrowsing(void)
+cupsdUpdatePolling(void)
 {
-  if (!Browsing || !(BrowseLocalProtocols | BrowseRemoteProtocols))
-    return;
-
-  if (((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_CUPS) &&
-      BrowseSocket >= 0)
-  {
-   /*
-    * Close the socket and remove it from the input selection set.
-    */
-
-#ifdef WIN32
-    closesocket(BrowseSocket);
-#else
-    close(BrowseSocket);
-#endif /* WIN32 */
+  char         *ptr,                   /* Pointer to end of line in buffer */
+               message[1024];          /* Pointer to message text */
+  int          loglevel;               /* Log level for message */
 
-    cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                   "cupsdStopBrowsing: Removing fd %d from InputSet...",
-                   BrowseSocket);
 
-    FD_CLR(BrowseSocket, InputSet);
-    BrowseSocket = -1;
-  }
+  while ((ptr = cupsdStatBufUpdate(PollStatusBuffer, &loglevel,
+                                   message, sizeof(message))) != NULL)
+    if (!strchr(PollStatusBuffer->buffer, '\n'))
+      break;
 
-#ifdef HAVE_LIBSLP
-  if (((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_SLP) &&
-      BrowseSLPHandle)
+  if (ptr == NULL)
   {
-   /* 
-    * Close SLP handle...
+   /*
+    * All polling processes have died; stop polling...
     */
 
-    SLPClose(BrowseSLPHandle);
-    BrowseSLPHandle = NULL;
-  }
-#endif /* HAVE_LIBSLP */
-
-#ifdef HAVE_OPENDAP
-  if (((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_LDAP) &&
-      BrowseLDAPHandle)
-  {
-    ldap_unbind(BrowseLDAPHandle);
-    BrowseLDAPHandle = NULL;
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "cupsdUpdatePolling: all polling processes have exited!");
+    cupsdStopPolling();
   }
-#endif /* HAVE_OPENLDAP */
 }
 
 
+#ifdef HAVE_LIBSLP 
 /*
- * 'cupsdStopPolling()' - Stop polling servers as needed.
+ * 'cupsdUpdateSLPBrowse()' - Get browsing information via SLP.
  */
 
 void
-cupsdStopPolling(void)
+cupsdUpdateSLPBrowse(void)
 {
-  int                  i;              /* Looping var */
-  cupsd_dirsvc_poll_t  *pollp;         /* Current polling server */
-
-
-  if (PollPipe >= 0)
-  {
-    cupsdStatBufDelete(PollStatusBuffer);
-    close(PollPipe);
-
-    cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                    "cupsdStopPolling: removing fd %d from InputSet.", PollPipe);
-    FD_CLR(PollPipe, InputSet);
+  slpsrvurl_t  *s,                     /* Temporary list of service URLs */
+               *next;                  /* Next service in list */
+  cupsd_printer_t p;                   /* Printer information */
+  const char   *uri;                   /* Pointer to printer URI */
+  char         host[HTTP_MAX_URI],     /* Host portion of URI */
+               resource[HTTP_MAX_URI]; /* Resource portion of URI */
 
-    PollPipe         = -1;
-    PollStatusBuffer = NULL;
-  }
 
-  for (i = 0, pollp = Polled; i < NumPolled; i ++, pollp ++)
-    if (pollp->pid)
-      cupsdEndProcess(pollp->pid, 0);
-}
+ /*
+  * Reset the refresh time...
+  */
 
+  BrowseSLPRefresh = time(NULL) + BrowseInterval;
 
-/*
* 'cupsdUpdateCUPSBrowse()' - Update the browse lists using the CUPS protocol.
- */
+ /* 
 * Poll for remote printers using SLP...
 */
 
-void
-cupsdUpdateCUPSBrowse(void)
-{
-  int          i;                      /* Looping var */
-  int          auth;                   /* Authorization status */
-  int          len;                    /* Length of name string */
-  int          bytes;                  /* Number of bytes left */
-  char         packet[1541],           /* Broadcast packet */
-               *pptr;                  /* Pointer into packet */
-  socklen_t    srclen;                 /* Length of source address */
-  http_addr_t  srcaddr;                /* Source address */
-  char         srcname[1024];          /* Source hostname */
-  unsigned     address[4];             /* Source address */
-  unsigned     type;                   /* Printer type */
-  unsigned     state;                  /* Printer state */
-  char         uri[HTTP_MAX_URI],      /* Printer URI */
-               host[HTTP_MAX_URI],     /* Host portion of URI */
-               resource[HTTP_MAX_URI], /* Resource portion of URI */
-               info[IPP_MAX_NAME],     /* Information string */
-               location[IPP_MAX_NAME], /* Location string */
-               make_model[IPP_MAX_NAME];/* Make and model string */
-  int          num_attrs;              /* Number of attributes */
-  cups_option_t        *attrs;                 /* Attributes */
+  s = NULL;
 
+  SLPFindSrvs(BrowseSLPHandle, SLP_CUPS_SRVTYPE, "", "",
+             slp_url_callback, &s);
 
  /*
-  * Read a packet from the browse socket...
+  * Loop through the list of available printers...
   */
 
-  srclen = sizeof(srcaddr);
-  if ((bytes = recvfrom(BrowseSocket, packet, sizeof(packet) - 1, 0, 
-                        (struct sockaddr *)&srcaddr, &srclen)) < 0)
+  for (; s; s = next)
   {
    /*
-    * "Connection refused" is returned under Linux if the destination port
-    * or address is unreachable from a previous sendto(); check for the
-    * error here and ignore it for now...
+    * Save the "next" pointer...
     */
 
-    if (errno != ECONNREFUSED && errno != EAGAIN)
-    {
-      cupsdLogMessage(CUPSD_LOG_ERROR, "Browse recv failed - %s.",
-                      strerror(errno));
-      cupsdLogMessage(CUPSD_LOG_ERROR, "Browsing turned off.");
-
-      cupsdStopBrowsing();
-      Browsing = 0;
-    }
+    next = s->next;
 
-    return;
-  }
-
-  packet[bytes] = '\0';
-
- /*
-  * If we're about to sleep, ignore incoming browse packets.
-  */
-
-  if (Sleeping)
-    return;
-
- /*
-  * Figure out where it came from...
-  */
-
-#ifdef AF_INET6
-  if (srcaddr.addr.sa_family == AF_INET6)
-  {
-    address[0] = ntohl(srcaddr.ipv6.sin6_addr.s6_addr32[0]);
-    address[1] = ntohl(srcaddr.ipv6.sin6_addr.s6_addr32[1]);
-    address[2] = ntohl(srcaddr.ipv6.sin6_addr.s6_addr32[2]);
-    address[3] = ntohl(srcaddr.ipv6.sin6_addr.s6_addr32[3]);
-  }
-  else
-#endif /* AF_INET6 */
-  {
-    address[0] = 0;
-    address[1] = 0;
-    address[2] = 0;
-    address[3] = ntohl(srcaddr.ipv4.sin_addr.s_addr);
-  }
-
-  if (HostNameLookups)
-    httpAddrLookup(&srcaddr, srcname, sizeof(srcname));
-  else
-    httpAddrString(&srcaddr, srcname, sizeof(srcname));
+   /* 
+    * Load a cupsd_printer_t structure with the SLP service attributes...
+    */
 
-  len = strlen(srcname);
+    SLPFindAttrs(BrowseSLPHandle, s->url, "", "", slp_attr_callback, &p);
 
- /*
-  * Do ACL stuff...
-  */
  /*
+    * Process this printer entry...
+    */
 
-  if (BrowseACL)
-  {
-    if (httpAddrLocalhost(&srcaddr) || !strcasecmp(srcname, "localhost"))
-    {
-     /*
-      * Access from localhost (127.0.0.1) is always allowed...
-      */
+    uri = s->url + SLP_CUPS_SRVLEN + 1;
 
-      auth = AUTH_ALLOW;
-    }
-    else
+    if (!strncmp(uri, "http://", 7) || !strncmp(uri, "ipp://", 6))
     {
      /*
-      * Do authorization checks on the domain/address...
+      * Pull the URI apart to see if this is a local or remote printer...
       */
 
-      switch (BrowseACL->order_type)
-      {
-        default :
-           auth = AUTH_DENY;   /* anti-compiler-warning-code */
-           break;
-
-       case AUTH_ALLOW : /* Order Deny,Allow */
-            auth = AUTH_ALLOW;
-
-            if (cupsdCheckAuth(address, srcname, len,
-                         BrowseACL->num_deny, BrowseACL->deny))
-             auth = AUTH_DENY;
-
-            if (cupsdCheckAuth(address, srcname, len,
-                         BrowseACL->num_allow, BrowseACL->allow))
-             auth = AUTH_ALLOW;
-           break;
-
-       case AUTH_DENY : /* Order Allow,Deny */
-            auth = AUTH_DENY;
-
-            if (cupsdCheckAuth(address, srcname, len,
-                         BrowseACL->num_allow, BrowseACL->allow))
-             auth = AUTH_ALLOW;
-
-            if (cupsdCheckAuth(address, srcname, len,
-                         BrowseACL->num_deny, BrowseACL->deny))
-             auth = AUTH_DENY;
-           break;
-      }
+      if (!is_local_queue(uri, host, sizeof(host), resource, sizeof(resource)))
+        process_browse_data(uri, host, resource, p.type, IPP_PRINTER_IDLE,
+                           p.location,  p.info, p.make_model, 0, NULL);
     }
-  }
-  else
-    auth = AUTH_ALLOW;
-
-  if (auth == AUTH_DENY)
-  {
-    cupsdLogMessage(CUPSD_LOG_DEBUG,
-                    "cupsdUpdateCUPSBrowse: Refused %d bytes from %s", bytes,
-                    srcname);
-    return;
-  }
-
-  cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "cupsdUpdateCUPSBrowse: (%d bytes from %s) %s", bytes,
-                 srcname, packet);
-
- /*
-  * Parse packet...
-  */
-
-  if (sscanf(packet, "%x%x%1023s", &type, &state, uri) < 3)
-  {
-    cupsdLogMessage(CUPSD_LOG_WARN,
-                    "cupsdUpdateCUPSBrowse: Garbled browse packet - %s", packet);
-    return;
-  }
-
-  strcpy(location, "Location Unknown");
-  strcpy(info, "No Information Available");
-  make_model[0] = '\0';
-  num_attrs     = 0;
-  attrs         = NULL;
 
-  if ((pptr = strchr(packet, '\"')) != NULL)
-  {
    /*
-    * Have extended information; can't use sscanf for it because not all
-    * sscanf's allow empty strings with %[^\"]...
+    * Free this listing...
     */
 
-    for (i = 0, pptr ++;
-         i < (sizeof(location) - 1) && *pptr && *pptr != '\"';
-         i ++, pptr ++)
-      location[i] = *pptr;
+    cupsdClearString(&p.info);
+    cupsdClearString(&p.location);
+    cupsdClearString(&p.make_model);
 
-    if (i)
-      location[i] = '\0';
+    free(s);
+  }       
+}
+#endif /* HAVE_LIBSLP */
 
-    if (*pptr == '\"')
-      pptr ++;
 
-    while (*pptr && isspace(*pptr & 255))
-      pptr ++;
+/*
+ * 'dequote()' - Remote quotes from a string.
+ */
 
-    if (*pptr == '\"')
-    {
-      for (i = 0, pptr ++;
-           i < (sizeof(info) - 1) && *pptr && *pptr != '\"';
-           i ++, pptr ++)
-       info[i] = *pptr;
+static char *                          /* O - Dequoted string */
+dequote(char       *d,                 /* I - Destination string */
+        const char *s,                 /* I - Source string */
+       int        dlen)                /* I - Destination length */
+{
+  char *dptr;                          /* Pointer into destination */
 
-      info[i] = '\0';
 
-      if (*pptr == '\"')
-       pptr ++;
+  if (s)
+  {
+    for (dptr = d, dlen --; *s && dlen > 0; s ++)
+      if (*s != '\"')
+      {
+       *dptr++ = *s;
+       dlen --;
+      }
 
-      while (*pptr && isspace(*pptr & 255))
-       pptr ++;
+    *dptr = '\0';
+  }
+  else
+    *d = '\0';
 
-      if (*pptr == '\"')
-      {
-       for (i = 0, pptr ++;
-             i < (sizeof(make_model) - 1) && *pptr && *pptr != '\"';
-             i ++, pptr ++)
-         make_model[i] = *pptr;
+  return (d);
+}
 
-       if (*pptr == '\"')
-         pptr ++;
 
-       make_model[i] = '\0';
+/*
+ * 'is_local_queue()' - Determine whether the URI points at a local queue.
+ */
 
-        if (*pptr)
-         num_attrs = cupsParseOptions(pptr, num_attrs, &attrs);
-      }
-    }
-  }
+static int                             /* O - 1 = local, 0 = remote, -1 = bad URI */
+is_local_queue(const char *uri,                /* I - Printer URI */
+               char       *host,       /* O - Host string */
+              int        hostlen,      /* I - Length of host buffer */
+               char       *resource,   /* O - Resource string */
+              int        resourcelen)  /* I - Length of resource buffer */
+{
+  char         scheme[32],             /* Scheme portion of URI */
+               username[HTTP_MAX_URI]; /* Username portion of URI */
+  int          port;                   /* Port portion of URI */
+  cupsd_netif_t        *iface;                 /* Network interface */
 
-  DEBUG_puts(packet);
-  DEBUG_printf(("type=%x, state=%x, uri=\"%s\"\n"
-                "location=\"%s\", info=\"%s\", make_model=\"%s\"\n",
-               type, state, uri, location, info, make_model));
 
  /*
   * Pull the URI apart to see if this is a local or remote printer...
   */
 
-  if (is_local_queue(uri, host, sizeof(host), resource, sizeof(resource)))
-  {
-    cupsFreeOptions(num_attrs, attrs);
-    return;
-  }
+  if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme),
+                      username, sizeof(username), host, hostlen, &port,
+                     resource, resourcelen) < HTTP_URI_OK)
+    return (-1);
+
+  DEBUG_printf(("host=\"%s\", ServerName=\"%s\"\n", host, ServerName));
 
  /*
-  * Do relaying...
+  * Check for local server addresses...
   */
 
-  for (i = 0; i < NumRelays; i ++)
-    if (cupsdCheckAuth(address, srcname, len, 1, &(Relays[i].from)))
-      if (sendto(BrowseSocket, packet, bytes, 0,
-                 (struct sockaddr *)&(Relays[i].to),
-                sizeof(http_addr_t)) <= 0)
-      {
-       cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "cupsdUpdateCUPSBrowse: sendto failed for relay %d - %s.",
-                       i + 1, strerror(errno));
-       cupsFreeOptions(num_attrs, attrs);
-       return;
-      }
+  if (!strcasecmp(host, ServerName) && port == LocalPort)
+    return (1);
+
+  cupsdNetIFUpdate();
+
+  for (iface = (cupsd_netif_t *)cupsArrayFirst(NetIFList);
+       iface;
+       iface = (cupsd_netif_t *)cupsArrayNext(NetIFList))
+    if (!strcasecmp(host, iface->hostname) && port == iface->port)
+      return (1);
 
  /*
-  * Process the browse data...
+  * If we get here, the printer is remote...
   */
 
-  process_browse_data(uri, host, resource, (cups_ptype_t)type,
-                      (ipp_pstate_t)state, location, info, make_model,
-                     num_attrs, attrs);
+  return (0);
 }
 
 
-#ifdef HAVE_OPENLDAP
 /*
- * 'cupsdUpdateLDAPBrowse()' - Scan for new printers via LDAP...
+ * 'process_browse_data()' - Process new browse data.
  */
 
-void
-cupsdUpdateLDAPBrowse(void)
+static void
+process_browse_data(
+    const char    *uri,                        /* I - URI of printer/class */
+    const char    *host,               /* I - Hostname */
+    const char    *resource,           /* I - Resource path */
+    cups_ptype_t  type,                        /* I - Printer type */
+    ipp_pstate_t  state,               /* I - Printer state */
+    const char    *location,           /* I - Printer location */
+    const char    *info,               /* I - Printer information */
+    const char    *make_model,         /* I - Printer make and model */
+    int                  num_attrs,            /* I - Number of attributes */
+    cups_option_t *attrs)              /* I - Attributes */
 {
-  char         uri[HTTP_MAX_URI],      /* Printer URI */
-               host[HTTP_MAX_URI],     /* Hostname */
-               resource[HTTP_MAX_URI], /* Resource path */
-               location[1024],         /* Printer location */
-               info[1024],             /* Printer information */
-               make_model[1024],       /* Printer make and model */
-               **value;                /* Holds the returned data from LDAP */
-  int          type;                   /* Printer type */
-  int          rc;                     /* LDAP status */
-  int          limit;                  /* Size limit */
-  LDAPMessage  *res,                   /* LDAP search results */
-                 *e;                   /* Current entry from search */
+  int          i;                      /* Looping var */
+  int          update;                 /* Update printer attributes? */
+  char         finaluri[HTTP_MAX_URI], /* Final URI for printer */
+               name[IPP_MAX_NAME],     /* Name of printer */
+               newname[IPP_MAX_NAME],  /* New name of printer */
+               *hptr,                  /* Pointer into hostname */
+               *sptr;                  /* Pointer into ServerName */
+  char         local_make_model[IPP_MAX_NAME];
+                                       /* Local make and model */
+  cupsd_printer_t *p;                  /* Printer information */
+  const char   *ipp_options,           /* ipp-options value */
+               *lease_duration;        /* lease-duration value */
 
 
  /*
-  * Search for printers...
+  * Determine if the URI contains any illegal characters in it...
   */
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "UpdateLDAPBrowse: %s", ServerName);
-
-  BrowseLDAPRefresh = time(NULL) + BrowseInterval;
-
-  rc = ldap_search_s(BrowseLDAPHandle, BrowseLDAPDN, LDAP_SCOPE_SUBTREE,
-                     "(objectclass=cupsPrinter)", (char **)ldap_attrs, 0, &res);
-  if (rc != LDAP_SUCCESS) 
+  if (strncmp(uri, "ipp://", 6) || !host[0] ||
+      (strncmp(resource, "/printers/", 10) &&
+       strncmp(resource, "/classes/", 9)))
   {
     cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "LDAP search returned error %d: %s", rc,
-                   ldap_err2string(rc));
+                    "process_browse_data: Bad printer URI in browse data: %s",
+                    uri);
     return;
   }
 
-  limit = ldap_count_entries(BrowseLDAPHandle, res);
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "LDAP search returned %d entries", limit);
-  if (limit < 1)
+  if (strchr(resource, '?') ||
+      (!strncmp(resource, "/printers/", 10) && strchr(resource + 10, '/')) ||
+      (!strncmp(resource, "/classes/", 9) && strchr(resource + 9, '/')))
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "process_browse_data: Bad resource in browse data: %s",
+                    resource);
     return;
+  }
 
  /*
-  * Loop through the available printers...
+  * OK, this isn't a local printer; add any remote options...
   */
 
-  if ((e = ldap_first_entry(BrowseLDAPHandle, res)) == NULL)
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to get LDAP printer entry!");
-    return;
-  }
+  ipp_options = cupsGetOption("ipp-options", num_attrs, attrs);
 
-  while (e)
+  if (BrowseRemoteOptions)
   {
-    value = ldap_get_values(BrowseLDAPHandle, e, "printerDescription");
-    strlcpy(info, *value, sizeof(info));
-    ldap_value_free(value);
-
-    value = ldap_get_values(BrowseLDAPHandle, e, "printerLocation");
-    strlcpy(location, *value, sizeof(location));
-    ldap_value_free(value);
-
-    value = ldap_get_values(BrowseLDAPHandle, e, "printerMakeAndModel");
-    strlcpy(make_model, *value, sizeof(make_model));
-    ldap_value_free(value);
+    if (BrowseRemoteOptions[0] == '?')
+    {
+     /*
+      * Override server-supplied options...
+      */
 
-    value = ldap_get_values(BrowseLDAPHandle, e, "printerType");
-    type = atoi(*value);
-    ldap_value_free(value);
+      snprintf(finaluri, sizeof(finaluri), "%s%s", uri, BrowseRemoteOptions);
+    }
+    else if (ipp_options)
+    {
+     /*
+      * Combine the server and local options...
+      */
 
-    value = ldap_get_values(BrowseLDAPHandle, e, "printerURI");
-    strlcpy(uri, *value, sizeof(uri));
-    ldap_value_free(value);
+      snprintf(finaluri, sizeof(finaluri), "%s?%s+%s", uri, ipp_options,
+               BrowseRemoteOptions);
+    }
+    else
+    {
+     /*
+      * Just use the local options...
+      */
 
-    if (!is_local_queue(uri, host, sizeof(host), resource, sizeof(resource)))
-      process_browse_data(uri, host, resource, type, IPP_PRINTER_IDLE,
-                          location, info, make_model, 0, NULL);
+      snprintf(finaluri, sizeof(finaluri), "%s?%s", uri, BrowseRemoteOptions);
+    }
 
-    e = ldap_next_entry(BrowseLDAPHandle, e);
+    uri = finaluri;
   }
-}
-#endif /* HAVE_OPENLDAP */
-
-
-/*
- * 'cupsdUpdatePolling()' - Read status messages from the poll daemons.
- */
-
-void
-cupsdUpdatePolling(void)
-{
-  char         *ptr,                   /* Pointer to end of line in buffer */
-               message[1024];          /* Pointer to message text */
-  int          loglevel;               /* Log level for message */
-
-
-  while ((ptr = cupsdStatBufUpdate(PollStatusBuffer, &loglevel,
-                                   message, sizeof(message))) != NULL)
-    if (!strchr(PollStatusBuffer->buffer, '\n'))
-      break;
-
-  if (ptr == NULL)
+  else if (ipp_options)
   {
    /*
-    * All polling processes have died; stop polling...
+    * Just use the server-supplied options...
     */
 
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "cupsdUpdatePolling: all polling processes have exited!");
-    cupsdStopPolling();
+    snprintf(finaluri, sizeof(finaluri), "%s?%s", uri, ipp_options);
+    uri = finaluri;
   }
-}
-
-
-#ifdef HAVE_LIBSLP 
-/*
- * 'cupsdUpdateSLPBrowse()' - Get browsing information via SLP.
- */
-
-void
-cupsdUpdateSLPBrowse(void)
-{
-  slpsrvurl_t  *s,                     /* Temporary list of service URLs */
-               *next;                  /* Next service in list */
-  cupsd_printer_t p;                   /* Printer information */
-  const char   *uri;                   /* Pointer to printer URI */
-  char         host[HTTP_MAX_URI],     /* Host portion of URI */
-               resource[HTTP_MAX_URI]; /* Resource portion of URI */
-
 
  /*
-  * Reset the refresh time...
-  */
-
-  BrowseSLPRefresh = time(NULL) + BrowseInterval;
-
- /* 
-  * Poll for remote printers using SLP...
+  * See if we already have it listed in the Printers list, and add it if not...
   */
 
-  s = NULL;
-
-  SLPFindSrvs(BrowseSLPHandle, SLP_CUPS_SRVTYPE, "", "",
-             slp_url_callback, &s);
-
- /*
-  * Loop through the list of available printers...
-  */
+  type   |= CUPS_PRINTER_REMOTE;
+  type   &= ~CUPS_PRINTER_IMPLICIT;
+  update = 0;
+  hptr   = strchr(host, '.');
+  sptr   = strchr(ServerName, '.');
 
-  for (; s; s = next)
+  if (sptr != NULL && hptr != NULL)
   {
    /*
-    * Save the "next" pointer...
-    */
-
-    next = s->next;
-
-   /* 
-    * Load a cupsd_printer_t structure with the SLP service attributes...
+    * Strip the common domain name components...
     */
 
-    SLPFindAttrs(BrowseSLPHandle, s->url, "", "", slp_attr_callback, &p);
+    while (hptr != NULL)
+    {
+      if (!strcasecmp(hptr, sptr))
+      {
+        *hptr = '\0';
+       break;
+      }
+      else
+        hptr = strchr(hptr + 1, '.');
+    }
+  }
 
+  if (type & CUPS_PRINTER_CLASS)
+  {
    /*
-    * Process this printer entry...
+    * Remote destination is a class...
     */
 
-    uri = s->url + SLP_CUPS_SRVLEN + 1;
+    if (!strncmp(resource, "/classes/", 9))
+      snprintf(name, sizeof(name), "%s@%s", resource + 9, host);
+    else
+      return;
 
-    if (!strncmp(uri, "http://", 7) || !strncmp(uri, "ipp://", 6))
+    if ((p = cupsdFindClass(name)) == NULL && BrowseShortNames)
     {
-     /*
-      * Pull the URI apart to see if this is a local or remote printer...
-      */
+      if ((p = cupsdFindClass(resource + 9)) != NULL)
+      {
+        if (p->hostname && strcasecmp(p->hostname, host))
+       {
+        /*
+         * Nope, this isn't the same host; if the hostname isn't the local host,
+         * add it to the other class and then find a class using the full host
+         * name...
+         */
 
-      if (!is_local_queue(uri, host, sizeof(host), resource, sizeof(resource)))
-        process_browse_data(uri, host, resource, p.type, IPP_PRINTER_IDLE,
-                           p.location,  p.info, p.make_model, 0, NULL);
-    }
+         if (p->type & CUPS_PRINTER_REMOTE)
+         {
+           cupsdLogMessage(CUPSD_LOG_INFO,
+                           "Renamed remote class \"%s\" to \"%s@%s\"...",
+                           p->name, p->name, p->hostname);
+           cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, p, NULL,
+                         "Class \'%s\' deleted by directory services.",
+                         p->name);
 
-   /*
-    * Free this listing...
-    */
-
-    cupsdClearString(&p.info);
-    cupsdClearString(&p.location);
-    cupsdClearString(&p.make_model);
-
-    free(s);
-  }       
-}
-#endif /* HAVE_LIBSLP */
-
-
-/*
- * 'dequote()' - Remote quotes from a string.
- */
-
-static char *                          /* O - Dequoted string */
-dequote(char       *d,                 /* I - Destination string */
-        const char *s,                 /* I - Source string */
-       int        dlen)                /* I - Destination length */
-{
-  char *dptr;                          /* Pointer into destination */
-
-
-  if (s)
-  {
-    for (dptr = d, dlen --; *s && dlen > 0; s ++)
-      if (*s != '\"')
-      {
-       *dptr++ = *s;
-       dlen --;
-      }
-
-    *dptr = '\0';
-  }
-  else
-    *d = '\0';
-
-  return (d);
-}
-
-
-/*
- * 'is_local_queue()' - Determine whether the URI points at a local queue.
- */
-
-static int                             /* O - 1 = local, 0 = remote, -1 = bad URI */
-is_local_queue(const char *uri,                /* I - Printer URI */
-               char       *host,       /* O - Host string */
-              int        hostlen,      /* I - Length of host buffer */
-               char       *resource,   /* O - Resource string */
-              int        resourcelen)  /* I - Length of resource buffer */
-{
-  char         scheme[32],             /* Scheme portion of URI */
-               username[HTTP_MAX_URI]; /* Username portion of URI */
-  int          port;                   /* Port portion of URI */
-  cupsd_netif_t        *iface;                 /* Network interface */
-
-
- /*
-  * Pull the URI apart to see if this is a local or remote printer...
-  */
-
-  if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme),
-                      username, sizeof(username), host, hostlen, &port,
-                     resource, resourcelen) < HTTP_URI_OK)
-    return (-1);
-
-  DEBUG_printf(("host=\"%s\", ServerName=\"%s\"\n", host, ServerName));
-
- /*
-  * Check for local server addresses...
-  */
-
-  if (!strcasecmp(host, ServerName) && port == LocalPort)
-    return (1);
-
-  cupsdNetIFUpdate();
-
-  for (iface = (cupsd_netif_t *)cupsArrayFirst(NetIFList);
-       iface;
-       iface = (cupsd_netif_t *)cupsArrayNext(NetIFList))
-    if (!strcasecmp(host, iface->hostname) && port == iface->port)
-      return (1);
-
- /*
-  * If we get here, the printer is remote...
-  */
-
-  return (0);
-}
-
-
-/*
- * 'process_browse_data()' - Process new browse data.
- */
-
-static void
-process_browse_data(
-    const char    *uri,                        /* I - URI of printer/class */
-    const char    *host,               /* I - Hostname */
-    const char    *resource,           /* I - Resource path */
-    cups_ptype_t  type,                        /* I - Printer type */
-    ipp_pstate_t  state,               /* I - Printer state */
-    const char    *location,           /* I - Printer location */
-    const char    *info,               /* I - Printer information */
-    const char    *make_model,         /* I - Printer make and model */
-    int                  num_attrs,            /* I - Number of attributes */
-    cups_option_t *attrs)              /* I - Attributes */
-{
-  int          i;                      /* Looping var */
-  int          update;                 /* Update printer attributes? */
-  char         finaluri[HTTP_MAX_URI], /* Final URI for printer */
-               name[IPP_MAX_NAME],     /* Name of printer */
-               newname[IPP_MAX_NAME],  /* New name of printer */
-               *hptr,                  /* Pointer into hostname */
-               *sptr;                  /* Pointer into ServerName */
-  char         local_make_model[IPP_MAX_NAME];
-                                       /* Local make and model */
-  cupsd_printer_t *p;                  /* Printer information */
-  const char   *ipp_options,           /* ipp-options value */
-               *lease_duration;        /* lease-duration value */
-
-
- /*
-  * Determine if the URI contains any illegal characters in it...
-  */
-
-  if (strncmp(uri, "ipp://", 6) || !host[0] ||
-      (strncmp(resource, "/printers/", 10) &&
-       strncmp(resource, "/classes/", 9)))
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "process_browse_data: Bad printer URI in browse data: %s",
-                    uri);
-    return;
-  }
-
-  if (strchr(resource, '?') ||
-      (!strncmp(resource, "/printers/", 10) && strchr(resource + 10, '/')) ||
-      (!strncmp(resource, "/classes/", 9) && strchr(resource + 9, '/')))
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "process_browse_data: Bad resource in browse data: %s",
-                    resource);
-    return;
-  }
-
- /*
-  * OK, this isn't a local printer; add any remote options...
-  */
-
-  ipp_options = cupsGetOption("ipp-options", num_attrs, attrs);
-
-  if (BrowseRemoteOptions)
-  {
-    if (BrowseRemoteOptions[0] == '?')
-    {
-     /*
-      * Override server-supplied options...
-      */
-
-      snprintf(finaluri, sizeof(finaluri), "%s%s", uri, BrowseRemoteOptions);
-    }
-    else if (ipp_options)
-    {
-     /*
-      * Combine the server and local options...
-      */
-
-      snprintf(finaluri, sizeof(finaluri), "%s?%s+%s", uri, ipp_options,
-               BrowseRemoteOptions);
-    }
-    else
-    {
-     /*
-      * Just use the local options...
-      */
-
-      snprintf(finaluri, sizeof(finaluri), "%s?%s", uri, BrowseRemoteOptions);
-    }
-
-    uri = finaluri;
-  }
-  else if (ipp_options)
-  {
-   /*
-    * Just use the server-supplied options...
-    */
-
-    snprintf(finaluri, sizeof(finaluri), "%s?%s", uri, ipp_options);
-    uri = finaluri;
-  }
-
- /*
-  * See if we already have it listed in the Printers list, and add it if not...
-  */
-
-  type   |= CUPS_PRINTER_REMOTE;
-  type   &= ~CUPS_PRINTER_IMPLICIT;
-  update = 0;
-  hptr   = strchr(host, '.');
-  sptr   = strchr(ServerName, '.');
-
-  if (sptr != NULL && hptr != NULL)
-  {
-   /*
-    * Strip the common domain name components...
-    */
-
-    while (hptr != NULL)
-    {
-      if (!strcasecmp(hptr, sptr))
-      {
-        *hptr = '\0';
-       break;
-      }
-      else
-        hptr = strchr(hptr + 1, '.');
-    }
-  }
-
-  if (type & CUPS_PRINTER_CLASS)
-  {
-   /*
-    * Remote destination is a class...
-    */
-
-    if (!strncmp(resource, "/classes/", 9))
-      snprintf(name, sizeof(name), "%s@%s", resource + 9, host);
-    else
-      return;
-
-    if ((p = cupsdFindClass(name)) == NULL && BrowseShortNames)
-    {
-      if ((p = cupsdFindClass(resource + 9)) != NULL)
-      {
-        if (p->hostname && strcasecmp(p->hostname, host))
-       {
-        /*
-         * Nope, this isn't the same host; if the hostname isn't the local host,
-         * add it to the other class and then find a class using the full host
-         * name...
-         */
-
-         if (p->type & CUPS_PRINTER_REMOTE)
-         {
-           cupsdLogMessage(CUPSD_LOG_INFO,
-                           "Renamed remote class \"%s\" to \"%s@%s\"...",
-                           p->name, p->name, p->hostname);
-           cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, p, NULL,
-                         "Class \'%s\' deleted by directory services.",
-                         p->name);
-
-            snprintf(newname, sizeof(newname), "%s@%s", p->name, p->hostname);
-            cupsdRenamePrinter(p, newname);
+            snprintf(newname, sizeof(newname), "%s@%s", p->name, p->hostname);
+            cupsdRenamePrinter(p, newname);
 
            cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, p, NULL,
                          "Class \'%s\' added by directory services.",
@@ -2907,7 +2430,488 @@ process_implicit_classes(void)
 }
 
 
-#ifdef HAVE_LIBSLP
+/*
+ * 'send_cups_browse()' - Send new browsing information using the CUPS
+ *                           protocol.
+ */
+
+static void
+send_cups_browse(cupsd_printer_t *p)   /* I - Printer to send */
+{
+  int                  i;              /* Looping var */
+  cups_ptype_t         type;           /* Printer type */
+  cupsd_dirsvc_addr_t  *b;             /* Browse address */
+  int                  bytes;          /* Length of packet */
+  char                 packet[1453],   /* Browse data packet */
+                       uri[1024],      /* Printer URI */
+                       location[1024], /* printer-location */
+                       info[1024],     /* printer-info */
+                       make_model[1024];
+                                       /* printer-make-and-model */
+  cupsd_netif_t                *iface;         /* Network interface */
+
+
+ /*
+  * Figure out the printer type value...
+  */
+
+  type = p->type | CUPS_PRINTER_REMOTE;
+
+  if (!p->accepting)
+    type |= CUPS_PRINTER_REJECTING;
+
+  if (p == DefaultPrinter)
+    type |= CUPS_PRINTER_DEFAULT;
+
+ /*
+  * Remove quotes from printer-info, printer-location, and
+  * printer-make-and-model attributes...
+  */
+
+  dequote(location, p->location, sizeof(location));
+  dequote(info, p->info, sizeof(info));
+  dequote(make_model, p->make_model ? p->make_model : "Unknown",
+          sizeof(make_model));
+
+ /*
+  * Send a packet to each browse address...
+  */
+
+  for (i = NumBrowsers, b = Browsers; i > 0; i --, b ++)
+    if (b->iface[0])
+    {
+     /*
+      * Send the browse packet to one or more interfaces...
+      */
+
+      if (!strcmp(b->iface, "*"))
+      {
+       /*
+        * Send to all local interfaces...
+       */
+
+        cupsdNetIFUpdate();
+
+       for (iface = (cupsd_netif_t *)cupsArrayFirst(NetIFList);
+            iface;
+            iface = (cupsd_netif_t *)cupsArrayNext(NetIFList))
+       {
+        /*
+         * Only send to local, IPv4 interfaces...
+         */
+
+         if (!iface->is_local || !iface->port ||
+             iface->address.addr.sa_family != AF_INET)
+           continue;
+
+         httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
+                          iface->hostname, iface->port,
+                          (p->type & CUPS_PRINTER_CLASS) ? "/classes/%s%s" :
+                                                           "/printers/%s",
+                          p->name);
+         snprintf(packet, sizeof(packet), "%x %x %s \"%s\" \"%s\" \"%s\" %s\n",
+                  type, p->state, uri, location, info, make_model,
+                  p->browse_attrs ? p->browse_attrs : "");
+
+         bytes = strlen(packet);
+
+         cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                         "cupsdSendBrowseList: (%d bytes to \"%s\") %s", bytes,
+                         iface->name, packet);
+
+          iface->broadcast.ipv4.sin_port = htons(BrowsePort);
+
+         sendto(BrowseSocket, packet, bytes, 0,
+                (struct sockaddr *)&(iface->broadcast),
+                sizeof(struct sockaddr_in));
+        }
+      }
+      else if ((iface = cupsdNetIFFind(b->iface)) != NULL)
+      {
+       /*
+        * Send to the named interface using the IPv4 address...
+       */
+
+        while (iface)
+         if (strcmp(b->iface, iface->name))
+         {
+           iface = NULL;
+           break;
+         }
+         else if (iface->address.addr.sa_family == AF_INET && iface->port)
+           break;
+         else
+            iface = (cupsd_netif_t *)cupsArrayNext(NetIFList);
+
+        if (iface)
+       {
+         httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
+                          iface->hostname, iface->port,
+                          (p->type & CUPS_PRINTER_CLASS) ? "/classes/%s%s" :
+                                                           "/printers/%s",
+                          p->name);
+         snprintf(packet, sizeof(packet), "%x %x %s \"%s\" \"%s\" \"%s\" %s\n",
+                  type, p->state, uri, location, info, make_model,
+                  p->browse_attrs ? p->browse_attrs : "");
+
+         bytes = strlen(packet);
+
+         cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                         "cupsdSendBrowseList: (%d bytes to \"%s\") %s", bytes,
+                         iface->name, packet);
+
+          iface->broadcast.ipv4.sin_port = htons(BrowsePort);
+
+         sendto(BrowseSocket, packet, bytes, 0,
+                (struct sockaddr *)&(iface->broadcast),
+                sizeof(struct sockaddr_in));
+        }
+      }
+    }
+    else
+    {
+     /*
+      * Send the browse packet to the indicated address using
+      * the default server name...
+      */
+
+      snprintf(packet, sizeof(packet), "%x %x %s \"%s\" \"%s\" \"%s\" %s\n",
+                      type, p->state, p->uri, location, info, make_model,
+              p->browse_attrs ? p->browse_attrs : "");
+
+      bytes = strlen(packet);
+      cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                      "cupsdSendBrowseList: (%d bytes) %s", bytes, packet);
+
+      if (sendto(BrowseSocket, packet, bytes, 0,
+                (struct sockaddr *)&(b->to),
+                sizeof(struct sockaddr_in)) <= 0)
+      {
+       /*
+        * Unable to send browse packet, so remove this address from the
+       * list...
+       */
+
+       cupsdLogMessage(CUPSD_LOG_ERROR,
+                       "cupsdSendBrowseList: sendto failed for browser "
+                       "%d - %s.",
+                       (int)(b - Browsers + 1), strerror(errno));
+
+        if (i > 1)
+         memmove(b, b + 1, (i - 1) * sizeof(cupsd_dirsvc_addr_t));
+
+       b --;
+       NumBrowsers --;
+      }
+    }
+}
+
+
+#ifdef HAVE_OPENLDAP
+/*
+ * 'send_ldap_browse()' - Send LDAP printer registrations.
+ */
+
+static void
+send_ldap_browse(cupsd_printer_t *p)   /* I - Printer to register */
+{
+  int          i;                      /* Looping var... */
+  LDAPMod      mods[7];                /* The 7 attributes we will be adding */
+  LDAPMod      *pmods[8];              /* Pointers to the 7 attributes + NULL */
+  LDAPMessage  *res;                   /* Search result token */
+  char         *cn_value[2],           /* Change records */
+               *uri[2],
+               *info[2],
+               *location[2],
+               *make_model[2],
+               *type[2],
+               typestring[255],        /* String to hold printer-type */
+               filter[256],            /* Search filter for possible UPDATEs */
+               dn[1024];               /* DN of the printer we are adding */
+  int          rc;                     /* LDAP status */
+  static const char * const objectClass_values[] =
+               {                       /* The 3 objectClass's we use in */
+                 "top",                /* our LDAP entries              */
+                 "device",
+                 "cupsPrinter",
+                 NULL
+               };
+
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_ldap_browse: %s\n", p->name);
+
+ /*
+  * Everything in ldap is ** so we fudge around it...
+  */
+
+  sprintf(typestring, "%u", p->type);
+
+  cn_value[0]   = p->info;
+  cn_value[1]   = NULL;
+  info[0]       = p->info;
+  info[1]       = NULL;
+  location[0]   = p->location;
+  location[1]   = NULL;
+  make_model[0] = p->make_model;
+  make_model[1] = NULL;
+  type[0]       = typestring;
+  type[1]       = NULL;
+  uri[0]        = p->uri;
+  uri[1]        = NULL;
+
+  snprintf(filter, sizeof(filter),
+           "(&(objectclass=cupsPrinter)(printerDescription~=%s))", p->info);
+
+  ldap_search_s(BrowseLDAPHandle, BrowseLDAPDN, LDAP_SCOPE_SUBTREE,
+                filter, (char **)ldap_attrs, 0, &res);
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_ldap_browse: Searching \"%s\"",
+                  filter);
+
+  mods[0].mod_type = "cn";
+  mods[0].mod_values = cn_value;
+  mods[1].mod_type = "printerDescription";
+  mods[1].mod_values = info;
+  mods[2].mod_type = "printerURI";
+  mods[2].mod_values = uri;
+  mods[3].mod_type = "printerLocation";
+  mods[3].mod_values = location;
+  mods[4].mod_type = "printerMakeAndModel";
+  mods[4].mod_values = make_model;
+  mods[5].mod_type = "printerType";
+  mods[5].mod_values = type;
+  mods[6].mod_type = "objectClass";
+  mods[6].mod_values = (char **)objectClass_values;
+
+  snprintf(dn, sizeof(dn), "cn=%s,ou=printers,%s", p->info, BrowseLDAPDN);
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_ldap_browse: dn=\"%s\"", dn);
+
+  if (ldap_count_entries(BrowseLDAPHandle, res) > 0)
+  {
+   /*
+    * Printer has already been registered, modify the current
+    * registration...
+    */
+
+    cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                    "send_ldap_browse: Replacing entry...");
+
+    for (i = 0; i < 7; i ++)
+    {
+      pmods[i]         = mods + i;
+      pmods[i]->mod_op = LDAP_MOD_REPLACE;
+    }
+    pmods[i] = NULL;
+
+    if ((rc = ldap_modify_s(BrowseLDAPHandle, dn, pmods)) != LDAP_SUCCESS)
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                      "LDAP modify for %s failed with status %d: %s",
+                      p->name, rc, ldap_err2string(rc));
+  }
+  else 
+  {
+   /*
+    * Printer has already been registered, modify the current
+    * registration...
+    */
+
+    cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                    "send_ldap_browse: Adding entry...");
+
+    for (i = 0; i < 7; i ++)
+    {
+      pmods[i]         = mods + i;
+      pmods[i]->mod_op = LDAP_MOD_REPLACE;
+    }
+    pmods[i] = NULL;
+
+    if ((rc = ldap_modify_s(BrowseLDAPHandle, dn, pmods)) != LDAP_SUCCESS)
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                      "LDAP add for %s failed with status %d: %s",
+                      p->name, rc, ldap_err2string(rc));
+  }
+}
+#endif /* HAVE_OPENLDAP */
+
+
+#ifdef HAVE_LIBSLP
+/*
+ * 'send_slp_browse()' - Register the specified printer with SLP.
+ */
+
+static void
+send_slp_browse(cupsd_printer_t *p)    /* I - Printer to register */
+{
+  char         srvurl[HTTP_MAX_URI],   /* Printer service URI */
+               attrs[8192],            /* Printer attributes */
+               finishings[1024],       /* Finishings to support */
+               make_model[IPP_MAX_NAME * 2],
+                                       /* Make and model, quoted */
+               location[IPP_MAX_NAME * 2],
+                                       /* Location, quoted */
+               info[IPP_MAX_NAME * 2], /* Info, quoted */
+               *src,                   /* Pointer to original string */
+               *dst;                   /* Pointer to destination string */
+  ipp_attribute_t *authentication;     /* uri-authentication-supported value */
+  SLPError     error;                  /* SLP error, if any */
+
+
+  cupsdLogMessage(CUPSD_LOG_DEBUG, "send_slp_browse(%p = \"%s\")", p,
+                  p->name);
+
+ /*
+  * Make the SLP service URL that conforms to the IANA 
+  * 'printer:' template.
+  */
+
+  snprintf(srvurl, sizeof(srvurl), SLP_CUPS_SRVTYPE ":%s", p->uri);
+
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "Service URL = \"%s\"", srvurl);
+
+ /*
+  * Figure out the finishings string...
+  */
+
+  if (p->type & CUPS_PRINTER_STAPLE)
+    strcpy(finishings, "staple");
+  else
+    finishings[0] = '\0';
+
+  if (p->type & CUPS_PRINTER_BIND)
+  {
+    if (finishings[0])
+      strlcat(finishings, ",bind", sizeof(finishings));
+    else
+      strcpy(finishings, "bind");
+  }
+
+  if (p->type & CUPS_PRINTER_PUNCH)
+  {
+    if (finishings[0])
+      strlcat(finishings, ",punch", sizeof(finishings));
+    else
+      strcpy(finishings, "punch");
+  }
+
+  if (p->type & CUPS_PRINTER_COVER)
+  {
+    if (finishings[0])
+      strlcat(finishings, ",cover", sizeof(finishings));
+    else
+      strcpy(finishings, "cover");
+  }
+
+  if (p->type & CUPS_PRINTER_SORT)
+  {
+    if (finishings[0])
+      strlcat(finishings, ",sort", sizeof(finishings));
+    else
+      strcpy(finishings, "sort");
+  }
+
+  if (!finishings[0])
+    strcpy(finishings, "none");
+
+ /*
+  * Quote any commas in the make and model, location, and info strings...
+  */
+
+  for (src = p->make_model, dst = make_model;
+       src && *src && dst < (make_model + sizeof(make_model) - 2);)
+  {
+    if (*src == ',' || *src == '\\' || *src == ')')
+      *dst++ = '\\';
+
+    *dst++ = *src++;
+  }
+
+  *dst = '\0';
+
+  if (!make_model[0])
+    strcpy(make_model, "Unknown");
+
+  for (src = p->location, dst = location;
+       src && *src && dst < (location + sizeof(location) - 2);)
+  {
+    if (*src == ',' || *src == '\\' || *src == ')')
+      *dst++ = '\\';
+
+    *dst++ = *src++;
+  }
+
+  *dst = '\0';
+
+  if (!location[0])
+    strcpy(location, "Unknown");
+
+  for (src = p->info, dst = info;
+       src && *src && dst < (info + sizeof(info) - 2);)
+  {
+    if (*src == ',' || *src == '\\' || *src == ')')
+      *dst++ = '\\';
+
+    *dst++ = *src++;
+  }
+
+  *dst = '\0';
+
+  if (!info[0])
+    strcpy(info, "Unknown");
+
+ /*
+  * Get the authentication value...
+  */
+
+  authentication = ippFindAttribute(p->attrs, "uri-authentication-supported",
+                                    IPP_TAG_KEYWORD);
+
+ /*
+  * Make the SLP attribute string list that conforms to
+  * the IANA 'printer:' template.
+  */
+
+  snprintf(attrs, sizeof(attrs),
+           "(printer-uri-supported=%s),"
+           "(uri-authentication-supported=%s>),"
+#ifdef HAVE_SSL
+           "(uri-security-supported=tls>),"
+#else
+           "(uri-security-supported=none>),"
+#endif /* HAVE_SSL */
+           "(printer-name=%s),"
+           "(printer-location=%s),"
+           "(printer-info=%s),"
+           "(printer-more-info=%s),"
+           "(printer-make-and-model=%s),"
+          "(printer-type=%d),"
+          "(charset-supported=utf-8),"
+          "(natural-language-configured=%s),"
+          "(natural-language-supported=de,en,es,fr,it),"
+           "(color-supported=%s),"
+           "(finishings-supported=%s),"
+           "(sides-supported=one-sided%s),"
+          "(multiple-document-jobs-supported=true)"
+          "(ipp-versions-supported=1.0,1.1)",
+          p->uri, authentication->values[0].string.text, p->name, location,
+          info, p->uri, make_model, p->type, DefaultLanguage,
+           p->type & CUPS_PRINTER_COLOR ? "true" : "false",
+           finishings,
+           p->type & CUPS_PRINTER_DUPLEX ?
+              ",two-sided-long-edge,two-sided-short-edge" : "");
+
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "Attributes = \"%s\"", attrs);
+
+ /*
+  * Register the printer with the SLP server...
+  */
+
+  error = SLPReg(BrowseSLPHandle, srvurl, BrowseTimeout,
+                SLP_CUPS_SRVTYPE, attrs, SLP_TRUE, slp_reg_callback, 0);
+
+  if (error != SLP_OK)
+    cupsdLogMessage(CUPSD_LOG_ERROR, "SLPReg of \"%s\" failed with status %d!", p->name,
+                    error);
+}
+
+
 /*
  * 'slp_attr_callback()' - SLP attribute callback 
  */
@@ -3111,5 +3115,5 @@ slp_url_callback(
 
 
 /*
- * End of "$Id: dirsvc.c 5223 2006-03-04 01:10:17Z mike $".
+ * End of "$Id: dirsvc.c 5305 2006-03-18 03:05:12Z mike $".
  */