]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/dirsvc.c
License change: Apache License, Version 2.0.
[thirdparty/cups.git] / scheduler / dirsvc.c
index 1a826c295a4a36edcafff08f6f90d487192f9e22..518418a32e677fe2bff40bfe2786f25996c1671b 100644 (file)
@@ -1,59 +1,10 @@
 /*
- * "$Id: dirsvc.c 6649 2007-07-11 21:46:42Z mike $"
+ * Directory services routines for the CUPS scheduler.
  *
- *   Directory services routines for the Common UNIX Printing System (CUPS).
+ * Copyright 2007-2017 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
- *   Copyright 2007 by Apple Inc.
- *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
- *
- *   These coded instructions, statements, and computer programs are the
- *   property of Apple Inc. and are protected by Federal copyright
- *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
- *   which should have been included with this file.  If this file is
- *   file is missing or damaged, see the license at "http://www.cups.org/".
- *
- * Contents:
- *
- *   cupsdDeregisterPrinter()      - Stop sending broadcast information for a 
- *                                  local printer and remove any pending
- *                                   references to remote printers.
- *   cupsdLoadRemoteCache()        - Load the remote printer cache.
- *   cupsdRegisterPrinter()        - Start sending broadcast information for a
- *                                   printer update the broadcast contents.
- *   cupsdSaveRemoteCache()        - Save the remote printer cache.
- *   cupsdSendBrowseList()         - Send new browsing information as necessary.
- *   cupsdStartBrowsing()          - Start sending and receiving broadcast
- *                                   information.
- *   cupsdStartPolling()           - Start polling servers as needed.
- *   cupsdStopBrowsing()           - Stop sending and receiving broadcast
- *                                   information.
- *   cupsdStopPolling()            - Stop polling servers as needed.
- *   cupsdUpdateDNSSDBrowse()      - Handle DNS-SD queries.
- *   cupsdUpdateLDAPBrowse()       - Scan for new printers via LDAP...
- *   cupsdUpdateSLPBrowse()        - Get browsing information via SLP.
- *   dnssdBuildTxtRecord()         - Build a TXT record from printer info.
- *   dnssdDeregisterPrinter()      - Stop sending broadcast information for a
- *                                   printer.
- *   dnssdPackTxtRecord()          - Pack an array of key/value pairs into the
- *                                   TXT record format.
- *   dnssdRegisterCallback()       - DNSServiceRegister callback.
- *   dnssdRegisterPrinter()        - Start sending broadcast information for a
- *                                   printer or update the broadcast contents.
- *   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.
- *   slp_reg_callback()            - Empty SLPRegReport.
- *   slp_url_callback()            - SLP service url callback
- *   update_cups_browse()          - Update the browse lists using the CUPS
- *                                   protocol.
- *   update_polling()              - Read status messages from the poll daemons.
+ * Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
  */
 
 /*
 #include "cupsd.h"
 #include <grp.h>
 
-#ifdef HAVE_DNSSD
-#  include <dns_sd.h>
-#  include <nameser.h>
+#if defined(HAVE_DNSSD) && defined(__APPLE__)
 #  include <nameser.h>
-#  ifdef HAVE_COREFOUNDATION
-#    include <CoreFoundation/CoreFoundation.h>
-#  endif /* HAVE_COREFOUNDATION */
-#  ifdef HAVE_SYSTEMCONFIGURATION
-#    include <SystemConfiguration/SystemConfiguration.h>
-#  endif /* HAVE_SYSTEMCONFIGURATION */
-#endif /* HAVE_DNSSD */
-
-
-/*
- * Local functions...
- */
+#  include <CoreFoundation/CoreFoundation.h>
+#  include <SystemConfiguration/SystemConfiguration.h>
+#endif /* HAVE_DNSSD && __APPLE__ */
 
-static char    *dequote(char *d, const char *s, int dlen);
-static int     is_local_queue(const char *uri, char *host, int hostlen,
-                              char *resource, int resourcelen);
-static void    process_browse_data(const char *uri, const char *host,
-                                   const char *resource, cups_ptype_t type,
-                                   ipp_pstate_t state, const char *location,
-                                   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 */
-#ifdef HAVE_LIBSLP
-static void    send_slp_browse(cupsd_printer_t *p);
-#endif /* HAVE_LIBSLP */
-static void    update_cups_browse(void);
-static void    update_polling(void);
-
-
-#ifdef HAVE_OPENLDAP
-static const char * const ldap_attrs[] =/* CUPS LDAP attributes */
-               {
-                 "printerDescription",
-                 "printerLocation",
-                 "printerMakeAndModel",
-                 "printerType",
-                 "printerURI",
-                 NULL
-               };
-#endif /* HAVE_OPENLDAP */
-
-#ifdef HAVE_LIBSLP 
-/*
- * SLP definitions...
- */
 
 /*
- * SLP service name for CUPS...
- */
-
-#  define SLP_CUPS_SRVTYPE     "service:printer"
-#  define SLP_CUPS_SRVLEN      15
-
-
-/* 
- * Printer service URL structure
+ * Local globals...
  */
 
-typedef struct _slpsrvurl_s            /**** SLP URL list ****/
-{
-  struct _slpsrvurl_s  *next;          /* Next URL in list */
-  char                 url[HTTP_MAX_URI];
-                                       /* URL */
-} slpsrvurl_t;
+#ifdef HAVE_AVAHI
+static int     avahi_running = 0;
+#endif /* HAVE_AVAHI */
 
 
 /*
  * Local functions...
  */
 
-static SLPBoolean      slp_attr_callback(SLPHandle hslp, const char *attrlist,
-                                         SLPError errcode, void *cookie);
-static void            slp_dereg_printer(cupsd_printer_t *p);
-static int             slp_get_attr(const char *attrlist, const char *tag,
-                                    char **valbuf);
-static void            slp_reg_callback(SLPHandle hslp, SLPError errcode,
-                                        void *cookie);
-static SLPBoolean      slp_url_callback(SLPHandle hslp, const char *srvurl,
-                                        unsigned short lifetime,
-                                        SLPError errcode, void *cookie);
-#endif /* HAVE_LIBSLP */
-
-#ifdef HAVE_DNSSD
-/*
- * For IPP register using a subtype of 'cups' so that shared printer browsing
- * only finds other CUPS servers (not all IPP based printers).
- */
-static char    dnssdIPPRegType[]    = "_ipp._tcp,_cups";
-static char    dnssdIPPFaxRegType[] = "_fax-ipp._tcp";
-
-static char    *dnssdBuildTxtRecord(int *txt_len, cupsd_printer_t *p);
-static void    dnssdDeregisterPrinter(cupsd_printer_t *p);
-static char    *dnssdPackTxtRecord(int *txt_len, char *keyvalue[][2],
-                                   int count);
-static void    dnssdRegisterCallback(DNSServiceRef sdRef,
-                                     DNSServiceFlags flags, 
-                                     DNSServiceErrorType errorCode,
-                                     const char *name, const char *regtype,
-                                     const char *domain, void *context);
-static void    dnssdRegisterPrinter(cupsd_printer_t *p);
-#endif /* HAVE_DNSSD */
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+static char            *get_auth_info_required(cupsd_printer_t *p,
+                                               char *buffer, size_t bufsize);
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+#ifdef __APPLE__
+static int             get_hostconfig(const char *name);
+#endif /* __APPLE__ */
+static void            update_lpd(int onoff);
+static void            update_smb(int onoff);
+
+
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+#  ifdef __APPLE__
+static void            dnssdAddAlias(const void *key, const void *value,
+                                     void *context);
+#  endif /* __APPLE__ */
+static cupsd_txt_t     dnssdBuildTxtRecord(cupsd_printer_t *p, int for_lpd);
+#  ifdef HAVE_AVAHI
+static void            dnssdClientCallback(AvahiClient *c, AvahiClientState state, void *userdata);
+#  endif /* HAVE_AVAHI */
+static void            dnssdDeregisterAllPrinters(int from_callback);
+static void            dnssdDeregisterInstance(cupsd_srv_t *srv, int from_callback);
+static void            dnssdDeregisterPrinter(cupsd_printer_t *p, int clear_name, int from_callback);
+static const char      *dnssdErrorString(int error);
+static void            dnssdFreeTxtRecord(cupsd_txt_t *txt);
+static void            dnssdRegisterAllPrinters(int from_callback);
+#  ifdef HAVE_DNSSD
+static void            dnssdRegisterCallback(DNSServiceRef sdRef,
+                                             DNSServiceFlags flags,
+                                             DNSServiceErrorType errorCode,
+                                             const char *name,
+                                             const char *regtype,
+                                             const char *domain,
+                                             void *context);
+#  else
+static void            dnssdRegisterCallback(AvahiEntryGroup *p,
+                                             AvahiEntryGroupState state,
+                                             void *context);
+#  endif /* HAVE_DNSSD */
+static int             dnssdRegisterInstance(cupsd_srv_t *srv, cupsd_printer_t *p, char *name, const char *type, const char *subtypes, int port, cupsd_txt_t *txt, int commit, int from_callback);
+static void            dnssdRegisterPrinter(cupsd_printer_t *p, int from_callback);
+static void            dnssdStop(void);
+#  ifdef HAVE_DNSSD
+static void            dnssdUpdate(void);
+#  endif /* HAVE_DNSSD */
+static void            dnssdUpdateDNSSDName(int from_callback);
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
 
 
 /*
- * 'cupsdDeregisterPrinter()' - Stop sending broadcast information for a 
+ * 'cupsdDeregisterPrinter()' - Stop sending broadcast information for a
  *                             local printer and remove any pending
  *                              references to remote printers.
  */
@@ -186,3629 +95,1762 @@ cupsdDeregisterPrinter(
     int             removeit)          /* I - Printer being permanently removed */
 {
  /*
-  * Only deregister if browsing is enabled and it's a local printers...
+  * Only deregister if browsing is enabled and it's a local printer...
   */
 
+  cupsdLogMessage(CUPSD_LOG_DEBUG,
+                  "cupsdDeregisterPrinter(p=%p(%s), removeit=%d)", p, p->name,
+                 removeit);
+
   if (!Browsing || !p->shared ||
-      (p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)))
+      (p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_SCANNER)))
     return;
 
  /*
   * Announce the deletion...
   */
 
-  if ((BrowseLocalProtocols & BROWSE_CUPS))
-  {
-    cups_ptype_t savedtype = p->type;  /* Saved printer type */
-
-    p->type |= CUPS_PRINTER_DELETE;
-
-    send_cups_browse(p);
-
-    p->type = savedtype;
-  }
-
-#ifdef HAVE_LIBSLP
-  if (BrowseLocalProtocols & BROWSE_SLP)
-    slp_dereg_printer(p);
-#endif /* HAVE_LIBSLP */
-
-#ifdef HAVE_DNSSD
-  if (removeit && (BrowseLocalProtocols & BROWSE_DNSSD))
-    dnssdDeregisterPrinter(p);
-#endif /* HAVE_DNSSD */
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+  if (removeit && (BrowseLocalProtocols & BROWSE_DNSSD) && DNSSDMaster)
+    dnssdDeregisterPrinter(p, 1, 0);
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
 }
 
 
 /*
- * 'cupsdLoadRemoteCache()' - Load the remote printer cache.
+ * 'cupsdRegisterPrinter()' - Start sending broadcast information for a
+ *                            printer or update the broadcast contents.
  */
 
 void
-cupsdLoadRemoteCache(void)
+cupsdRegisterPrinter(cupsd_printer_t *p)/* I - Printer */
 {
-  cups_file_t          *fp;            /* remote.cache file */
-  int                  linenum;        /* Current line number */
-  char                 line[1024],     /* Line from file */
-                       *value,         /* Pointer to value */
-                       *valueptr,      /* Pointer into value */
-                       scheme[32],     /* Scheme portion of URI */
-                       username[64],   /* Username portion of URI */
-                       host[HTTP_MAX_HOST],
-                                       /* Hostname portion of URI */
-                       resource[HTTP_MAX_URI];
-                                       /* Resource portion of URI */
-  int                  port;           /* Port number */
-  cupsd_printer_t      *p;             /* Current printer */
-  time_t               now;            /* Current time */
-
-
- /*
-  * Don't load the cache if the CUPS remote protocol is disabled...
-  */
+  cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdRegisterPrinter(p=%p(%s))", p,
+                  p->name);
 
-  if (!Browsing || !(BrowseRemoteProtocols & BROWSE_CUPS))
-  {
-    cupsdLogMessage(CUPSD_LOG_DEBUG,
-                    "cupsdLoadRemoteCache: Not loading remote cache.");
+  if (!Browsing || !BrowseLocalProtocols ||
+      (p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_SCANNER)))
     return;
-  }
 
- /*
-  * Open the remote.cache file...
-  */
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+  if ((BrowseLocalProtocols & BROWSE_DNSSD) && DNSSDMaster)
+    dnssdRegisterPrinter(p, 0);
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+}
 
-  snprintf(line, sizeof(line), "%s/remote.cache", CacheDir);
-  if ((fp = cupsFileOpen(line, "r")) == NULL)
-    return;
 
- /*
 * Read printer configurations until we hit EOF...
 */
+/*
* 'cupsdStartBrowsing()' - Start sending and receiving broadcast information.
+ */
 
-  linenum = 0;
-  p       = NULL;
-  now     = time(NULL);
+void
+cupsdStartBrowsing(void)
+{
+  if (!Browsing || !BrowseLocalProtocols)
+    return;
 
-  while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+  if (BrowseLocalProtocols & BROWSE_DNSSD)
   {
+#  ifdef HAVE_DNSSD
+    DNSServiceErrorType error;         /* Error from service creation */
+
    /*
-    * Decode the directive...
+    * First create a "master" connection for all registrations...
     */
 
-    if (!strcasecmp(line, "<Printer") ||
-        !strcasecmp(line, "<DefaultPrinter"))
-    {
-     /*
-      * <Printer name> or <DefaultPrinter name>
-      */
-
-      if (p == NULL && value)
-      {
-       /*
-        * Add the printer and a base file type...
-       */
-
-        cupsdLogMessage(CUPSD_LOG_DEBUG,
-                       "cupsdLoadRemoteCache: Loading printer %s...", value);
-
-        if ((p = cupsdFindDest(value)) != NULL)
-       {
-         if (p->type & CUPS_PRINTER_CLASS)
-         {
-           cupsdLogMessage(CUPSD_LOG_WARN,
-                           "Cached remote printer \"%s\" conflicts with "
-                           "existing class!",
-                           value);
-           p = NULL;
-           continue;
-         }
-       }
-       else
-          p = cupsdAddPrinter(value);
-
-       p->accepting     = 1;
-       p->state         = IPP_PRINTER_IDLE;
-       p->type          |= CUPS_PRINTER_REMOTE | CUPS_PRINTER_DISCOVERED;
-       p->browse_time   = now;
-       p->browse_expire = now + BrowseTimeout;
-
-       /*
-        * Set the default printer as needed...
-       */
-
-        if (!strcasecmp(line, "<DefaultPrinter"))
-         DefaultPrinter = p;
-      }
-      else
-      {
-        cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "Syntax error on line %d of remote.cache.", linenum);
-        return;
-      }
-    }
-    else if (!strcasecmp(line, "<Class") ||
-             !strcasecmp(line, "<DefaultClass"))
-    {
-     /*
-      * <Class name> or <DefaultClass name>
-      */
-
-      if (p == NULL && value)
-      {
-       /*
-        * Add the printer and a base file type...
-       */
-
-        cupsdLogMessage(CUPSD_LOG_DEBUG,
-                       "cupsdLoadRemoteCache: Loading class %s...", value);
-
-        if ((p = cupsdFindDest(value)) != NULL)
-         p->type = CUPS_PRINTER_CLASS;
-       else
-          p = cupsdAddClass(value);
-
-       p->accepting     = 1;
-       p->state         = IPP_PRINTER_IDLE;
-       p->type          |= CUPS_PRINTER_REMOTE | CUPS_PRINTER_DISCOVERED;
-       p->browse_time   = now;
-       p->browse_expire = now + BrowseTimeout;
-
-       /*
-        * Set the default printer as needed...
-       */
-
-        if (!strcasecmp(line, "<DefaultClass"))
-         DefaultPrinter = p;
-      }
-      else
-      {
-        cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "Syntax error on line %d of remote.cache.", linenum);
-        return;
-      }
-    }
-    else if (!strcasecmp(line, "</Printer>") ||
-             !strcasecmp(line, "</Class>"))
-    {
-      if (p != NULL)
-      {
-       /*
-        * Close out the current printer...
-       */
-
-        cupsdSetPrinterAttrs(p);
-
-        p = NULL;
-      }
-      else
-      {
-        cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "Syntax error on line %d of remote.cache.", linenum);
-        return;
-      }
-    }
-    else if (!p)
+    if ((error = DNSServiceCreateConnection(&DNSSDMaster))
+           != kDNSServiceErr_NoError)
     {
       cupsdLogMessage(CUPSD_LOG_ERROR,
-                      "Syntax error on line %d of remote.cache.", linenum);
-      return;
-    }
-    else if (!strcasecmp(line, "Info"))
-    {
-      if (value)
-       cupsdSetString(&p->info, value);
-    }
-    else if (!strcasecmp(line, "MakeModel"))
-    {
-      if (value)
-       cupsdSetString(&p->make_model, value);
-    }
-    else if (!strcasecmp(line, "Location"))
-    {
-      if (value)
-       cupsdSetString(&p->location, value);
-    }
-    else if (!strcasecmp(line, "DeviceURI"))
-    {
-      if (value)
-      {
-       httpSeparateURI(HTTP_URI_CODING_ALL, value, scheme, sizeof(scheme),
-                       username, sizeof(username), host, sizeof(host), &port,
-                       resource, sizeof(resource));
+                     "Unable to create master DNS-SD reference: %d", error);
 
-       cupsdSetString(&p->hostname, host);
-       cupsdSetString(&p->uri, value);
-       cupsdSetString(&p->device_uri, value);
-      }
-      else
-      {
-       cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "Syntax error on line %d of remote.cache.", linenum);
-       return;
-      }
+      if (FatalErrors & CUPSD_FATAL_BROWSE)
+       cupsdEndProcess(getpid(), 0);
     }
-    else if (!strcasecmp(line, "Option") && value)
+    else
     {
      /*
-      * Option name value
+      * Add the master connection to the select list...
       */
 
-      for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++);
+      int fd = DNSServiceRefSockFD(DNSSDMaster);
 
-      if (!*valueptr)
-        cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "Syntax error on line %d of remote.cache.", linenum);
-      else
-      {
-        for (; *valueptr && isspace(*valueptr & 255); *valueptr++ = '\0');
+      fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
 
-        p->num_options = cupsAddOption(value, valueptr, p->num_options,
-                                      &(p->options));
-      }
+      cupsdAddSelect(fd, (cupsd_selfunc_t)dnssdUpdate, NULL, NULL);
     }
-    else if (!strcasecmp(line, "State"))
-    {
-     /*
-      * Set the initial queue state...
-      */
 
-      if (value && !strcasecmp(value, "idle"))
-        p->state = IPP_PRINTER_IDLE;
-      else if (value && !strcasecmp(value, "stopped"))
-        p->state = IPP_PRINTER_STOPPED;
-      else
-      {
-       cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "Syntax error on line %d of remote.cache.", linenum);
-       return;
-      }
-    }
-    else if (!strcasecmp(line, "StateMessage"))
-    {
-     /*
-      * Set the initial queue state message...
-      */
+   /*
+    * Set the computer name and register the web interface...
+    */
 
-      if (value)
-       strlcpy(p->state_message, value, sizeof(p->state_message));
-    }
-    else if (!strcasecmp(line, "Accepting"))
-    {
-     /*
-      * Set the initial accepting state...
-      */
+    DNSSDPort = 0;
+    cupsdUpdateDNSSDName();
 
-      if (value &&
-          (!strcasecmp(value, "yes") ||
-           !strcasecmp(value, "on") ||
-           !strcasecmp(value, "true")))
-        p->accepting = 1;
-      else if (value &&
-               (!strcasecmp(value, "no") ||
-               !strcasecmp(value, "off") ||
-               !strcasecmp(value, "false")))
-        p->accepting = 0;
-      else
-      {
-       cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "Syntax error on line %d of remote.cache.", linenum);
-       return;
-      }
-    }
-    else if (!strcasecmp(line, "Type"))
-    {
-      if (value)
-        p->type = atoi(value);
-      else
-      {
-       cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "Syntax error on line %d of remote.cache.", linenum);
-       return;
-      }
-    }
-    else if (!strcasecmp(line, "BrowseTime"))
+#  else /* HAVE_AVAHI */
+    if ((DNSSDMaster = avahi_threaded_poll_new()) == NULL)
     {
-      if (value)
-      {
-        time_t t = atoi(value);
+      cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create DNS-SD thread.");
 
-       if (t > p->browse_expire)
-          p->browse_expire = t;
-      }
-      else
-      {
-       cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "Syntax error on line %d of remote.cache.", linenum);
-       return;
-      }
+      if (FatalErrors & CUPSD_FATAL_BROWSE)
+       cupsdEndProcess(getpid(), 0);
     }
-    else if (!strcasecmp(line, "JobSheets"))
+    else
     {
-     /*
-      * Set the initial job sheets...
-      */
-
-      if (value)
-      {
-       for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++);
-
-       if (*valueptr)
-          *valueptr++ = '\0';
-
-       cupsdSetString(&p->job_sheets[0], value);
+      int error;                       /* Error code, if any */
 
-       while (isspace(*valueptr & 255))
-          valueptr ++;
+      DNSSDClient = avahi_client_new(avahi_threaded_poll_get(DNSSDMaster), AVAHI_CLIENT_NO_FAIL, dnssdClientCallback, NULL, &error);
 
-       if (*valueptr)
-       {
-          for (value = valueptr; *valueptr && !isspace(*valueptr & 255); valueptr ++);
+      if (DNSSDClient == NULL)
+      {
+        cupsdLogMessage(CUPSD_LOG_ERROR,
+                        "Unable to communicate with avahi-daemon: %s",
+                        dnssdErrorString(error));
 
-         if (*valueptr)
-            *valueptr++ = '\0';
+        if (FatalErrors & CUPSD_FATAL_BROWSE)
+         cupsdEndProcess(getpid(), 0);
 
-         cupsdSetString(&p->job_sheets[1], value);
-       }
-      }
-      else
-      {
-       cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "Syntax error on line %d of remote.cache.", linenum);
-       return;
-      }
-    }
-    else if (!strcasecmp(line, "AllowUser"))
-    {
-      if (value)
-      {
-        p->deny_users = 0;
-        cupsdAddPrinterUser(p, value);
-      }
-      else
-      {
-       cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "Syntax error on line %d of remote.cache.", linenum);
-       return;
-      }
-    }
-    else if (!strcasecmp(line, "DenyUser"))
-    {
-      if (value)
-      {
-        p->deny_users = 1;
-        cupsdAddPrinterUser(p, value);
+        avahi_threaded_poll_free(DNSSDMaster);
+        DNSSDMaster = NULL;
       }
       else
-      {
-       cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "Syntax error on line %d of remote.cache.", linenum);
-       return;
-      }
-    }
-    else
-    {
-     /*
-      * Something else we don't understand...
-      */
-
-      cupsdLogMessage(CUPSD_LOG_ERROR,
-                      "Unknown configuration directive %s on line %d of remote.cache.",
-                     line, linenum);
+       avahi_threaded_poll_start(DNSSDMaster);
     }
+#  endif /* HAVE_DNSSD */
   }
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+
+ /*
+  * Enable LPD and SMB printer sharing as needed through external programs...
+  */
 
-  cupsFileClose(fp);
+  if (BrowseLocalProtocols & BROWSE_LPD)
+    update_lpd(1);
 
+  if (BrowseLocalProtocols & BROWSE_SMB)
+    update_smb(1);
+
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
  /*
-  * Do auto-classing if needed...
+  * Register the individual printers
   */
 
-  process_implicit_classes();
+  dnssdRegisterAllPrinters(0);
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
 }
 
 
 /*
- * 'cupsdRegisterPrinter()' - Start sending broadcast information for a
- *                            printer or update the broadcast contents.
+ * 'cupsdStopBrowsing()' - Stop sending and receiving broadcast information.
  */
 
 void
-cupsdRegisterPrinter(cupsd_printer_t *p)/* I - Printer */
+cupsdStopBrowsing(void)
 {
-  if (!Browsing || !BrowseLocalProtocols || !BrowseInterval || !NumBrowsers ||
-      (p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)))
+  if (!Browsing || !BrowseLocalProtocols)
     return;
 
-#ifdef HAVE_LIBSLP
-/*  if (BrowseLocalProtocols & BROWSE_SLP)
-    slpRegisterPrinter(p); */
-#endif /* HAVE_LIBSLP */
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+ /*
+  * De-register the individual printers
+  */
+
+  dnssdDeregisterAllPrinters(0);
 
-#ifdef HAVE_DNSSD
-  if (BrowseLocalProtocols & BROWSE_DNSSD)
-    dnssdRegisterPrinter(p);
-#endif /* HAVE_DNSSD */
+ /*
+  * Shut down browsing sockets...
+  */
+
+  if ((BrowseLocalProtocols & BROWSE_DNSSD) && DNSSDMaster)
+    dnssdStop();
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+
+ /*
+  * Disable LPD and SMB printer sharing as needed through external programs...
+  */
+
+  if (BrowseLocalProtocols & BROWSE_LPD)
+    update_lpd(0);
+
+  if (BrowseLocalProtocols & BROWSE_SMB)
+    update_smb(0);
 }
 
 
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
 /*
- * 'cupsdRestartPolling()' - Restart polling servers as needed.
+ * 'cupsdUpdateDNSSDName()' - Update the computer name we use for browsing...
  */
 
 void
-cupsdRestartPolling(void)
+cupsdUpdateDNSSDName(void)
 {
-  int                  i;              /* Looping var */
-  cupsd_dirsvc_poll_t  *pollp;         /* Current polling server */
-
-
-  for (i = 0, pollp = Polled; i < NumPolled; i ++, pollp ++)
-    if (pollp->pid)
-      kill(pollp->pid, SIGHUP);
+  dnssdUpdateDNSSDName(0);
 }
 
 
+#  ifdef __APPLE__
 /*
- * 'cupsdSaveRemoteCache()' - Save the remote printer cache.
+ * 'dnssdAddAlias()' - Add a DNS-SD alias name.
  */
 
-void
-cupsdSaveRemoteCache(void)
+static void
+dnssdAddAlias(const void *key,         /* I - Key */
+              const void *value,       /* I - Value (domain) */
+             void       *context)      /* I - Unused */
 {
-  int                  i;              /* Looping var */
-  cups_file_t          *fp;            /* printers.conf file */
-  char                 temp[1024];     /* Temporary string */
-  cupsd_printer_t      *printer;       /* Current printer class */
-  time_t               curtime;        /* Current time */
-  struct tm            *curdate;       /* Current date */
-  cups_option_t                *option;        /* Current option */
-
+  char valueStr[1024],                 /* Domain string */
+       hostname[1024],                 /* Complete hostname */
+       *hostptr;                       /* Pointer into hostname */
 
- /*
-  * Create the remote.cache file...
-  */
 
-  snprintf(temp, sizeof(temp), "%s/remote.cache", CacheDir);
+  (void)key;
+  (void)context;
 
-  if ((fp = cupsFileOpen(temp, "w")) == NULL)
+  if (CFGetTypeID((CFStringRef)value) == CFStringGetTypeID() &&
+      CFStringGetCString((CFStringRef)value, valueStr, sizeof(valueStr),
+                         kCFStringEncodingUTF8))
   {
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "Unable to save remote.cache - %s", strerror(errno));
-    return;
+    snprintf(hostname, sizeof(hostname), "%s.%s", DNSSDHostName, valueStr);
+    hostptr = hostname + strlen(hostname) - 1;
+    if (*hostptr == '.')
+      *hostptr = '\0';                 /* Strip trailing dot */
+
+    if (!DNSSDAlias)
+      DNSSDAlias = cupsArrayNew(NULL, NULL);
+
+    cupsdAddAlias(DNSSDAlias, hostname);
+    cupsdLogMessage(CUPSD_LOG_DEBUG, "Added Back to My Mac ServerAlias %s",
+                   hostname);
   }
   else
-    cupsdLogMessage(CUPSD_LOG_DEBUG, "Saving remote.cache...");
-
- /*
-  * Restrict access to the file...
-  */
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "Bad Back to My Mac domain in dynamic store!");
+}
+#  endif /* __APPLE__ */
 
-  fchown(cupsFileNumber(fp), getuid(), Group);
-  fchmod(cupsFileNumber(fp), ConfigFilePerm);
 
- /*
 * Write a small header to the file...
 */
+/*
* 'dnssdBuildTxtRecord()' - Build a TXT record from printer info.
+ */
 
-  curtime = time(NULL);
-  curdate = localtime(&curtime);
-  strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", curdate);
+static cupsd_txt_t                     /* O - TXT record */
+dnssdBuildTxtRecord(
+    cupsd_printer_t *p,                        /* I - Printer information */
+    int             for_lpd)           /* I - 1 = LPD, 0 = IPP */
+{
+  int          i,                      /* Looping var */
+               count;                  /* Count of key/value pairs */
+  char         admin_hostname[256],    /* Hostname for admin page */
+               adminurl_str[256],      /* URL for the admin page */
+               type_str[32],           /* Type to string buffer */
+               state_str[32],          /* State to string buffer */
+               rp_str[1024],           /* Queue name string buffer */
+               air_str[1024],          /* auth-info-required string buffer */
+               *keyvalue[32][2],       /* Table of key/value pairs */
+                *ptr;                   /* Pointer in string */
+  cupsd_txt_t  txt;                    /* TXT record */
+  cupsd_listener_t *lis;                /* Current listener */
+  const char    *admin_scheme = "http"; /* Admin page URL scheme */
 
-  cupsFilePuts(fp, "# Remote cache file for " CUPS_SVERSION "\n");
-  cupsFilePrintf(fp, "# Written by cupsd on %s\n", temp);
 
  /*
-  * Write each local printer known to the system...
+  * Load up the key value pairs...
   */
 
-  for (printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
-       printer;
-       printer = (cupsd_printer_t *)cupsArrayNext(Printers))
-  {
-   /*
-    * Skip local destinations...
-    */
+  count = 0;
 
-    if (!(printer->type & CUPS_PRINTER_DISCOVERED))
-      continue;
+  if (!for_lpd || (BrowseLocalProtocols & BROWSE_LPD))
+  {
+    keyvalue[count  ][0] = "txtvers";
+    keyvalue[count++][1] = "1";
 
-   /*
-    * Write printers as needed...
-    */
+    keyvalue[count  ][0] = "qtotal";
+    keyvalue[count++][1] = "1";
 
-    if (printer == DefaultPrinter)
-      cupsFilePuts(fp, "<Default");
+    keyvalue[count  ][0] = "rp";
+    keyvalue[count++][1] = rp_str;
+    if (for_lpd)
+      strlcpy(rp_str, p->name, sizeof(rp_str));
     else
-      cupsFilePutChar(fp, '<');
+      snprintf(rp_str, sizeof(rp_str), "%s/%s",
+              (p->type & CUPS_PRINTER_CLASS) ? "classes" : "printers",
+              p->name);
 
-    if (printer->type & CUPS_PRINTER_CLASS)
-      cupsFilePrintf(fp, "Class %s>\n", printer->name);
-    else
-      cupsFilePrintf(fp, "Printer %s>\n", printer->name);
+    keyvalue[count  ][0] = "ty";
+    keyvalue[count++][1] = p->make_model ? p->make_model : "Unknown";
 
-    cupsFilePrintf(fp, "Type %d\n", printer->type);
+   /*
+    * Get the hostname for the admin page...
+    */
 
-    cupsFilePrintf(fp, "BrowseTime %d\n", (int)printer->browse_expire);
-
-    if (printer->info)
-      cupsFilePrintf(fp, "Info %s\n", printer->info);
-
-    if (printer->make_model)
-      cupsFilePrintf(fp, "MakeModel %s\n", printer->make_model);
-
-    if (printer->location)
-      cupsFilePrintf(fp, "Location %s\n", printer->location);
-
-    if (printer->device_uri)
-      cupsFilePrintf(fp, "DeviceURI %s\n", printer->device_uri);
-
-    if (printer->state == IPP_PRINTER_STOPPED)
+    if (strchr(DNSSDHostName, '.'))
     {
-      cupsFilePuts(fp, "State Stopped\n");
-      cupsFilePrintf(fp, "StateMessage %s\n", printer->state_message);
-    }
-    else
-      cupsFilePuts(fp, "State Idle\n");
-
-    if (printer->accepting)
-      cupsFilePuts(fp, "Accepting Yes\n");
-    else
-      cupsFilePuts(fp, "Accepting No\n");
-
-    cupsFilePrintf(fp, "JobSheets %s %s\n", printer->job_sheets[0],
-            printer->job_sheets[1]);
-
-    for (i = 0; i < printer->num_users; i ++)
-      cupsFilePrintf(fp, "%sUser %s\n", printer->deny_users ? "Deny" : "Allow",
-              printer->users[i]);
-
-    for (i = printer->num_options, option = printer->options;
-         i > 0;
-        i --, option ++)
-      cupsFilePrintf(fp, "Option %s %s\n", option->name, option->value);
-
-    if (printer->type & CUPS_PRINTER_CLASS)
-      cupsFilePuts(fp, "</Class>\n");
-    else
-      cupsFilePuts(fp, "</Printer>\n");
-  }
-
-  cupsFileClose(fp);
-}
-
-
-/*
- * 'cupsdSendBrowseList()' - Send new browsing information as necessary.
- */
-
-void
-cupsdSendBrowseList(void)
-{
-  int                  count;          /* Number of dests to update */
-  cupsd_printer_t      *p;             /* Current printer */
-  time_t               ut,             /* Minimum update time */
-                       to;             /* Timeout time */
-
-
-  if (!Browsing || !BrowseLocalProtocols || !Printers)
-    return;
-
- /*
-  * Compute the update and timeout times...
-  */
-
-  to = time(NULL);
-  ut = to - BrowseInterval;
-
- /*
-  * Figure out how many printers need an update...
-  */
-
-  if (BrowseInterval > 0)
-  {
-    int        max_count;                      /* Maximum number to update */
-
-
-   /*
-    * Throttle the number of printers we'll be updating this time
-    * around based on the number of queues that need updating and
-    * the maximum number of queues to update each second...
-    */
-
-    max_count = 2 * cupsArrayCount(Printers) / BrowseInterval + 1;
-
-    for (count = 0, p = (cupsd_printer_t *)cupsArrayFirst(Printers);
-         count < max_count && p != NULL;
-        p = (cupsd_printer_t *)cupsArrayNext(Printers))
-      if (!(p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) &&
-          p->shared && p->browse_time < ut)
-        count ++;
-
-   /*
-    * Loop through all of the printers and send local updates as needed...
-    */
+     /*
+      * Use the provided hostname, but make sure it ends with a period...
+      */
 
-    if (BrowseNext)
-      p = (cupsd_printer_t *)cupsArrayFind(Printers, BrowseNext);
+      if ((ptr = DNSSDHostName + strlen(DNSSDHostName) - 1) >= DNSSDHostName && *ptr == '.')
+        strlcpy(admin_hostname, DNSSDHostName, sizeof(admin_hostname));
+      else
+        snprintf(admin_hostname, sizeof(admin_hostname), "%s.", DNSSDHostName);
+    }
     else
-      p = (cupsd_printer_t *)cupsArrayFirst(Printers);
-
-    for (;
-         count > 0;
-        p = (cupsd_printer_t *)cupsArrayNext(Printers))
     {
      /*
-      * Check for wraparound...
+      * Unqualified hostname gets ".local." added to it...
       */
 
-      if (!p)
-        p = (cupsd_printer_t *)cupsArrayFirst(Printers);
-
-      if (!p)
-        break;
-      else if ((p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) ||
-               !p->shared)
-        continue;
-      else if (p->browse_time < ut)
-      {
-       /*
-       * Need to send an update...
-       */
-
-       count --;
-
-       p->browse_time = time(NULL);
-
-       if (BrowseLocalProtocols & BROWSE_CUPS)
-          send_cups_browse(p);
-
-#ifdef HAVE_LIBSLP
-       if (BrowseLocalProtocols & BROWSE_SLP)
-          send_slp_browse(p);
-#endif /* HAVE_LIBSLP */
-
-#ifdef HAVE_LDAP
-       if (BrowseLocalProtocols & BROWSE_LDAP)
-          send_ldap_browse(p);
-#endif /* HAVE_LDAP */
-      }
+      snprintf(admin_hostname, sizeof(admin_hostname), "%s.local.", DNSSDHostName);
     }
 
    /*
-    * Save where we left off so that all printers get updated...
-    */
-
-    BrowseNext = p;
-  }
-
- /*
-  * Loop through all of the printers and send local updates as needed...
-  */
-
-  for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
-       p;
-       p = (cupsd_printer_t *)cupsArrayNext(Printers))
-  {
-   /*
-    * If this is a remote queue, see if it needs to be timed out...
+    * Get the URL scheme for the admin page...
     */
 
-    if (p->type & CUPS_PRINTER_DISCOVERED)
+#  ifdef HAVE_SSL
+    for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners); lis; lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
     {
-      if (p->browse_expire < to)
+      if (lis->encryption != HTTP_ENCRYPTION_NEVER)
       {
-       cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, p, NULL,
-                      "%s \'%s\' deleted by directory services (timeout).",
-                     (p->type & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
-                     p->name);
-
-        cupsdLogMessage(CUPSD_LOG_DEBUG,
-                       "Remote destination \"%s\" has timed out; "
-                       "deleting it...",
-                       p->name);
-
-        cupsArraySave(Printers);
-        cupsdDeletePrinter(p, 1);
-        cupsArrayRestore(Printers);
+        admin_scheme = "https";
+        break;
       }
     }
-  }
-}
-
+#  endif /* HAVE_SSL */
 
-/*
- * 'cupsdStartBrowsing()' - Start sending and receiving broadcast information.
- */
+    httpAssembleURIf(HTTP_URI_CODING_ALL, adminurl_str, sizeof(adminurl_str), admin_scheme,  NULL, admin_hostname, DNSSDPort, "/%s/%s", (p->type & CUPS_PRINTER_CLASS) ? "classes" : "printers", p->name);
+    keyvalue[count  ][0] = "adminurl";
+    keyvalue[count++][1] = adminurl_str;
 
-void
-cupsdStartBrowsing(void)
-{
-  int                  val;            /* Socket option value */
-  struct sockaddr_in   addr;           /* Broadcast address */
-  cupsd_printer_t      *p;             /* Current printer */
+    if (p->location)
+    {
+      keyvalue[count  ][0] = "note";
+      keyvalue[count++][1] = p->location;
+    }
 
+    keyvalue[count  ][0] = "priority";
+    keyvalue[count++][1] = for_lpd ? "100" : "0";
 
-  BrowseNext = NULL;
+    keyvalue[count  ][0] = "product";
+    keyvalue[count++][1] = p->pc && p->pc->product ? p->pc->product : "Unknown";
 
-  if (!Browsing || !(BrowseLocalProtocols | BrowseRemoteProtocols))
-    return;
+    keyvalue[count  ][0] = "pdl";
+    keyvalue[count++][1] = p->pdl ? p->pdl : "application/postscript";
 
-  if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_CUPS)
-  {
-    if (BrowseSocket < 0)
+    if (get_auth_info_required(p, air_str, sizeof(air_str)))
     {
-     /*
-      * Create the broadcast socket...
-      */
-
-      if ((BrowseSocket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
-      {
-       cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "Unable to create broadcast socket - %s.",
-                       strerror(errno));
-       BrowseLocalProtocols &= ~BROWSE_CUPS;
-       BrowseRemoteProtocols &= ~BROWSE_CUPS;
-       return;
-      }
+      keyvalue[count  ][0] = "air";
+      keyvalue[count++][1] = air_str;
+    }
 
-     /*
-      * Bind the socket to browse port...
-      */
+    keyvalue[count  ][0] = "UUID";
+    keyvalue[count++][1] = p->uuid + 9;
 
-      memset(&addr, 0, sizeof(addr));
-      addr.sin_addr.s_addr = htonl(INADDR_ANY);
-      addr.sin_family      = AF_INET;
-      addr.sin_port        = htons(BrowsePort);
+  #ifdef HAVE_SSL
+    keyvalue[count  ][0] = "TLS";
+    keyvalue[count++][1] = "1.2";
+  #endif /* HAVE_SSL */
 
-      if (bind(BrowseSocket, (struct sockaddr *)&addr, sizeof(addr)))
-      {
-       cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "Unable to bind broadcast socket - %s.",
-                       strerror(errno));
-
-#ifdef WIN32
-       closesocket(BrowseSocket);
-#else
-       close(BrowseSocket);
-#endif /* WIN32 */
-
-       BrowseSocket = -1;
-       BrowseLocalProtocols &= ~BROWSE_CUPS;
-       BrowseRemoteProtocols &= ~BROWSE_CUPS;
-       return;
-      }
+    if (p->type & CUPS_PRINTER_FAX)
+    {
+      keyvalue[count  ][0] = "Fax";
+      keyvalue[count++][1] = "T";
+      keyvalue[count  ][0] = "rfo";
+      keyvalue[count++][1] = rp_str;
     }
 
-   /*
-    * Set the "broadcast" flag...
-    */
-
-    val = 1;
-    if (setsockopt(BrowseSocket, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val)))
+    if (p->type & CUPS_PRINTER_COLOR)
     {
-      cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to set broadcast mode - %s.",
-                     strerror(errno));
-
-#ifdef WIN32
-      closesocket(BrowseSocket);
-#else
-      close(BrowseSocket);
-#endif /* WIN32 */
-
-      BrowseSocket = -1;
-      BrowseLocalProtocols &= ~BROWSE_CUPS;
-      BrowseRemoteProtocols &= ~BROWSE_CUPS;
-      return;
+      keyvalue[count  ][0] = "Color";
+      keyvalue[count++][1] = (p->type & CUPS_PRINTER_COLOR) ? "T" : "F";
     }
 
-   /*
-    * Close the socket on exec...
-    */
-
-    fcntl(BrowseSocket, F_SETFD, fcntl(BrowseSocket, F_GETFD) | FD_CLOEXEC);
-
-   /*
-    * Finally, add the socket to the input selection set as needed...
-    */
-
-    if (BrowseRemoteProtocols & BROWSE_CUPS)
+    if (p->type & CUPS_PRINTER_DUPLEX)
     {
-     /*
-      * We only listen if we want remote printers...
-      */
-
-      cupsdAddSelect(BrowseSocket, (cupsd_selfunc_t)update_cups_browse,
-                     NULL, NULL);
+      keyvalue[count  ][0] = "Duplex";
+      keyvalue[count++][1] = (p->type & CUPS_PRINTER_DUPLEX) ? "T" : "F";
     }
-  }
-  else
-    BrowseSocket = -1;
 
-#ifdef HAVE_LIBSLP
-  if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_SLP)
-  {
-   /* 
-    * Open SLP handle...
-    */
+    if (p->type & CUPS_PRINTER_STAPLE)
+    {
+      keyvalue[count  ][0] = "Staple";
+      keyvalue[count++][1] = (p->type & CUPS_PRINTER_STAPLE) ? "T" : "F";
+    }
 
-    if (SLPOpen("en", SLP_FALSE, &BrowseSLPHandle) != SLP_OK)
+    if (p->type & CUPS_PRINTER_COPIES)
     {
-      cupsdLogMessage(CUPSD_LOG_ERROR,
-                      "Unable to open an SLP handle; disabling SLP browsing!");
-      BrowseLocalProtocols &= ~BROWSE_SLP;
-      BrowseRemoteProtocols &= ~BROWSE_SLP;
+      keyvalue[count  ][0] = "Copies";
+      keyvalue[count++][1] = (p->type & CUPS_PRINTER_COPIES) ? "T" : "F";
     }
 
-    BrowseSLPRefresh = 0;
-  }
-  else
-    BrowseSLPHandle = NULL;
-#endif /* HAVE_LIBSLP */
+    if (p->type & CUPS_PRINTER_COLLATE)
+    {
+      keyvalue[count  ][0] = "Collate";
+      keyvalue[count++][1] = (p->type & CUPS_PRINTER_COLLATE) ? "T" : "F";
+    }
 
-#ifdef HAVE_OPENLDAP
-  if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_LDAP)
-  {
-    if (!BrowseLDAPDN)
+    if (p->type & CUPS_PRINTER_PUNCH)
     {
-      cupsdLogMessage(CUPSD_LOG_ERROR,
-                      "Need to set BrowseLDAPDN to use LDAP browsing!");
-      BrowseLocalProtocols &= ~BROWSE_LDAP;
-      BrowseRemoteProtocols &= ~BROWSE_LDAP;
+      keyvalue[count  ][0] = "Punch";
+      keyvalue[count++][1] = (p->type & CUPS_PRINTER_PUNCH) ? "T" : "F";
     }
-    else
+
+    if (p->type & CUPS_PRINTER_BIND)
     {
-     /* 
-      * Open LDAP handle...
-      */
+      keyvalue[count  ][0] = "Bind";
+      keyvalue[count++][1] = (p->type & CUPS_PRINTER_BIND) ? "T" : "F";
+    }
 
-      int              rc;             /* LDAP API status */
-      int              version = 3;    /* LDAP version */
-      struct berval    bv = {0, ""};   /* SASL bind value */
+    if (p->type & CUPS_PRINTER_SORT)
+    {
+      keyvalue[count  ][0] = "Sort";
+      keyvalue[count++][1] = (p->type & CUPS_PRINTER_SORT) ? "T" : "F";
+    }
 
+    if (p->type & CUPS_PRINTER_MFP)
+    {
+      keyvalue[count  ][0] = "Scan";
+      keyvalue[count++][1] = (p->type & CUPS_PRINTER_MFP) ? "T" : "F";
+    }
 
-     /*
-      * Set the certificate file to use for encrypted LDAP sessions...
-      */
+    snprintf(type_str, sizeof(type_str), "0x%X", p->type | CUPS_PRINTER_REMOTE);
+    snprintf(state_str, sizeof(state_str), "%d", p->state);
 
-      if (BrowseLDAPCACertFile)
-      {
-       cupsdLogMessage(CUPSD_LOG_DEBUG,
-                       "cupsdStartBrowsing: Setting CA certificate file \"%s\"",
-                       BrowseLDAPCACertFile);
-
-        if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE,
-                                 (void *)BrowseLDAPCACertFile))
-               != LDAP_SUCCESS)
-          cupsdLogMessage(CUPSD_LOG_ERROR,
-                          "Unable to set CA certificate file for LDAP "
-                         "connections: %d - %s", rc, ldap_err2string(rc));
-      }
+    keyvalue[count  ][0] = "printer-state";
+    keyvalue[count++][1] = state_str;
 
-     /*
-      * LDAP stuff currently only supports ldapi EXTERNAL SASL binds...
-      */
+    keyvalue[count  ][0] = "printer-type";
+    keyvalue[count++][1] = type_str;
+  }
 
-      if (!BrowseLDAPServer || !strcasecmp(BrowseLDAPServer, "localhost")) 
-        rc = ldap_initialize(&BrowseLDAPHandle, "ldapi:///");
-      else     
-       rc = ldap_initialize(&BrowseLDAPHandle, BrowseLDAPServer);
+ /*
+  * Then pack them into a proper txt record...
+  */
 
-      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);
+#  ifdef HAVE_DNSSD
+  TXTRecordCreate(&txt, 0, NULL);
 
-       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;
-       }
-      }
-    }
+  for (i = 0; i < count; i ++)
+  {
+    size_t len = strlen(keyvalue[i][1]);
 
-    BrowseLDAPRefresh = 0;
+    if (len < 256)
+      TXTRecordSetValue(&txt, keyvalue[i][0], (uint8_t)len, keyvalue[i][1]);
   }
-#endif /* HAVE_OPENLDAP */
 
- /*
-  * Register the individual printers
-  */
+#  else
+  for (i = 0, txt = NULL; i < count; i ++)
+    txt = avahi_string_list_add_printf(txt, "%s=%s", keyvalue[i][0],
+                                       keyvalue[i][1]);
+#  endif /* HAVE_DNSSD */
 
-  for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
-       p;
-       p = (cupsd_printer_t *)cupsArrayNext(Printers))
-    if (!(p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)))
-      cupsdRegisterPrinter(p);
+  return (txt);
 }
 
 
+#  ifdef HAVE_AVAHI
 /*
- * 'cupsdStartPolling()' - Start polling servers as needed.
+ * 'dnssdClientCallback()' - Client callback for Avahi.
+ *
+ * Called whenever the client or server state changes...
  */
 
-void
-cupsdStartPolling(void)
+static void
+dnssdClientCallback(
+    AvahiClient      *c,               /* I - Client */
+    AvahiClientState state,            /* I - Current state */
+    void             *userdata)                /* I - User data (unused) */
 {
-  int                  i;              /* Looping var */
-  cupsd_dirsvc_poll_t  *pollp;         /* Current polling server */
-  char                 polld[1024];    /* Poll daemon path */
-  char                 sport[255];     /* Server port */
-  char                 bport[255];     /* Browser port */
-  char                 interval[255];  /* Poll interval */
-  int                  statusfds[2];   /* Status pipe */
-  char                 *argv[6];       /* Arguments */
-  char                 *envp[100];     /* Environment */
+  int  error;                          /* Error code, if any */
 
 
- /*
-  * Don't do anything if we aren't polling...
-  */
+  (void)userdata;
 
-  if (NumPolled == 0 || BrowseSocket < 0)
-  {
-    PollPipe         = -1;
-    PollStatusBuffer = NULL;
+  if (!c)
     return;
-  }
 
  /*
-  * Setup string arguments for polld, port and interval options.
+  * Make sure DNSSDClient is already set also if this callback function is
+  * already running before avahi_client_new() in dnssdStartBrowsing()
+  * finishes.
   */
 
-  snprintf(polld, sizeof(polld), "%s/daemon/cups-polld", ServerBin);
+  if (!DNSSDClient)
+    DNSSDClient = c;
 
-  sprintf(bport, "%d", BrowsePort);
+  switch (state)
+  {
+    case AVAHI_CLIENT_S_REGISTERING:
+    case AVAHI_CLIENT_S_RUNNING:
+    case AVAHI_CLIENT_S_COLLISION:
+       cupsdLogMessage(CUPSD_LOG_DEBUG, "Avahi server connection now available, registering printers for Bonjour broadcasting.");
 
-  if (BrowseInterval)
-    sprintf(interval, "%d", BrowseInterval);
-  else
-    strcpy(interval, "30");
+       /*
+       * Mark that Avahi server is running...
+       */
 
-  argv[0] = "cups-polld";
-  argv[2] = sport;
-  argv[3] = interval;
-  argv[4] = bport;
-  argv[5] = NULL;
+       avahi_running = 1;
 
-  cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
+       /*
+       * Set the computer name and register the web interface...
+       */
 
- /*
-  * Create a pipe that receives the status messages from each
-  * polling daemon...
-  */
+       DNSSDPort = 0;
+       dnssdUpdateDNSSDName(1);
 
-  if (cupsdOpenPipe(statusfds))
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "Unable to create polling status pipes - %s.",
-                   strerror(errno));
-    PollPipe         = -1;
-    PollStatusBuffer = NULL;
-    return;
-  }
+       /*
+       * Register the individual printers
+       */
 
-  PollPipe         = statusfds[0];
-  PollStatusBuffer = cupsdStatBufNew(PollPipe, "[Poll]");
+       dnssdRegisterAllPrinters(1);
+       break;
 
- /*
-  * Run each polling daemon, redirecting stderr to the polling pipe...
-  */
+    case AVAHI_CLIENT_FAILURE:
+       if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED)
+       {
+         cupsdLogMessage(CUPSD_LOG_DEBUG, "Avahi server disappeared, unregistering printers for Bonjour broadcasting.");
 
-  for (i = 0, pollp = Polled; i < NumPolled; i ++, pollp ++)
-  {
-    sprintf(sport, "%d", pollp->port);
+        /*
+         * Unregister everything and close the client...
+         */
 
-    argv[1] = pollp->hostname;
+         dnssdDeregisterAllPrinters(1);
+         dnssdDeregisterInstance(&WebIFSrv, 1);
+         avahi_client_free(DNSSDClient);
+         DNSSDClient = NULL;
 
-    if (cupsdStartProcess(polld, argv, envp, -1, -1, statusfds[1], -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);
-  }
+        /*
+         * Mark that Avahi server is not running...
+         */
 
-  close(statusfds[1]);
+         avahi_running = 0;
 
- /*
-  * Finally, add the pipe to the input selection set...
-  */
       /*
+         * Renew Avahi client...
+         */
 
-  cupsdAddSelect(PollPipe, (cupsd_selfunc_t)update_polling, NULL, NULL);
+         DNSSDClient = avahi_client_new(avahi_threaded_poll_get(DNSSDMaster), AVAHI_CLIENT_NO_FAIL, dnssdClientCallback, NULL, &error);
+
+         if (!DNSSDClient)
+         {
+           cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to communicate with avahi-daemon: %s", dnssdErrorString(error));
+           if (FatalErrors & CUPSD_FATAL_BROWSE)
+             cupsdEndProcess(getpid(), 0);
+         }
+       }
+       else
+       {
+         cupsdLogMessage(CUPSD_LOG_ERROR, "Communication with avahi-daemon has failed: %s", avahi_strerror(avahi_client_errno(c)));
+         if (FatalErrors & CUPSD_FATAL_BROWSE)
+           cupsdEndProcess(getpid(), 0);
+       }
+       break;
+
+    default:
+        break;
+  }
 }
+#  endif /* HAVE_AVAHI */
 
 
 /*
- * 'cupsdStopBrowsing()' - Stop sending and receiving broadcast information.
+ * 'dnssdDeregisterAllPrinters()' - Deregister all printers.
  */
 
-void
-cupsdStopBrowsing(void)
+static void
+dnssdDeregisterAllPrinters(
+    int             from_callback)     /* I - Deregistering because of callback? */
 {
   cupsd_printer_t      *p;             /* Current printer */
 
 
-  if (!Browsing || !(BrowseLocalProtocols | BrowseRemoteProtocols))
+  if (!DNSSDMaster)
     return;
 
- /*
-  * De-register the individual printers
-  */
-
   for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
        p;
        p = (cupsd_printer_t *)cupsArrayNext(Printers))
-    if (!(p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)))
-      cupsdDeregisterPrinter(p, 1);
-
- /*
-  * Shut down browsing sockets...
-  */
-
-  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 */
-
-    cupsdRemoveSelect(BrowseSocket);
-    BrowseSocket = -1;
-  }
-
-#ifdef HAVE_LIBSLP
-  if (((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_SLP) &&
-      BrowseSLPHandle)
-  {
-   /* 
-    * Close SLP handle...
-    */
-
-    SLPClose(BrowseSLPHandle);
-    BrowseSLPHandle = NULL;
-  }
-#endif /* HAVE_LIBSLP */
-
-#ifdef HAVE_OPENLDAP
-  if (((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_LDAP) &&
-      BrowseLDAPHandle)
-  {
-    ldap_unbind(BrowseLDAPHandle);
-    BrowseLDAPHandle = NULL;
-  }
-#endif /* HAVE_OPENLDAP */
+    if (!(p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_SCANNER)))
+      dnssdDeregisterPrinter(p, 1, from_callback);
 }
 
 
 /*
- * 'cupsdStopPolling()' - Stop polling servers as needed.
- */
-
-void
-cupsdStopPolling(void)
-{
-  int                  i;              /* Looping var */
-  cupsd_dirsvc_poll_t  *pollp;         /* Current polling server */
-
-
-  if (PollPipe >= 0)
-  {
-    cupsdStatBufDelete(PollStatusBuffer);
-    close(PollPipe);
-
-    cupsdRemoveSelect(PollPipe);
-
-    PollPipe         = -1;
-    PollStatusBuffer = NULL;
-  }
-
-  for (i = 0, pollp = Polled; i < NumPolled; i ++, pollp ++)
-    if (pollp->pid)
-      cupsdEndProcess(pollp->pid, 0);
-}
-
-
-#ifdef HAVE_DNSSD
-/*
- * 'cupsdUpdateDNSSDBrowse()' - Handle DNS-SD queries.
- */
-
-void
-cupsdUpdateDNSSDBrowse(
-    cupsd_printer_t *p)                        /* I - Printer being queried */
-{
-  DNSServiceErrorType  sdErr;          /* Service discovery error */
-
-
-  if ((sdErr = DNSServiceProcessResult(p->dnssd_ipp_ref))
-          != kDNSServiceErr_NoError)
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "DNS Service Discovery registration error %d for \"%s\"!",
-                   sdErr, p->name);
-    cupsdRemoveSelect(p->dnssd_ipp_fd);
-    DNSServiceRefDeallocate(p->dnssd_ipp_ref);
-
-    p->dnssd_ipp_ref = NULL;
-    p->dnssd_ipp_fd  = -1;
-  }
-}
-#endif /* HAVE_DNSSD */
-
-
-#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 */
-
-
- /*
-  * Search for printers...
-  */
-
-  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,
-                    "LDAP search returned error %d: %s", rc,
-                   ldap_err2string(rc));
-    return;
-  }
-
-  limit = ldap_count_entries(BrowseLDAPHandle, res);
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "LDAP search returned %d entries", limit);
-  if (limit < 1)
-    return;
-
- /*
-  * Loop through the available printers...
-  */
-
-  for (e = ldap_first_entry(BrowseLDAPHandle, res);
-       e;
-       e = ldap_next_entry(BrowseLDAPHandle, e))
-  {
-   /*
-    * Get the required values from this entry...
-    */
-
-    if ((value = ldap_get_values(BrowseLDAPHandle, e,
-                                 "printerDescription")) == NULL)
-      continue;
-
-    strlcpy(info, *value, sizeof(info));
-    ldap_value_free(value);
-
-    if ((value = ldap_get_values(BrowseLDAPHandle, e,
-                                 "printerLocation")) == NULL)
-      continue;
-
-    strlcpy(location, *value, sizeof(location));
-    ldap_value_free(value);
-
-    if ((value = ldap_get_values(BrowseLDAPHandle, e,
-                                 "printerMakeAndModel")) == NULL)
-      continue;
-
-    strlcpy(make_model, *value, sizeof(make_model));
-    ldap_value_free(value);
-
-    if ((value = ldap_get_values(BrowseLDAPHandle, e,
-                                 "printerType")) == NULL)
-      continue;
-
-    type = atoi(*value);
-    ldap_value_free(value);
-
-    if ((value = ldap_get_values(BrowseLDAPHandle, e,
-                                 "printerURI")) == NULL)
-      continue;
-
-    strlcpy(uri, *value, sizeof(uri));
-    ldap_value_free(value);
-
-   /*
-    * Process the entry as browse data...
-    */
-
-    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);
-
-  }
-}
-#endif /* HAVE_OPENLDAP */
-
-
-#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...
-  */
-
-  s = NULL;
-
-  SLPFindSrvs(BrowseSLPHandle, SLP_CUPS_SRVTYPE, "", "",
-             slp_url_callback, &s);
-
- /*
-  * Loop through the list of available printers...
-  */
-
-  for (; s; s = next)
-  {
-   /*
-    * Save the "next" pointer...
-    */
-
-    next = s->next;
-
-   /* 
-    * Load a cupsd_printer_t structure with the SLP service attributes...
-    */
-
-    SLPFindAttrs(BrowseSLPHandle, s->url, "", "", slp_attr_callback, &p);
-
-   /*
-    * Process this printer entry...
-    */
-
-    uri = s->url + SLP_CUPS_SRVLEN + 1;
-
-    if (!strncmp(uri, "http://", 7) || !strncmp(uri, "ipp://", 6))
-    {
-     /*
-      * 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)))
-        process_browse_data(uri, host, resource, p.type, IPP_PRINTER_IDLE,
-                           p.location,  p.info, p.make_model, 0, NULL);
-    }
-
-   /*
-    * 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 | CUPS_PRINTER_DISCOVERED;
-  type   &= ~CUPS_PRINTER_IMPLICIT;
-  update = 0;
-  hptr   = strchr(host, '.');
-  sptr   = strchr(ServerName, '.');
-
-  if (!ServerNameIsIP && 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 (hptr && !*hptr)
-      *hptr = '.';                     /* Resource FQDN */
-
-    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_DEBUG,
-                           "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);
-
-           cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, p, NULL,
-                         "Class \'%s\' added by directory services.",
-                         p->name);
-         }
-
-          p = NULL;
-       }
-       else if (!p->hostname)
-       {
-        /*
-         * Hostname not set, so this must be a cached remote printer
-         * that was created for a pending print job...
-         */
-
-          cupsdSetString(&p->hostname, host);
-         cupsdSetString(&p->uri, uri);
-         cupsdSetString(&p->device_uri, uri);
-          update = 1;
-        }
-      }
-      else
-      {
-       /*
-        * Use the short name for this shared class.
-       */
-
-        strlcpy(name, resource + 9, sizeof(name));
-      }
-    }
-    else if (p && !p->hostname)
-    {
-     /*
-      * Hostname not set, so this must be a cached remote printer
-      * that was created for a pending print job...
-      */
-
-      cupsdSetString(&p->hostname, host);
-      cupsdSetString(&p->uri, uri);
-      cupsdSetString(&p->device_uri, uri);
-      update = 1;
-    }
-
-    if (!p)
-    {
-     /*
-      * Class doesn't exist; add it...
-      */
-
-      p = cupsdAddClass(name);
-
-      cupsdLogMessage(CUPSD_LOG_DEBUG, "Added remote class \"%s\"...", name);
-
-      cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, p, NULL,
-                    "Class \'%s\' added by directory services.", name);
-
-     /*
-      * Force the URI to point to the real server...
-      */
-
-      p->type      = type & ~CUPS_PRINTER_REJECTING;
-      p->accepting = 1;
-      cupsdSetString(&p->uri, uri);
-      cupsdSetString(&p->device_uri, uri);
-      cupsdSetString(&p->hostname, host);
-
-      update = 1;
-    }
-  }
-  else
-  {
-   /*
-    * Remote destination is a printer...
-    */
-
-    if (!strncmp(resource, "/printers/", 10))
-      snprintf(name, sizeof(name), "%s@%s", resource + 10, host);
-    else
-      return;
-
-    if (hptr && !*hptr)
-      *hptr = '.';                     /* Resource FQDN */
-
-    if ((p = cupsdFindPrinter(name)) == NULL && BrowseShortNames)
-    {
-      if ((p = cupsdFindPrinter(resource + 10)) != 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 printer and then find a printer using the full host
-         * name...
-         */
-
-         if (p->type & CUPS_PRINTER_REMOTE)
-         {
-           cupsdLogMessage(CUPSD_LOG_DEBUG,
-                           "Renamed remote printer \"%s\" to \"%s@%s\"...",
-                           p->name, p->name, p->hostname);
-           cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, p, NULL,
-                         "Printer \'%s\' deleted by directory services.",
-                         p->name);
-
-            snprintf(newname, sizeof(newname), "%s@%s", p->name, p->hostname);
-            cupsdRenamePrinter(p, newname);
-
-           cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, p, NULL,
-                         "Printer \'%s\' added by directory services.",
-                         p->name);
-         }
-
-          p = NULL;
-       }
-       else if (!p->hostname)
-       {
-        /*
-         * Hostname not set, so this must be a cached remote printer
-         * that was created for a pending print job...
-         */
-
-          cupsdSetString(&p->hostname, host);
-         cupsdSetString(&p->uri, uri);
-         cupsdSetString(&p->device_uri, uri);
-          update = 1;
-        }
-      }
-      else
-      {
-       /*
-        * Use the short name for this shared printer.
-       */
-
-        strlcpy(name, resource + 10, sizeof(name));
-      }
-    }
-    else if (p && !p->hostname)
-    {
-     /*
-      * Hostname not set, so this must be a cached remote printer
-      * that was created for a pending print job...
-      */
-
-      cupsdSetString(&p->hostname, host);
-      cupsdSetString(&p->uri, uri);
-      cupsdSetString(&p->device_uri, uri);
-      update = 1;
-    }
-
-    if (!p)
-    {
-     /*
-      * Printer doesn't exist; add it...
-      */
-
-      p = cupsdAddPrinter(name);
-
-      cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, p, NULL,
-                    "Printer \'%s\' added by directory services.", name);
-
-      cupsdLogMessage(CUPSD_LOG_DEBUG, "Added remote printer \"%s\"...", name);
-
-     /*
-      * Force the URI to point to the real server...
-      */
-
-      p->type      = type & ~CUPS_PRINTER_REJECTING;
-      p->accepting = 1;
-      cupsdSetString(&p->hostname, host);
-      cupsdSetString(&p->uri, uri);
-      cupsdSetString(&p->device_uri, uri);
-
-      update = 1;
-    }
-  }
-
- /*
-  * Update the state...
-  */
-
-  p->state       = state;
-  p->browse_time = time(NULL);
-
-  if ((lease_duration = cupsGetOption("lease-duration", num_attrs,
-                                      attrs)) != NULL)
-  {
-   /*
-    * Grab the lease-duration for the browse data; anything less then 1
-    * second or more than 1 week gets the default BrowseTimeout...
-    */
-
-    i = atoi(lease_duration);
-    if (i < 1 || i > 604800)
-      i = BrowseTimeout;
-
-    p->browse_expire = p->browse_time + i;
-  }
-  else
-    p->browse_expire = p->browse_time + BrowseTimeout;
-
-  if (type & CUPS_PRINTER_REJECTING)
-  {
-    type &= ~CUPS_PRINTER_REJECTING;
-
-    if (p->accepting)
-    {
-      update       = 1;
-      p->accepting = 0;
-    }
-  }
-  else if (!p->accepting)
-  {
-    update       = 1;
-    p->accepting = 1;
-  }
-
-  if (p->type != type)
-  {
-    p->type = type;
-    update  = 1;
-  }
-
-  if (location && (!p->location || strcmp(p->location, location)))
-  {
-    cupsdSetString(&p->location, location);
-    update = 1;
-  }
-
-  if (info && (!p->info || strcmp(p->info, info)))
-  {
-    cupsdSetString(&p->info, info);
-    update = 1;
-  }
-
-  if (!make_model || !make_model[0])
-  {
-    if (type & CUPS_PRINTER_CLASS)
-      snprintf(local_make_model, sizeof(local_make_model),
-               "Remote Class on %s", host);
-    else
-      snprintf(local_make_model, sizeof(local_make_model),
-               "Remote Printer on %s", host);
-  }
-  else
-    snprintf(local_make_model, sizeof(local_make_model),
-             "%s on %s", make_model, host);
-
-  if (!p->make_model || strcmp(p->make_model, local_make_model))
-  {
-    cupsdSetString(&p->make_model, local_make_model);
-    update = 1;
-  }
-
-  if (p->num_options)
-  {
-    if (!update && !(type & CUPS_PRINTER_DELETE))
-    {
-     /*
-      * See if we need to update the attributes...
-      */
-
-      if (p->num_options != num_attrs)
-       update = 1;
-      else
-      {
-       for (i = 0; i < num_attrs; i ++)
-          if (strcmp(attrs[i].name, p->options[i].name) ||
-             (!attrs[i].value != !p->options[i].value) ||
-             (attrs[i].value && strcmp(attrs[i].value, p->options[i].value)))
-          {
-           update = 1;
-           break;
-          }
-      }
-    }
-
-   /*
-    * Free the old options...
-    */
-
-    cupsFreeOptions(p->num_options, p->options);
-  }
-
-  p->num_options = num_attrs;
-  p->options     = attrs;
-
-  if (type & CUPS_PRINTER_DELETE)
-  {
-    cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, p, NULL,
-                  "%s \'%s\' deleted by directory services.",
-                 (type & CUPS_PRINTER_CLASS) ? "Class" : "Printer", p->name);
-
-    cupsdExpireSubscriptions(p, NULL);
-    cupsdDeletePrinter(p, 1);
-    cupsdUpdateImplicitClasses();
-  }
-  else if (update)
-  {
-    cupsdSetPrinterAttrs(p);
-    cupsdUpdateImplicitClasses();
-  }
-
- /*
-  * See if we have a default printer...  If not, make the first network
-  * default printer the default.
-  */
-
-  if (DefaultPrinter == NULL && Printers != NULL && UseNetworkDefault)
-  {
-   /*
-    * Find the first network default printer and use it...
-    */
-
-    for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
-         p;
-        p = (cupsd_printer_t *)cupsArrayNext(Printers))
-      if (p->type & CUPS_PRINTER_DEFAULT)
-      {
-        DefaultPrinter = p;
-       break;
-      }
-  }
-
- /*
-  * Do auto-classing if needed...
-  */
-
-  process_implicit_classes();
-
- /*
-  * Update the printcap file...
-  */
-
-  cupsdWritePrintcap();
-}
-
-
-#ifdef HAVE_DNSSD
-/*
- * 'dnssdBuildTxtRecord()' - Build a TXT record from printer info.
- */
-
-static char *                          /* O - TXT record */
-dnssdBuildTxtRecord(
-    int             *txt_len,          /* O - TXT record length */
-    cupsd_printer_t *p)                        /* I - Printer information */
-{
-  int          i, j;                   /* Looping vars */
-  char         type_str[32],           /* Type to string buffer */
-               state_str[32],          /* State to string buffer */
-               rp_str[1024],           /* Queue name string buffer */
-               air_str[1024],          /* auth-info-required string buffer */
-               *keyvalue[32][2];       /* Table of key/value pairs */
-
-
- /*
-  * Load up the key value pairs...
-  */
-
-  i = 0;
-
-  keyvalue[i  ][0] = "txtvers";
-  keyvalue[i++][1] = "1";
-
-  keyvalue[i  ][0] = "qtotal";
-  keyvalue[i++][1] = "1";
-
-  keyvalue[i  ][0] = "rp";
-  keyvalue[i++][1] = rp_str;
-  snprintf(rp_str, sizeof(rp_str), "%s/%s", 
-          (p->type & CUPS_PRINTER_CLASS) ? "classes" : "printers", p->name);
-
-  keyvalue[i  ][0] = "ty";
-  keyvalue[i++][1] = p->make_model;
-
-  if (p->location && *p->location != '\0')
-  {
-    keyvalue[i  ][0] = "note";
-    keyvalue[i++][1] = p->location;
-  }
-
-  keyvalue[i  ][0] = "product";
-  keyvalue[i++][1] = p->product ? p->product : "Unknown";
-
-  snprintf(type_str, sizeof(type_str), "0x%X", p->type | CUPS_PRINTER_REMOTE);
-  snprintf(state_str, sizeof(state_str), "%d", p->state);
-
-  keyvalue[i  ][0] = "printer-state";
-  keyvalue[i++][1] = state_str;
-
-  keyvalue[i  ][0] = "printer-type";
-  keyvalue[i++][1] = type_str;
-
-  keyvalue[i  ][0] = "Transparent";
-  keyvalue[i++][1] = "T";
-
-  keyvalue[i  ][0] = "Binary";
-  keyvalue[i++][1] = "T";
-
-  if ((p->type & CUPS_PRINTER_FAX))
-  {
-    keyvalue[i  ][0] = "Fax";
-    keyvalue[i++][1] = "T";
-  }
-
-  if ((p->type & CUPS_PRINTER_COLOR))
-  {
-    keyvalue[i  ][0] = "Color";
-    keyvalue[i++][1] = "T";
-  }
-
-  if ((p->type & CUPS_PRINTER_DUPLEX))
-  {
-    keyvalue[i  ][0] = "Duplex";
-    keyvalue[i++][1] = "T";
-  }
-
-  if ((p->type & CUPS_PRINTER_STAPLE))
-  {
-    keyvalue[i  ][0] = "Staple";
-    keyvalue[i++][1] = "T";
-  }
-
-  if ((p->type & CUPS_PRINTER_COPIES))
-  {
-    keyvalue[i  ][0] = "Copies";
-    keyvalue[i++][1] = "T";
-  }
-
-  if ((p->type & CUPS_PRINTER_COLLATE))
-  {
-    keyvalue[i  ][0] = "Collate";
-    keyvalue[i++][1] = "T";
-  }
-
-  if ((p->type & CUPS_PRINTER_PUNCH))
-  {
-    keyvalue[i  ][0] = "Punch";
-    keyvalue[i++][1] = "T";
-  }
-
-  if ((p->type & CUPS_PRINTER_BIND))
-  {
-    keyvalue[i  ][0] = "Bind";
-    keyvalue[i++][1] = "T";
-  }
-
-  if ((p->type & CUPS_PRINTER_SORT))
-  {
-    keyvalue[i  ][0] = "Sort";
-    keyvalue[i++][1] = "T";
-  }
-
-  keyvalue[i  ][0] = "pdl";
-  keyvalue[i++][1] = p->pdl ? p->pdl : "application/postscript";
-
-  if (p->num_auth_info_required)
-  {
-    char       *air = air_str;         /* Pointer into string */
-
-
-    for (j = 0; j < p->num_auth_info_required; j ++)
-    {
-      if (air >= (air_str + sizeof(air_str) - 2))
-        break;
-
-      if (j)
-        *air++ = ',';
-
-      strlcpy(air, p->auth_info_required[j], sizeof(air_str) - (air - air_str));
-      air += strlen(air);
-    }
-
-    keyvalue[i  ][0] = "air";
-    keyvalue[i++][1] = air;
-  }
-
- /*
-  * Then pack them into a proper txt record...
-  */
-
-  return (dnssdPackTxtRecord(txt_len, keyvalue, i));
-}
-
-
-/*
- * 'dnssdDeregisterPrinter()' - Stop sending broadcast information for a
- *                              printer.
- */
-
-static void
-dnssdDeregisterPrinter(
-    cupsd_printer_t *p)                        /* I - Printer */
-{
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdDeregisterPrinter(%s)", p->name);
-
- /*
-  * Closing the socket deregisters the service
-  */
-
-  if (p->dnssd_ipp_ref)
-  {
-    cupsdRemoveSelect(p->dnssd_ipp_fd);
-    DNSServiceRefDeallocate(p->dnssd_ipp_ref);
-    p->dnssd_ipp_ref = NULL;
-    p->dnssd_ipp_fd  = -1;
-  }
-
-  cupsdClearString(&p->reg_name);
-
-  if (p->txt_record)
-  {
-   /*
-    * p->txt_record is malloc'd, not _cupsStrAlloc'd...
-    */
-
-    free(p->txt_record);
-    p->txt_record = NULL;
-  }
-}
-
-
-/*
- * 'dnssdPackTxtRecord()' - Pack an array of key/value pairs into the
- *                          TXT record format.
- */
-
-static char *                          /* O - TXT record */
-dnssdPackTxtRecord(int  *txt_len,      /* O - TXT record length */
-                  char *keyvalue[][2], /* I - Table of key value pairs */
-                  int  count)          /* I - Items in table */
-{
-  int  i;                              /* Looping var */
-  int  length;                         /* Length of TXT record */
-  int  length2;                                /* Length of value */
-  char *txtRecord;                     /* TXT record buffer */
-  char *cursor;                                /* Looping pointer */
-
-
- /*
-  * Calculate the buffer size
-  */
-
-  for (length = i = 0; i < count; i++)
-    length += 1 + strlen(keyvalue[i][0]) + 
-             (keyvalue[i][1] ? 1 + strlen(keyvalue[i][1]) : 0);
-
- /*
-  * Allocate and fill it
-  */
-
-  txtRecord = malloc(length);
-  if (txtRecord)
-  {
-    *txt_len = length;
-
-    for (cursor = txtRecord, i = 0; i < count; i++)
-    {
-     /*
-      * Drop in the p-string style length byte followed by the data
-      */
-
-      length  = strlen(keyvalue[i][0]);
-      length2 = keyvalue[i][1] ? 1 + strlen(keyvalue[i][1]) : 0;
-
-      *cursor++ = (unsigned char)(length + length2);
-
-      memcpy(cursor, keyvalue[i][0], length);
-      cursor += length;
-
-      if (length2)
-      {
-        length2 --;
-       *cursor++ = '=';
-       memcpy(cursor, keyvalue[i][1], length2);
-       cursor += length2;
-      }
-    }
-  }
-
-  return (txtRecord);
-}
-
-
-/*
- * 'dnssdRegisterCallback()' - DNSServiceRegister callback.
+ * 'dnssdDeregisterInstance()' - Deregister a DNS-SD service instance.
  */
 
 static void
-dnssdRegisterCallback(
-    DNSServiceRef      sdRef,          /* I - DNS Service reference */
-    DNSServiceFlags    flags,          /* I - Reserved for future use */
-    DNSServiceErrorType        errorCode,      /* I - Error code */
-    const char         *name,          /* I - Service name */
-    const char         *regtype,       /* I - Service type */
-    const char         *domain,        /* I - Domain. ".local" for now */
-    void               *context)       /* I - User-defined context */
-{
-  (void)context;
-
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, 
-                 "dnssdRegisterCallback(%s, %s)", name, regtype);
-
-  if (errorCode)
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR, 
-                   "DNSServiceRegister failed with error %d", (int)errorCode);
-    return;
-  }
-}
-
-
-/*
- * 'dnssdRegisterPrinter()' - Start sending broadcast information for a printer
- *                           or update the broadcast contents.
- */
-
-static void 
-dnssdRegisterPrinter(cupsd_printer_t *p)/* I - Printer */
-{
-  DNSServiceErrorType  se;             /* dnssd errors */
-  cupsd_listener_t     *lis;           /* Current listening socket */
-  char                 *txt_record,    /* TXT record buffer */
-                       *name;          /* Service name */
-  int                  txt_len,        /* TXT record length */
-                       port;           /* IPP port number */
-  char                 str_buffer[1024];
-                                       /* C-string buffer */
-  const char           *computerName;  /* Computer name c-string ptr */
-  const char           *regtype;       /* Registration type */
-#ifdef HAVE_COREFOUNDATION_H
-  CFStringRef          computerNameRef;/* Computer name CFString */
-  CFStringEncoding     nameEncoding;   /* Computer name encoding */
-  CFMutableStringRef   shortNameRef;   /* Mutable name string */
-  CFIndex              nameLength;     /* Name string length */
-#else
-  int                  nameLength;     /* Name string length */
-#endif /* HAVE_COREFOUNDATION_H */
-
-
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdRegisterPrinter(%s) %s", p->name,
-                  !p->dnssd_ipp_ref ? "new" : "update");
-
- /*
-  * If per-printer sharing was just disabled make sure we're not
-  * registered before returning.
-  */
-
-  if (!p->shared)
-  {
-    dnssdDeregisterPrinter(p);
-    return;
-  }
-
- /*
-  * Get the computer name as a c-string...
-  */
-
-#ifdef HAVE_COREFOUNDATION_H
-  computerName = NULL;
-  if ((computerNameRef = SCDynamicStoreCopyComputerName(NULL, &nameEncoding)))
-    if ((computerName = CFStringGetCStringPtr(computerNameRef,
-                                              kCFStringEncodingUTF8)) == NULL)
-      if (CFStringGetCString(computerNameRef, str_buffer, sizeof(str_buffer),
-                             kCFStringEncodingUTF8))
-       computerName = str_buffer;
-#else
-  computerName = ServerName;
-#endif /* HAVE_COREFOUNDATION_H */
-
- /*
-  * The registered name takes the form of "<printer-info> @ <computer name>"...
-  */
-
-  name = NULL;
-  if (computerName)
-    cupsdSetStringf(&name, "%s @ %s",
-                    (p->info && strlen(p->info)) ? p->info : p->name,
-                   computerName);
-  else
-    cupsdSetString(&name, (p->info && strlen(p->info)) ? p->info : p->name);
-
-#ifdef HAVE_COREFOUNDATION_H
-  if (computerNameRef)
-    CFRelease(computerNameRef);
-#endif /* HAVE_COREFOUNDATION_H */
-
- /*
-  * If an existing printer was renamed, unregister it and start over...
-  */
-
-  if (p->reg_name && strcmp(p->reg_name, name))
-    dnssdDeregisterPrinter(p);
-
-  txt_len    = 0;                      /* anti-compiler-warning-code */
-  txt_record = dnssdBuildTxtRecord(&txt_len, p);
-
-  if (!p->dnssd_ipp_ref)
-  {
-   /*
-    * Initial registration...
-    */
-
-    cupsdSetString(&p->reg_name, name);
-
-    port = ippPort();
-
-    for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners);
-        lis;
-        lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
-    {
-      if (lis->address.addr.sa_family == AF_INET)
-      {
-       port = ntohs(lis->address.ipv4.sin_port);
-       break;
-      }
-      else if (lis->address.addr.sa_family == AF_INET6)
-      {
-       port = ntohs(lis->address.ipv6.sin6_port);
-       break;
-      }
-    }
-
-   /*
-    * Use the _fax subtype for fax queues...
-    */
-
-    regtype = (p->type & CUPS_PRINTER_FAX) ? dnssdIPPFaxRegType :
-                                             dnssdIPPRegType;
-
-    cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdRegisterPrinter(%s) type is \"%s\"",
-                    p->name, regtype);
-
-    se = DNSServiceRegister(&p->dnssd_ipp_ref, 0, 0, name, regtype, 
-                           NULL, NULL, htons(port), txt_len, txt_record,
-                           dnssdRegisterCallback, p);
-
-   /*
-    * In case the name is too long, try shortening the string one character
-    * at a time...
-    */
-
-    if (se == kDNSServiceErr_BadParam)
-    {
-#ifdef HAVE_COREFOUNDATION_H
-      if ((shortNameRef = CFStringCreateMutable(NULL, 0)) != NULL)
-      {
-       CFStringAppendCString(shortNameRef, name, kCFStringEncodingUTF8);
-        nameLength = CFStringGetLength(shortNameRef);
-
-       while (se == kDNSServiceErr_BadParam && nameLength > 1)
-       {
-         CFStringDelete(shortNameRef, CFRangeMake(--nameLength, 1));
-         if (CFStringGetCString(shortNameRef, str_buffer, sizeof(str_buffer),
-                                kCFStringEncodingUTF8))
-         {
-           se = DNSServiceRegister(&p->dnssd_ipp_ref, 0, 0, str_buffer,
-                                   regtype, NULL, NULL, htons(port),
-                                   txt_len, txt_record,
-                                   dnssdRegisterCallback, p);
-         }
-       }
+dnssdDeregisterInstance(
+    cupsd_srv_t     *srv,              /* I - Service */
+    int             from_callback)     /* I - Called from callback? */
+{
+  if (!srv || !*srv)
+    return;
 
-       CFRelease(shortNameRef);
-      }
-#else
-      nameLength = strlen(name);
-      while (se == kDNSServiceErr_BadParam && nameLength > 1)
-      {
-       name[--nameLength] = '\0';
-       se = DNSServiceRegister(&p->dnssd_ipp_ref, 0, 0, str_buffer, regtype, 
-                               NULL, NULL, htons(port), txt_len, txt_record,
-                               dnssdRegisterCallback, p);
-      }
-#endif /* HAVE_COREFOUNDATION_H */
-    }
+#  ifdef HAVE_DNSSD
+  (void)from_callback;
 
-    if (se == kDNSServiceErr_NoError)
-    {
-      p->dnssd_ipp_fd = DNSServiceRefSockFD(p->dnssd_ipp_ref);
-      p->txt_record   = txt_record;
-      p->txt_len      = txt_len;
-      txt_record      = NULL;
+  DNSServiceRefDeallocate(*srv);
 
-      cupsdAddSelect(p->dnssd_ipp_fd, (cupsd_selfunc_t)cupsdUpdateDNSSDBrowse,
-                     NULL, (void *)p);
-    }
-    else
-      cupsdLogMessage(CUPSD_LOG_WARN,
-                      "DNS-SD registration of \"%s\" failed with %d",
-                     p->name, se);
-  }
-  else if (txt_len != p->txt_len || memcmp(txt_record, p->txt_record, txt_len))
+#  else /* HAVE_AVAHI */
+  if (*srv)
   {
-   /*
-    * Update the existing registration...
-    */
+    if (!from_callback)
+      avahi_threaded_poll_lock(DNSSDMaster);
 
-    /* A TTL of 0 means use record's original value (Radar 3176248) */
-    se = DNSServiceUpdateRecord(p->dnssd_ipp_ref, NULL, 0,
-                               txt_len, txt_record, 0);
+    avahi_entry_group_free(*srv);
 
-    if (p->txt_record)
-      free(p->txt_record);
-
-    p->txt_record = txt_record;
-    p->txt_len    = txt_len;
-    txt_record    = NULL;
+    if (!from_callback)
+      avahi_threaded_poll_unlock(DNSSDMaster);
   }
+#  endif /* HAVE_DNSSD */
 
-  if (txt_record)
-    free(txt_record);
-
-  cupsdClearString(&name);
+  *srv = NULL;
 }
-#endif /* HAVE_DNSSD */
 
 
 /*
- * 'process_implicit_classes()' - Create/update implicit classes as needed.
+ * 'dnssdDeregisterPrinter()' - Deregister all services for a printer.
  */
 
 static void
-process_implicit_classes(void)
+dnssdDeregisterPrinter(
+    cupsd_printer_t *p,                        /* I - Printer */
+    int             clear_name,                /* I - Clear the name? */
+    int             from_callback)     /* I - Called from callback? */
+
 {
-  int          i;                      /* Looping var */
-  int          update;                 /* Update printer attributes? */
-  char         name[IPP_MAX_NAME],     /* Name of printer */
-               *hptr;                  /* Pointer into hostname */
-  cupsd_printer_t *p,                  /* Printer information */
-               *pclass,                /* Printer class */
-               *first;                 /* First printer in class */
-  int          offset,                 /* Offset of name */
-               len;                    /* Length of name */
-
-
-  if (!ImplicitClasses || !Printers)
-    return;
+  cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                  "dnssdDeregisterPrinter(p=%p(%s), clear_name=%d)", p, p->name,
+                  clear_name);
+
+  if (p->ipp_srv)
+  {
+    dnssdDeregisterInstance(&p->ipp_srv, from_callback);
+
+#  ifdef HAVE_DNSSD
+#    ifdef HAVE_SSL
+    dnssdDeregisterInstance(&p->ipps_srv, from_callback);
+#    endif /* HAVE_SSL */
+    dnssdDeregisterInstance(&p->printer_srv, from_callback);
+#  endif /* HAVE_DNSSD */
+  }
 
  /*
-  * Loop through all available printers and create classes as needed...
+  * Remove the printer from the array of DNS-SD printers but keep the
+  * registered name...
   */
 
-  for (p = (cupsd_printer_t *)cupsArrayFirst(Printers), len = 0, offset = 0,
-           update = 0, pclass = NULL, first = NULL;
-       p != NULL;
-       p = (cupsd_printer_t *)cupsArrayNext(Printers))
+  cupsArrayRemove(DNSSDPrinters, p);
+
+ /*
+  * Optionally clear the service name...
+  */
+
+  if (clear_name)
+    cupsdClearString(&p->reg_name);
+}
+
+
+/*
+ * 'dnssdErrorString()' - Return an error string for an error code.
+ */
+
+static const char *                    /* O - Error message */
+dnssdErrorString(int error)            /* I - Error number */
+{
+#  ifdef HAVE_DNSSD
+  switch (error)
   {
-   /*
-    * Skip implicit classes...
-    */
+    case kDNSServiceErr_NoError :
+        return ("OK.");
 
-    if (p->type & CUPS_PRINTER_IMPLICIT)
-    {
-      len = 0;
-      continue;
-    }
+    default :
+    case kDNSServiceErr_Unknown :
+        return ("Unknown error.");
 
-   /*
-    * If len == 0, get the length of this printer name up to the "@"
-    * sign (if any).
-    */
+    case kDNSServiceErr_NoSuchName :
+        return ("Service not found.");
 
-    cupsArraySave(Printers);
+    case kDNSServiceErr_NoMemory :
+        return ("Out of memory.");
 
-    if (len > 0 &&
-       !strncasecmp(p->name, name + offset, len) &&
-       (p->name[len] == '\0' || p->name[len] == '@'))
-    {
-     /*
-      * We have more than one printer with the same name; see if
-      * we have a class, and if this printer is a member...
-      */
+    case kDNSServiceErr_BadParam :
+        return ("Bad parameter.");
 
-      if (pclass && strcasecmp(pclass->name, name))
-      {
-       if (update)
-         cupsdSetPrinterAttrs(pclass);
+    case kDNSServiceErr_BadReference :
+        return ("Bad service reference.");
 
-       update = 0;
-       pclass = NULL;
-      }
+    case kDNSServiceErr_BadState :
+        return ("Bad state.");
 
-      if (!pclass && (pclass = cupsdFindDest(name)) == NULL)
-      {
-       /*
-       * Need to add the class...
-       */
+    case kDNSServiceErr_BadFlags :
+        return ("Bad flags.");
 
-       pclass = cupsdAddPrinter(name);
-       cupsArrayAdd(ImplicitPrinters, pclass);
+    case kDNSServiceErr_Unsupported :
+        return ("Unsupported.");
 
-       pclass->type      |= CUPS_PRINTER_IMPLICIT;
-       pclass->accepting = 1;
-       pclass->state     = IPP_PRINTER_IDLE;
+    case kDNSServiceErr_NotInitialized :
+        return ("Not initialized.");
 
-        cupsdSetString(&pclass->location, p->location);
-        cupsdSetString(&pclass->info, p->info);
+    case kDNSServiceErr_AlreadyRegistered :
+        return ("Already registered.");
 
-        cupsdSetString(&pclass->job_sheets[0], p->job_sheets[0]);
-        cupsdSetString(&pclass->job_sheets[1], p->job_sheets[1]);
+    case kDNSServiceErr_NameConflict :
+        return ("Name conflict.");
 
-        update = 1;
+    case kDNSServiceErr_Invalid :
+        return ("Invalid name.");
 
-        cupsdLogMessage(CUPSD_LOG_DEBUG, "Added implicit class \"%s\"...",
-                       name);
-       cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, p, NULL,
-                      "Implicit class \'%s\' added by directory services.",
-                     name);
-      }
+    case kDNSServiceErr_Firewall :
+        return ("Firewall prevents registration.");
 
-      if (first != NULL)
-      {
-        for (i = 0; i < pclass->num_printers; i ++)
-         if (pclass->printers[i] == first)
-           break;
+    case kDNSServiceErr_Incompatible :
+        return ("Client library incompatible.");
 
-        if (i >= pclass->num_printers)
-       {
-         first->in_implicit_class = 1;
-         cupsdAddPrinterToClass(pclass, first);
-        }
+    case kDNSServiceErr_BadInterfaceIndex :
+        return ("Bad interface index.");
 
-       first = NULL;
-      }
+    case kDNSServiceErr_Refused :
+        return ("Server prevents registration.");
 
-      for (i = 0; i < pclass->num_printers; i ++)
-       if (pclass->printers[i] == p)
-         break;
+    case kDNSServiceErr_NoSuchRecord :
+        return ("Record not found.");
 
-      if (i >= pclass->num_printers)
-      {
-       p->in_implicit_class = 1;
-       cupsdAddPrinterToClass(pclass, p);
-       update = 1;
-      }
-    }
-    else
-    {
-     /*
-      * First time around; just get name length and mark it as first
-      * in the list...
-      */
+    case kDNSServiceErr_NoAuth :
+        return ("Authentication required.");
 
-      if ((hptr = strchr(p->name, '@')) != NULL)
-       len = hptr - p->name;
-      else
-       len = strlen(p->name);
+    case kDNSServiceErr_NoSuchKey :
+        return ("Encryption key not found.");
 
-      strncpy(name, p->name, len);
-      name[len] = '\0';
-      offset    = 0;
+    case kDNSServiceErr_NATTraversal :
+        return ("Unable to traverse NAT boundary.");
 
-      if ((first = (hptr ? cupsdFindDest(name) : p)) != NULL &&
-         !(first->type & CUPS_PRINTER_IMPLICIT))
-      {
-       /*
-       * Can't use same name as a local printer; add "Any" to the
-       * front of the name, unless we have explicitly disabled
-       * the "ImplicitAnyClasses"...
-       */
+    case kDNSServiceErr_DoubleNAT :
+        return ("Unable to traverse double-NAT boundary.");
 
-        if (ImplicitAnyClasses && len < (sizeof(name) - 4))
-       {
-        /*
-         * Add "Any" to the class name...
-         */
+    case kDNSServiceErr_BadTime :
+        return ("Bad system time.");
 
-          strcpy(name, "Any");
-          strncpy(name + 3, p->name, len);
-         name[len + 3] = '\0';
-         offset        = 3;
-       }
-       else
-       {
-        /*
-         * Don't create an implicit class if we have a local printer
-         * with the same name...
-         */
+    case kDNSServiceErr_BadSig :
+        return ("Bad signature.");
 
-         len = 0;
-          cupsArrayRestore(Printers);
-         continue;
-       }
-      }
+    case kDNSServiceErr_BadKey :
+        return ("Bad encryption key.");
 
-      first = p;
-    }
+    case kDNSServiceErr_Transient :
+        return ("Transient error occurred - please try again.");
+
+    case kDNSServiceErr_ServiceNotRunning :
+        return ("Server not running.");
+
+    case kDNSServiceErr_NATPortMappingUnsupported :
+        return ("NAT doesn't support NAT-PMP or UPnP.");
+
+    case kDNSServiceErr_NATPortMappingDisabled :
+        return ("NAT supports NAT-PNP or UPnP but it is disabled.");
+
+    case kDNSServiceErr_NoRouter :
+        return ("No Internet/default router configured.");
+
+    case kDNSServiceErr_PollingMode :
+        return ("Service polling mode error.");
 
-    cupsArrayRestore(Printers);
+    case kDNSServiceErr_Timeout :
+        return ("Service timeout.");
   }
 
- /*
-  * Update the last printer class as needed...
-  */
+#  else /* HAVE_AVAHI */
+  return (avahi_strerror(error));
+#  endif /* HAVE_DNSSD */
+}
+
+
+/*
+ * 'dnssdRegisterCallback()' - Free a TXT record.
+ */
+
+static void
+dnssdFreeTxtRecord(cupsd_txt_t *txt)   /* I - TXT record */
+{
+#  ifdef HAVE_DNSSD
+  TXTRecordDeallocate(txt);
 
-  if (pclass && update)
-    cupsdSetPrinterAttrs(pclass);
+#  else /* HAVE_AVAHI */
+  avahi_string_list_free(*txt);
+  *txt = NULL;
+#  endif /* HAVE_DNSSD */
 }
 
 
 /*
- * 'send_cups_browse()' - Send new browsing information using the CUPS
- *                        protocol.
+ * 'dnssdRegisterAllPrinters()' - Register all printers.
  */
 
 static void
-send_cups_browse(cupsd_printer_t *p)   /* I - Printer to send */
+dnssdRegisterAllPrinters(int from_callback)    /* I - Called from callback? */
 {
-  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 */
+  cupsd_printer_t      *p;                     /* Current printer */
 
 
- /*
-  * Figure out the printer type value...
-  */
+  if (!DNSSDMaster)
+    return;
+
+  for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
+       p;
+       p = (cupsd_printer_t *)cupsArrayNext(Printers))
+    if (!(p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_SCANNER)))
+      dnssdRegisterPrinter(p, from_callback);
+}
 
-  type = p->type | CUPS_PRINTER_REMOTE;
 
-  if (!p->accepting)
-    type |= CUPS_PRINTER_REJECTING;
+/*
+ * 'dnssdRegisterCallback()' - DNSServiceRegister callback.
+ */
 
-  if (p == DefaultPrinter)
-    type |= CUPS_PRINTER_DEFAULT;
+#  ifdef HAVE_DNSSD
+static void
+dnssdRegisterCallback(
+    DNSServiceRef      sdRef,          /* I - DNS Service reference */
+    DNSServiceFlags    flags,          /* I - Reserved for future use */
+    DNSServiceErrorType        errorCode,      /* I - Error code */
+    const char         *name,          /* I - Service name */
+    const char         *regtype,       /* I - Service type */
+    const char         *domain,        /* I - Domain. ".local" for now */
+    void               *context)       /* I - Printer */
+{
+  cupsd_printer_t *p = (cupsd_printer_t *)context;
+                                       /* Current printer */
 
- /*
-  * 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));
+  (void)sdRef;
+  (void)flags;
+  (void)domain;
+
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdRegisterCallback(%s, %s) for %s (%s)",
+                  name, regtype, p ? p->name : "Web Interface",
+                 p ? (p->reg_name ? p->reg_name : "(null)") : "NA");
 
-  if (p->make_model)
-    dequote(make_model, p->make_model, sizeof(make_model));
-  else if (p->type & CUPS_PRINTER_CLASS)
+  if (errorCode)
   {
-    if (p->num_printers > 0 && p->printers[0]->make_model)
-      strlcpy(make_model, p->printers[0]->make_model, sizeof(make_model));
-    else
-      strlcpy(make_model, "Local Printer Class", sizeof(make_model));
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                   "DNSServiceRegister failed with error %d", (int)errorCode);
+    return;
   }
-  else if (p->raw)
-    strlcpy(make_model, "Local Raw Printer", sizeof(make_model));
-  else
-    strlcpy(make_model, "Local System V Printer", sizeof(make_model));
+  else if (p && (!p->reg_name || _cups_strcasecmp(name, p->reg_name)))
+  {
+    cupsdLogMessage(CUPSD_LOG_INFO, "Using service name \"%s\" for \"%s\"",
+                    name, p->name);
 
- /*
-  * Send a packet to each browse address...
-  */
+    cupsArrayRemove(DNSSDPrinters, p);
+    cupsdSetString(&p->reg_name, name);
+    cupsArrayAdd(DNSSDPrinters, p);
 
-  for (i = NumBrowsers, b = Browsers; i > 0; i --, b ++)
-    if (b->iface[0])
-    {
-     /*
-      * Send the browse packet to one or more interfaces...
-      */
+    LastEvent |= CUPSD_EVENT_PRINTER_MODIFIED;
+  }
+}
 
-      if (!strcmp(b->iface, "*"))
-      {
-       /*
-        * Send to all local interfaces...
-       */
+#  else /* HAVE_AVAHI */
+static void
+dnssdRegisterCallback(
+    AvahiEntryGroup      *srv,         /* I - Service */
+    AvahiEntryGroupState state,                /* I - Registration state */
+    void                 *context)     /* I - Printer */
+{
+  cupsd_printer_t *p = (cupsd_printer_t *)context;
+                                       /* Current printer */
 
-        cupsdNetIFUpdate();
+  cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                  "dnssdRegisterCallback(srv=%p, state=%d, context=%p) "
+                  "for %s (%s)", srv, state, context,
+                  p ? p->name : "Web Interface",
+                 p ? (p->reg_name ? p->reg_name : "(null)") : "NA");
 
-       for (iface = (cupsd_netif_t *)cupsArrayFirst(NetIFList);
-            iface;
-            iface = (cupsd_netif_t *)cupsArrayNext(NetIFList))
-       {
-        /*
-         * Only send to local, IPv4 interfaces...
-         */
+  /* TODO: Handle collisions with avahi_alternate_service_name(p->reg_name)? */
+}
+#  endif /* HAVE_DNSSD */
 
-         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" :
-                                                           "/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 : "");
+/*
+ * 'dnssdRegisterInstance()' - Register an instance of a printer service.
+ */
 
-         bytes = strlen(packet);
+static int                             /* O - 1 on success, 0 on failure */
+dnssdRegisterInstance(
+    cupsd_srv_t     *srv,              /* O - Service */
+    cupsd_printer_t *p,                        /* I - Printer */
+    char            *name,             /* I - DNS-SD service name */
+    const char      *type,             /* I - DNS-SD service type */
+    const char      *subtypes,         /* I - Subtypes to register or NULL */
+    int             port,              /* I - Port number or 0 */
+    cupsd_txt_t     *txt,              /* I - TXT record */
+    int             commit,            /* I - Commit registration? */
+    int             from_callback)     /* I - Called from callback? */
+{
+  char temp[256],                      /* Temporary string */
+       *ptr;                           /* Pointer into string */
+  int  error;                          /* Any error */
 
-         cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                         "cupsdSendBrowseList: (%d bytes to \"%s\") %s", bytes,
-                         iface->name, packet);
 
-          iface->broadcast.ipv4.sin_port = htons(BrowsePort);
+#  ifdef HAVE_DNSSD
+  (void)from_callback;
+#  endif /* HAVE_DNSSD */
 
-         sendto(BrowseSocket, packet, bytes, 0,
-                (struct sockaddr *)&(iface->broadcast),
-                httpAddrLength(&(iface->broadcast)));
-        }
-      }
-      else if ((iface = cupsdNetIFFind(b->iface)) != NULL)
-      {
-       /*
-        * Send to the named interface using the IPv4 address...
-       */
+  cupsdLogMessage(CUPSD_LOG_DEBUG, "Registering \"%s\" with DNS-SD type \"%s\".", name, type);
 
-        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 (p && !srv)
+  {
+   /*
+    * Assign the correct pointer for "srv"...
+    */
 
-        if (iface)
-       {
-         httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
-                          iface->hostname, iface->port,
-                          (p->type & CUPS_PRINTER_CLASS) ? "/classes/%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),
-                httpAddrLength(&(iface->broadcast)));
-        }
-      }
-    }
+#  ifdef HAVE_DNSSD
+    if (!strcmp(type, "_printer._tcp"))
+      srv = &p->printer_srv;           /* Target LPD service */
+#    ifdef HAVE_SSL
+    else if (!strcmp(type, "_ipps._tcp"))
+      srv = &p->ipps_srv;              /* Target IPPS service */
+#    endif /* HAVE_SSL */
     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 : "");
+      srv = &p->ipp_srv;               /* Target IPP service */
 
-      bytes = strlen(packet);
-      cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                      "cupsdSendBrowseList: (%d bytes) %s", bytes, packet);
+#  else /* HAVE_AVAHI */
+    srv = &p->ipp_srv;                 /* Target service group */
+#  endif /* HAVE_DNSSD */
+  }
 
-      if (sendto(BrowseSocket, packet, bytes, 0,
-                (struct sockaddr *)&(b->to),
-                httpAddrLength(&(b->to))) <= 0)
-      {
-       /*
-        * Unable to send browse packet, so remove this address from the
-       * list...
-       */
+#  ifdef HAVE_DNSSD
+  (void)commit;
 
-       cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "cupsdSendBrowseList: sendto failed for browser "
-                       "%d - %s.",
-                       (int)(b - Browsers + 1), strerror(errno));
+#  else /* HAVE_AVAHI */
+  if (!from_callback)
+    avahi_threaded_poll_lock(DNSSDMaster);
 
-        if (i > 1)
-         memmove(b, b + 1, (i - 1) * sizeof(cupsd_dirsvc_addr_t));
+  if (!*srv)
+    *srv = avahi_entry_group_new(DNSSDClient, dnssdRegisterCallback, NULL);
+  if (!*srv)
+  {
+    if (!from_callback)
+      avahi_threaded_poll_unlock(DNSSDMaster);
 
-       b --;
-       NumBrowsers --;
-      }
-    }
-}
+    cupsdLogMessage(CUPSD_LOG_WARN, "DNS-SD registration of \"%s\" failed: %s",
+                    name, dnssdErrorString(avahi_client_errno(DNSSDClient)));
+    return (0);
+  }
+#  endif /* HAVE_DNSSD */
 
+ /*
+  * Make sure the name is <= 63 octets, and when we truncate be sure to
+  * properly truncate any UTF-8 characters...
+  */
 
-#ifdef HAVE_OPENLDAP
-/*
- * 'send_ldap_browse()' - Send LDAP printer registrations.
- */
+  ptr = name + strlen(name);
+  while ((ptr - name) > 63)
+  {
+    do
+    {
+      ptr --;
+    }
+    while (ptr > name && (*ptr & 0xc0) == 0x80);
 
-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);
+    if (ptr > name)
+      *ptr = '\0';
+  }
 
  /*
-  * Everything in ldap is ** so we fudge around it...
+  * Register the service...
   */
 
-  sprintf(typestring, "%u", p->type);
-
-  cn_value[0]   = p->name;
-  cn_value[1]   = NULL;
-  info[0]       = p->info ? p->info : "Unknown";
-  info[1]       = NULL;
-  location[0]   = p->location ? p->location : "Unknown";
-  location[1]   = NULL;
-  make_model[0] = p->make_model ? p->make_model : "Unknown";
-  make_model[1] = NULL;
-  type[0]       = typestring;
-  type[1]       = NULL;
-  uri[0]        = p->uri;
-  uri[1]        = NULL;
-
-  snprintf(filter, sizeof(filter),
-           "(&(objectclass=cupsPrinter)(printerURI=%s))", p->uri);
-
-  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->name, 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...");
+#  ifdef HAVE_DNSSD
+  if (subtypes)
+    snprintf(temp, sizeof(temp), "%s,%s", type, subtypes);
+  else
+    strlcpy(temp, type, sizeof(temp));
 
-    for (i = 0; i < 7; i ++)
-    {
-      pmods[i]         = mods + i;
-      pmods[i]->mod_op = LDAP_MOD_REPLACE;
-    }
-    pmods[i] = NULL;
+  *srv  = DNSSDMaster;
+  error = DNSServiceRegister(srv, kDNSServiceFlagsShareConnection,
+                            0, name, temp, NULL, DNSSDHostName, htons(port),
+                            txt ? TXTRecordGetLength(txt) : 0,
+                            txt ? TXTRecordGetBytesPtr(txt) : NULL,
+                            dnssdRegisterCallback, p);
 
-    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 /* HAVE_AVAHI */
+  if (txt)
+  {
+    AvahiStringList *temptxt;
+    for (temptxt = *txt; temptxt; temptxt = temptxt->next)
+      cupsdLogMessage(CUPSD_LOG_DEBUG, "DNS_SD \"%s\" %s", name, temptxt->text);
   }
-  else 
+
+  error = avahi_entry_group_add_service_strlst(*srv, AVAHI_IF_UNSPEC,
+                                               AVAHI_PROTO_UNSPEC, 0, name,
+                                               type, NULL, DNSSDHostName, port,
+                                               txt ? *txt : NULL);
+  if (error)
+    cupsdLogMessage(CUPSD_LOG_DEBUG, "DNS-SD service add for \"%s\" failed.",
+                    name);
+
+  if (!error && subtypes)
   {
    /*
-    * Printer has never been registered, add the current
-    * registration...
+    * Register all of the subtypes...
     */
 
-    cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                    "send_ldap_browse: Adding entry...");
+    char       *start,                 /* Start of subtype */
+               subtype[256];           /* Subtype string */
 
-    for (i = 0; i < 7; i ++)
+    strlcpy(temp, subtypes, sizeof(temp));
+
+    for (start = temp; *start; start = ptr)
     {
-      pmods[i]         = mods + i;
-      pmods[i]->mod_op = LDAP_MOD_ADD;
+     /*
+      * Skip leading whitespace...
+      */
+
+      while (*start && isspace(*start & 255))
+        start ++;
+
+     /*
+      * Grab everything up to the next comma or the end of the string...
+      */
+
+      for (ptr = start; *ptr && *ptr != ','; ptr ++);
+
+      if (*ptr)
+        *ptr++ = '\0';
+
+      if (!*start)
+        break;
+
+     /*
+      * Register the subtype...
+      */
+
+      snprintf(subtype, sizeof(subtype), "%s._sub.%s", start, type);
+
+      error = avahi_entry_group_add_service_subtype(*srv, AVAHI_IF_UNSPEC,
+                                                    AVAHI_PROTO_UNSPEC, 0,
+                                                    name, type, NULL, subtype);
+      if (error)
+      {
+        cupsdLogMessage(CUPSD_LOG_DEBUG,
+                        "DNS-SD subtype %s registration for \"%s\" failed." ,
+                        subtype, name);
+        break;
+      }
     }
-    pmods[i] = NULL;
+  }
 
-    if ((rc = ldap_add_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));
+  if (!error && commit)
+  {
+    if ((error = avahi_entry_group_commit(*srv)) != 0)
+      cupsdLogMessage(CUPSD_LOG_DEBUG, "DNS-SD commit of \"%s\" failed.",
+                      name);
+  }
+
+  if (!from_callback)
+    avahi_threaded_poll_unlock(DNSSDMaster);
+#  endif /* HAVE_DNSSD */
+
+  if (error)
+  {
+    cupsdLogMessage(CUPSD_LOG_WARN, "DNS-SD registration of \"%s\" failed: %s",
+                    name, dnssdErrorString(error));
+    cupsdLogMessage(CUPSD_LOG_DEBUG, "DNS-SD type: %s", type);
+    if (subtypes)
+      cupsdLogMessage(CUPSD_LOG_DEBUG, "DNS-SD sub-types: %s", subtypes);
   }
+
+  return (!error);
 }
-#endif /* HAVE_OPENLDAP */
 
 
-#ifdef HAVE_LIBSLP
 /*
- * 'send_slp_browse()' - Register the specified printer with SLP.
+ * 'dnssdRegisterPrinter()' - Start sending broadcast information for a printer
+ *                           or update the broadcast contents.
  */
 
 static void
-send_slp_browse(cupsd_printer_t *p)    /* I - Printer to register */
+dnssdRegisterPrinter(
+    cupsd_printer_t *p,                        /* I - Printer */
+    int             from_callback)     /* I - Called from callback? */
 {
-  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);
+  char         name[256];              /* Service name */
+  int          printer_port;           /* LPD port number */
+  int          status;                 /* Registration status */
+  cupsd_txt_t  ipp_txt,                /* IPP(S) TXT record */
+               printer_txt;            /* LPD TXT record */
 
- /*
-  * 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, "dnssdRegisterPrinter(%s) %s", p->name,
+                  !p->ipp_srv ? "new" : "update");
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "Service URL = \"%s\"", srvurl);
+#  ifdef HAVE_AVAHI
+  if (!avahi_running)
+    return;
+#  endif /* HAVE_AVAHI */
 
  /*
-  * Figure out the finishings string...
+  * Remove the current registrations if we have them and then return if
+  * per-printer sharing was just disabled...
   */
 
-  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");
-  }
+  dnssdDeregisterPrinter(p, 0, from_callback);
 
-  if (p->type & CUPS_PRINTER_PUNCH)
-  {
-    if (finishings[0])
-      strlcat(finishings, ",punch", sizeof(finishings));
-    else
-      strcpy(finishings, "punch");
-  }
+  if (!p->shared)
+    return;
 
-  if (p->type & CUPS_PRINTER_COVER)
-  {
-    if (finishings[0])
-      strlcat(finishings, ",cover", sizeof(finishings));
-    else
-      strcpy(finishings, "cover");
-  }
+ /*
+  * Set the registered name as needed; the registered name takes the form of
+  * "<printer-info> @ <computer name>"...
+  */
 
-  if (p->type & CUPS_PRINTER_SORT)
+  if (!p->reg_name)
   {
-    if (finishings[0])
-      strlcat(finishings, ",sort", sizeof(finishings));
+    if (p->info && strlen(p->info) > 0)
+    {
+      if (DNSSDComputerName)
+       snprintf(name, sizeof(name), "%s @ %s", p->info, DNSSDComputerName);
+      else
+       strlcpy(name, p->info, sizeof(name));
+    }
+    else if (DNSSDComputerName)
+      snprintf(name, sizeof(name), "%s @ %s", p->name, DNSSDComputerName);
     else
-      strcpy(finishings, "sort");
+      strlcpy(name, p->name, sizeof(name));
   }
-
-  if (!finishings[0])
-    strcpy(finishings, "none");
+  else
+    strlcpy(name, p->reg_name, sizeof(name));
 
  /*
-  * Quote any commas in the make and model, location, and info strings...
+  * Register IPP and LPD...
+  *
+  * We always must register the "_printer" service type in order to reserve
+  * our name, but use port number 0 if we haven't actually configured cups-lpd
+  * to share via LPD...
   */
 
-  for (src = p->make_model, dst = make_model;
-       src && *src && dst < (make_model + sizeof(make_model) - 2);)
-  {
-    if (*src == ',' || *src == '\\' || *src == ')')
-      *dst++ = '\\';
+  ipp_txt     = dnssdBuildTxtRecord(p, 0);
+  printer_txt = dnssdBuildTxtRecord(p, 1);
 
-    *dst++ = *src++;
-  }
+  if (BrowseLocalProtocols & BROWSE_LPD)
+    printer_port = 515;
+  else
+    printer_port = 0;
 
-  *dst = '\0';
+  status = dnssdRegisterInstance(NULL, p, name, "_printer._tcp", NULL, printer_port, &printer_txt, 0, from_callback);
 
-  if (!make_model[0])
-    strcpy(make_model, "Unknown");
+#  ifdef HAVE_SSL
+  if (status)
+    dnssdRegisterInstance(NULL, p, name, "_ipps._tcp", DNSSDSubTypes, DNSSDPort, &ipp_txt, 0, from_callback);
+#  endif /* HAVE_SSL */
 
-  for (src = p->location, dst = location;
-       src && *src && dst < (location + sizeof(location) - 2);)
+  if (status)
   {
-    if (*src == ',' || *src == '\\' || *src == ')')
-      *dst++ = '\\';
+   /*
+    * Use the "_fax-ipp" service type for fax queues, otherwise use "_ipp"...
+    */
 
-    *dst++ = *src++;
+    if (p->type & CUPS_PRINTER_FAX)
+      status = dnssdRegisterInstance(NULL, p, name, "_fax-ipp._tcp", DNSSDSubTypes, DNSSDPort, &ipp_txt, 1, from_callback);
+    else
+      status = dnssdRegisterInstance(NULL, p, name, "_ipp._tcp", DNSSDSubTypes, DNSSDPort, &ipp_txt, 1, from_callback);
   }
 
-  *dst = '\0';
+  dnssdFreeTxtRecord(&ipp_txt);
+  dnssdFreeTxtRecord(&printer_txt);
 
-  if (!location[0])
-    strcpy(location, "Unknown");
+  if (status)
+  {
+   /*
+    * Save the registered name and add the printer to the array of DNS-SD
+    * printers...
+    */
 
-  for (src = p->info, dst = info;
-       src && *src && dst < (info + sizeof(info) - 2);)
+    cupsdSetString(&p->reg_name, name);
+    cupsArrayAdd(DNSSDPrinters, p);
+  }
+  else
   {
-    if (*src == ',' || *src == '\\' || *src == ')')
-      *dst++ = '\\';
+   /*
+    * Registration failed for this printer...
+    */
+
+    dnssdDeregisterInstance(&p->ipp_srv, from_callback);
 
-    *dst++ = *src++;
+#  ifdef HAVE_DNSSD
+#    ifdef HAVE_SSL
+    dnssdDeregisterInstance(&p->ipps_srv, from_callback);
+#    endif /* HAVE_SSL */
+    dnssdDeregisterInstance(&p->printer_srv, from_callback);
+#  endif /* HAVE_DNSSD */
   }
+}
 
-  *dst = '\0';
 
-  if (!info[0])
-    strcpy(info, "Unknown");
+/*
+ * 'dnssdStop()' - Stop all DNS-SD registrations.
+ */
 
- /*
-  * Get the authentication value...
-  */
+static void
+dnssdStop(void)
+{
+  cupsd_printer_t      *p;             /* Current printer */
 
-  authentication = ippFindAttribute(p->attrs, "uri-authentication-supported",
-                                    IPP_TAG_KEYWORD);
 
  /*
-  * Make the SLP attribute string list that conforms to
-  * the IANA 'printer:' template.
+  * De-register the individual printers
   */
 
-  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);
+  for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
+       p;
+       p = (cupsd_printer_t *)cupsArrayNext(Printers))
+    dnssdDeregisterPrinter(p, 1, 0);
 
  /*
-  * Register the printer with the SLP server...
+  * Shutdown the rest of the service refs...
   */
 
-  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);
-}
+  dnssdDeregisterInstance(&WebIFSrv, 0);
 
+#  ifdef HAVE_DNSSD
+  cupsdRemoveSelect(DNSServiceRefSockFD(DNSSDMaster));
 
-/*
- * 'slp_attr_callback()' - SLP attribute callback 
- */
+  DNSServiceRefDeallocate(DNSSDMaster);
+  DNSSDMaster = NULL;
 
-static SLPBoolean                      /* O - SLP_TRUE for success */
-slp_attr_callback(
-    SLPHandle  hslp,                   /* I - SLP handle */
-    const char *attrlist,              /* I - Attribute list */
-    SLPError   errcode,                        /* I - Parsing status for this attr */
-    void       *cookie)                        /* I - Current printer */
-{
-  char                 *tmp = 0;       /* Temporary string */
-  cupsd_printer_t      *p = (cupsd_printer_t*)cookie;
-                                       /* Current printer */
+#  else /* HAVE_AVAHI */
+  if (DNSSDMaster)
+    avahi_threaded_poll_stop(DNSSDMaster);
 
+  if (DNSSDClient)
+  {
+    avahi_client_free(DNSSDClient);
+    DNSSDClient = NULL;
+  }
 
-  (void)hslp;                          /* anti-compiler-warning-code */
+  if (DNSSDMaster)
+  {
+    avahi_threaded_poll_free(DNSSDMaster);
+    DNSSDMaster = NULL;
+  }
+#  endif /* HAVE_DNSSD */
 
- /*
-  * Bail if there was an error
-  */
+  cupsArrayDelete(DNSSDPrinters);
+  DNSSDPrinters = NULL;
 
-  if (errcode != SLP_OK)
-    return (SLP_TRUE);
+  DNSSDPort = 0;
+}
 
- /*
-  * Parse the attrlist to obtain things needed to build CUPS browse packet
-  */
 
-  memset(p, 0, sizeof(cupsd_printer_t));
+#  ifdef HAVE_DNSSD
+/*
+ * 'dnssdUpdate()' - Handle DNS-SD queries.
+ */
 
-  if (slp_get_attr(attrlist, "(printer-location=", &(p->location)))
-    return (SLP_FALSE);
-  if (slp_get_attr(attrlist, "(printer-info=", &(p->info)))
-    return (SLP_FALSE);
-  if (slp_get_attr(attrlist, "(printer-make-and-model=", &(p->make_model)))
-    return (SLP_FALSE);
-  if (!slp_get_attr(attrlist, "(printer-type=", &tmp))
-    p->type = atoi(tmp);
-  else
-    p->type = CUPS_PRINTER_REMOTE;
+static void
+dnssdUpdate(void)
+{
+  DNSServiceErrorType  sdErr;          /* Service discovery error */
 
-  cupsdClearString(&tmp);
 
-  return (SLP_TRUE);
+  if ((sdErr = DNSServiceProcessResult(DNSSDMaster)) != kDNSServiceErr_NoError)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "DNS Service Discovery registration error %d!",
+                   sdErr);
+    dnssdStop();
+  }
 }
+#  endif /* HAVE_DNSSD */
 
 
 /*
- * 'slp_dereg_printer()' - SLPDereg() the specified printer
+ * 'dnssdUpdateDNSSDName()' - Update the listen port, computer name, and web interface registration.
  */
 
-static void 
-slp_dereg_printer(cupsd_printer_t *p)  /* I - Printer */
+static void
+dnssdUpdateDNSSDName(int from_callback)        /* I - Called from callback? */
 {
-  char srvurl[HTTP_MAX_URI];           /* Printer service URI */
+  char         webif[1024];            /* Web interface share name */
+#  ifdef __APPLE__
+  SCDynamicStoreRef sc;                        /* Context for dynamic store */
+  CFDictionaryRef btmm;                        /* Back-to-My-Mac domains */
+  CFStringEncoding nameEncoding;       /* Encoding of computer name */
+  CFStringRef  nameRef;                /* Host name CFString */
+  char         nameBuffer[1024];       /* C-string buffer */
+#  endif /* __APPLE__ */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG, "slp_dereg_printer: printer=\"%s\"", p->name);
+ /*
+  * Only share the web interface and printers when non-local listening is
+  * enabled...
+  */
 
-  if (!(p->type & CUPS_PRINTER_REMOTE))
+  if (!DNSSDPort)
   {
    /*
-    * Make the SLP service URL that conforms to the IANA 
-    * 'printer:' template.
+    * Get the port we use for registrations.  If we are not listening on any
+    * non-local ports, there is no sense sharing local printers via Bonjour...
     */
 
-    snprintf(srvurl, sizeof(srvurl), SLP_CUPS_SRVTYPE ":%s", p->uri);
+    cupsd_listener_t   *lis;           /* Current listening socket */
 
-   /*
-    * Deregister the printer...
-    */
+    for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners);
+        lis;
+        lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
+    {
+      if (httpAddrLocalhost(&(lis->address)))
+       continue;
 
-    SLPDereg(BrowseSLPHandle, srvurl, slp_reg_callback, 0);
+      DNSSDPort = httpAddrPort(&(lis->address));
+      break;
+    }
   }
-}
 
+  if (!DNSSDPort)
+    return;
 
-/*
* 'slp_get_attr()' - Get an attribute from an SLP registration.
- */
+ /*
 * Get the computer name as a c-string...
 */
 
-static int                             /* O - 0 on success */
-slp_get_attr(const char *attrlist,     /* I - Attribute list string */
-             const char *tag,          /* I - Name of attribute */
-             char       **valbuf)      /* O - Value */
-{
-  char *ptr1,                          /* Pointer into string */
-       *ptr2;                          /* ... */
+#  ifdef __APPLE__
+  sc = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("cupsd"), NULL, NULL);
 
+  if (sc)
+  {
+   /*
+    * Get the computer name from the dynamic store...
+    */
 
-  cupsdClearString(valbuf);
+    cupsdClearString(&DNSSDComputerName);
 
-  if ((ptr1 = strstr(attrlist, tag)) != NULL)
-  {
-    ptr1 += strlen(tag);
+    if ((nameRef = SCDynamicStoreCopyComputerName(sc, &nameEncoding)) != NULL)
+    {
+      if (CFStringGetCString(nameRef, nameBuffer, sizeof(nameBuffer),
+                            kCFStringEncodingUTF8))
+      {
+        cupsdLogMessage(CUPSD_LOG_DEBUG,
+                       "Dynamic store computer name is \"%s\".", nameBuffer);
+       cupsdSetString(&DNSSDComputerName, nameBuffer);
+      }
+
+      CFRelease(nameRef);
+    }
 
-    if ((ptr2 = strchr(ptr1,')')) != NULL)
+    if (!DNSSDComputerName)
     {
      /*
-      * Copy the value...
+      * Use the ServerName instead...
       */
 
-      *valbuf = calloc(ptr2 - ptr1 + 1, 1);
-      strncpy(*valbuf, ptr1, ptr2 - ptr1);
+      cupsdLogMessage(CUPSD_LOG_DEBUG,
+                      "Using ServerName \"%s\" as computer name.", ServerName);
+      cupsdSetString(&DNSSDComputerName, ServerName);
+    }
 
+    if (!DNSSDHostName)
+    {
      /*
-      * Dequote the value...
+      * Get the local hostname from the dynamic store...
       */
 
-      for (ptr1 = *valbuf; *ptr1; ptr1 ++)
-       if (*ptr1 == '\\' && ptr1[1])
-         _cups_strcpy(ptr1, ptr1 + 1);
+      if ((nameRef = SCDynamicStoreCopyLocalHostName(sc)) != NULL)
+      {
+       if (CFStringGetCString(nameRef, nameBuffer, sizeof(nameBuffer),
+                              kCFStringEncodingUTF8))
+       {
+         cupsdLogMessage(CUPSD_LOG_DEBUG,
+                         "Dynamic store host name is \"%s\".", nameBuffer);
+         cupsdSetString(&DNSSDHostName, nameBuffer);
+       }
 
-      return (0);
+       CFRelease(nameRef);
+      }
     }
-  }
-
-  return (-1);
-}
-
 
-/*
- * 'slp_reg_callback()' - Empty SLPRegReport.
- */
-
-static void
-slp_reg_callback(SLPHandle hslp,       /* I - SLP handle */
-                 SLPError  errcode,    /* I - Error code, if any */
-                void      *cookie)     /* I - App data */
-{
-  (void)hslp;
-  (void)errcode;
-  (void)cookie;
-
-  return;
-}
-
-
-/*
- * 'slp_url_callback()' - SLP service url callback
- */
+    if (!DNSSDHostName)
+    {
+     /*
+      * Use the ServerName instead...
+      */
 
-static SLPBoolean                      /* O - TRUE = OK, FALSE = error */
-slp_url_callback(
-    SLPHandle      hslp,               /* I - SLP handle */
-    const char     *srvurl,            /* I - URL of service */
-    unsigned short lifetime,           /* I - Life of service */
-    SLPError       errcode,            /* I - Existing error code */
-    void           *cookie)            /* I - Pointer to service list */
-{
-  slpsrvurl_t  *s,                     /* New service entry */
-               **head;                 /* Pointer to head of entry */
+      cupsdLogMessage(CUPSD_LOG_DEBUG,
+                      "Using ServerName \"%s\" as host name.", ServerName);
+      cupsdSetString(&DNSSDHostName, ServerName);
+    }
 
+   /*
+    * Get any Back-to-My-Mac domains and add them as aliases...
+    */
 
- /*
-  * Let the compiler know we won't be using these vars...
-  */
+    cupsdFreeAliases(DNSSDAlias);
+    DNSSDAlias = NULL;
 
-  (void)hslp;
-  (void)lifetime;
+    btmm = SCDynamicStoreCopyValue(sc, CFSTR("Setup:/Network/BackToMyMac"));
+    if (btmm && CFGetTypeID(btmm) == CFDictionaryGetTypeID())
+    {
+      cupsdLogMessage(CUPSD_LOG_DEBUG, "%d Back to My Mac aliases to add.",
+                     (int)CFDictionaryGetCount(btmm));
+      CFDictionaryApplyFunction(btmm, dnssdAddAlias, NULL);
+    }
+    else if (btmm)
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                     "Bad Back to My Mac data in dynamic store!");
+    else
+      cupsdLogMessage(CUPSD_LOG_DEBUG, "No Back to My Mac aliases to add.");
 
- /*
-  * Bail if there was an error
-  */
+    if (btmm)
+      CFRelease(btmm);
 
-  if (errcode != SLP_OK)
-    return (SLP_TRUE);
+    CFRelease(sc);
+  }
+  else
+#  endif /* __APPLE__ */
+#  ifdef HAVE_AVAHI
+  if (DNSSDClient)
+  {
+    const char *host_name = avahi_client_get_host_name(DNSSDClient);
 
- /*
-  * Grab the head of the list...
-  */
+    cupsdSetString(&DNSSDComputerName, host_name ? host_name : ServerName);
 
-  head = (slpsrvurl_t**)cookie;
+    if (!DNSSDHostName)
+    {
+      const char *host_fqdn = avahi_client_get_host_name_fqdn(DNSSDClient);
 
- /*
-  * Allocate a *temporary* slpsrvurl_t to hold this entry.
-  */
+      if (host_fqdn)
+       cupsdSetString(&DNSSDHostName, host_fqdn);
+      else if (strchr(ServerName, '.'))
+       cupsdSetString(&DNSSDHostName, ServerName);
+      else
+       cupsdSetStringf(&DNSSDHostName, "%s.local", ServerName);
+    }
+  }
+  else
+#  endif /* HAVE_AVAHI */
+  {
+    cupsdSetString(&DNSSDComputerName, ServerName);
 
-  if ((s = (slpsrvurl_t *)calloc(1, sizeof(slpsrvurl_t))) == NULL)
-    return (SLP_FALSE);
+    if (!DNSSDHostName)
+    {
+      if (strchr(ServerName, '.'))
+       cupsdSetString(&DNSSDHostName, ServerName);
+      else
+       cupsdSetStringf(&DNSSDHostName, "%s.local", ServerName);
+    }
+  }
 
  /*
-  * Copy the SLP service URL...
+  * Then (re)register the web interface if enabled...
   */
 
-  strlcpy(s->url, srvurl, sizeof(s->url));
-
- /* 
-  * Link the SLP service URL into the head of the list
-  */
-
-  if (*head)
-    s->next = *head;
-
-  *head = s;
+  if (BrowseWebIF)
+  {
+    if (DNSSDComputerName)
+      snprintf(webif, sizeof(webif), "CUPS @ %s", DNSSDComputerName);
+    else
+      strlcpy(webif, "CUPS", sizeof(webif));
 
-  return (SLP_TRUE);
+    dnssdDeregisterInstance(&WebIFSrv, from_callback);
+    dnssdRegisterInstance(&WebIFSrv, NULL, webif, "_http._tcp", "_printer", DNSSDPort, NULL, 1, from_callback);
+  }
 }
-#endif /* HAVE_LIBSLP */
 
 
 /*
- * 'update_cups_browse()' - Update the browse lists using the CUPS protocol.
+ * 'get_auth_info_required()' - Get the auth-info-required value to advertise.
  */
 
-static void
-update_cups_browse(void)
+static char *                          /* O - String or NULL if none */
+get_auth_info_required(
+    cupsd_printer_t *p,                        /* I - Printer */
+    char            *buffer,           /* I - Value buffer */
+    size_t          bufsize)           /* I - Size of value buffer */
 {
-  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 */
+  cupsd_location_t *auth;              /* Pointer to authentication element */
+  char         resource[1024];         /* Printer/class resource path */
 
 
  /*
-  * Read a packet from the browse socket...
+  * If auth-info-required is set for this printer, return that...
   */
 
-  srclen = sizeof(srcaddr);
-  if ((bytes = recvfrom(BrowseSocket, packet, sizeof(packet) - 1, 0, 
-                        (struct sockaddr *)&srcaddr, &srclen)) < 0)
+  if (p->num_auth_info_required > 0 && strcmp(p->auth_info_required[0], "none"))
   {
-   /*
-    * "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...
-    */
+    int                i;                      /* Looping var */
+    char       *bufptr;                /* Pointer into buffer */
 
-    if (errno != ECONNREFUSED && errno != EAGAIN)
+    for (i = 0, bufptr = buffer; i < p->num_auth_info_required; i ++)
     {
-      cupsdLogMessage(CUPSD_LOG_ERROR, "Browse recv failed - %s.",
-                      strerror(errno));
-      cupsdLogMessage(CUPSD_LOG_ERROR, "Browsing turned off.");
+      if (bufptr >= (buffer + bufsize - 2))
+       break;
+
+      if (i)
+       *bufptr++ = ',';
 
-      cupsdStopBrowsing();
-      Browsing = 0;
+      strlcpy(bufptr, p->auth_info_required[i], bufsize - (size_t)(bufptr - buffer));
+      bufptr += strlen(bufptr);
     }
 
-    return;
+    return (buffer);
   }
 
-  packet[bytes] = '\0';
-
  /*
-  * If we're about to sleep, ignore incoming browse packets.
+  * Figure out the authentication data requirements to advertise...
   */
 
-  if (Sleeping)
-    return;
+  if (p->type & CUPS_PRINTER_CLASS)
+    snprintf(resource, sizeof(resource), "/classes/%s", p->name);
+  else
+    snprintf(resource, sizeof(resource), "/printers/%s", p->name);
 
- /*
-  * Figure out where it came from...
-  */
+  if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL ||
+      auth->type == CUPSD_AUTH_NONE)
+    auth = cupsdFindPolicyOp(p->op_policy_ptr, IPP_PRINT_JOB);
 
-#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 */
+  if (auth)
   {
-    address[0] = 0;
-    address[1] = 0;
-    address[2] = 0;
-    address[3] = ntohl(srcaddr.ipv4.sin_addr.s_addr);
+    int        auth_type;                      /* Authentication type */
+
+    if ((auth_type = auth->type) == CUPSD_AUTH_DEFAULT)
+      auth_type = cupsdDefaultAuthType();
+
+    switch (auth_type)
+    {
+      case CUPSD_AUTH_NONE :
+          return (NULL);
+
+      case CUPSD_AUTH_NEGOTIATE :
+         strlcpy(buffer, "negotiate", bufsize);
+         break;
+
+      default :
+         strlcpy(buffer, "username,password", bufsize);
+         break;
+    }
+
+    return (buffer);
   }
 
-  if (HostNameLookups)
-    httpAddrLookup(&srcaddr, srcname, sizeof(srcname));
-  else
-    httpAddrString(&srcaddr, srcname, sizeof(srcname));
+  return ("none");
+}
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+
+
+#ifdef __APPLE__
+/*
+ * 'get_hostconfig()' - Get an /etc/hostconfig service setting.
+ */
+
+static int                             /* O - 1 for YES or AUTOMATIC, 0 for NO */
+get_hostconfig(const char *name)       /* I - Name of service */
+{
+  cups_file_t  *fp;                    /* Hostconfig file */
+  char         line[1024],             /* Line from file */
+               *ptr;                   /* Pointer to value */
+  int          state = 1;              /* State of service */
 
-  len = strlen(srcname);
 
  /*
-  * Do ACL stuff...
+  * Try opening the /etc/hostconfig file; if we can't open it, assume that
+  * the service is enabled/auto.
   */
 
-  if (BrowseACL)
+  if ((fp = cupsFileOpen("/etc/hostconfig", "r")) != NULL)
   {
-    if (httpAddrLocalhost(&srcaddr) || !strcasecmp(srcname, "localhost"))
-    {
-     /*
-      * Access from localhost (127.0.0.1) is always allowed...
-      */
+   /*
+    * Read lines from the file until we find the service...
+    */
 
-      auth = AUTH_ALLOW;
-    }
-    else
+    while (cupsFileGets(fp, line, sizeof(line)))
     {
-     /*
-      * Do authorization checks on the domain/address...
-      */
+      if (line[0] == '#' || (ptr = strchr(line, '=')) == NULL)
+        continue;
 
-      switch (BrowseACL->order_type)
-      {
-        default :
-           auth = AUTH_DENY;   /* anti-compiler-warning-code */
-           break;
+      *ptr++ = '\0';
 
-       case AUTH_ALLOW : /* Order Deny,Allow */
-            auth = AUTH_ALLOW;
+      if (!_cups_strcasecmp(line, name))
+      {
+       /*
+        * Found the service, see if it is set to "-NO-"...
+       */
 
-            if (cupsdCheckAuth(address, srcname, len,
-                         BrowseACL->num_deny, BrowseACL->deny))
-             auth = AUTH_DENY;
+       if (!_cups_strncasecmp(ptr, "-NO-", 4))
+         state = 0;
+        break;
+      }
+    }
 
-            if (cupsdCheckAuth(address, srcname, len,
-                         BrowseACL->num_allow, BrowseACL->allow))
-             auth = AUTH_ALLOW;
-           break;
+    cupsFileClose(fp);
+  }
 
-       case AUTH_DENY : /* Order Allow,Deny */
-            auth = AUTH_DENY;
+  return (state);
+}
+#endif /* __APPLE__ */
 
-            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;
-      }
-    }
-  }
-  else
-    auth = AUTH_ALLOW;
+/*
+ * 'update_lpd()' - Update the LPD configuration as needed.
+ */
 
-  if (auth == AUTH_DENY)
-  {
-    cupsdLogMessage(CUPSD_LOG_DEBUG,
-                    "update_cups_browse: Refused %d bytes from %s", bytes,
-                    srcname);
+static void
+update_lpd(int onoff)                  /* - 1 = turn on, 0 = turn off */
+{
+  if (!LPDConfigFile)
     return;
-  }
-
-  cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "update_cups_browse: (%d bytes from %s) %s", bytes,
-                 srcname, packet);
 
+#ifdef __APPLE__
  /*
-  * Parse packet...
+  * Allow /etc/hostconfig CUPS_LPD service setting to override cupsd.conf
+  * setting for backwards-compatibility.
   */
 
-  if (sscanf(packet, "%x%x%1023s", &type, &state, uri) < 3)
-  {
-    cupsdLogMessage(CUPSD_LOG_WARN,
-                    "update_cups_browse: 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 (onoff && !get_hostconfig("CUPS_LPD"))
+    onoff = 0;
+#endif /* __APPLE__ */
 
-  if ((pptr = strchr(packet, '\"')) != NULL)
+  if (!strncmp(LPDConfigFile, "xinetd:///", 10))
   {
    /*
-    * Have extended information; can't use sscanf for it because not all
-    * sscanf's allow empty strings with %[^\"]...
+    * Enable/disable LPD via the xinetd.d config file for cups-lpd...
     */
 
-    for (i = 0, pptr ++;
-         i < (sizeof(location) - 1) && *pptr && *pptr != '\"';
-         i ++, pptr ++)
-      location[i] = *pptr;
-
-    if (i)
-      location[i] = '\0';
+    char       newfile[1024];          /* New cups-lpd.N file */
+    cups_file_t        *ofp,                   /* Original file pointer */
+               *nfp;                   /* New file pointer */
+    char       line[1024];             /* Line from file */
 
-    if (*pptr == '\"')
-      pptr ++;
 
-    while (*pptr && isspace(*pptr & 255))
-      pptr ++;
+    snprintf(newfile, sizeof(newfile), "%s.N", LPDConfigFile + 9);
 
-    if (*pptr == '\"')
+    if ((ofp = cupsFileOpen(LPDConfigFile + 9, "r")) == NULL)
     {
-      for (i = 0, pptr ++;
-           i < (sizeof(info) - 1) && *pptr && *pptr != '\"';
-           i ++, pptr ++)
-       info[i] = *pptr;
-
-      info[i] = '\0';
+      cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open \"%s\" - %s",
+                      LPDConfigFile + 9, strerror(errno));
+      return;
+    }
 
-      if (*pptr == '\"')
-       pptr ++;
+    if ((nfp = cupsFileOpen(newfile, "w")) == NULL)
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create \"%s\" - %s",
+                      newfile, strerror(errno));
+      cupsFileClose(ofp);
+      return;
+    }
 
-      while (*pptr && isspace(*pptr & 255))
-       pptr ++;
+   /*
+    * Copy all of the lines from the cups-lpd file...
+    */
 
-      if (*pptr == '\"')
+    while (cupsFileGets(ofp, line, sizeof(line)))
+    {
+      if (line[0] == '{')
       {
-       for (i = 0, pptr ++;
-             i < (sizeof(make_model) - 1) && *pptr && *pptr != '\"';
-             i ++, pptr ++)
-         make_model[i] = *pptr;
-
-       if (*pptr == '\"')
-         pptr ++;
-
-       make_model[i] = '\0';
-
-        if (*pptr)
-         num_attrs = cupsParseOptions(pptr, num_attrs, &attrs);
+        cupsFilePrintf(nfp, "%s\n", line);
+        snprintf(line, sizeof(line), "\tdisable = %s",
+                onoff ? "no" : "yes");
       }
+      else if (!strstr(line, "disable ="))
+        cupsFilePrintf(nfp, "%s\n", line);
     }
-  }
-
-  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;
+    cupsFileClose(nfp);
+    cupsFileClose(ofp);
+    rename(newfile, LPDConfigFile + 9);
   }
+#ifdef __APPLE__
+  else if (!strncmp(LPDConfigFile, "launchd:///", 11))
+  {
+   /*
+    * Enable/disable LPD via the launchctl command...
+    */
 
- /*
-  * Do relaying...
-  */
+    char       *argv[5],               /* Arguments for command */
+               *envp[MAX_ENV];         /* Environment for command */
+    int                pid;                    /* Process ID */
 
-  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),
-                httpAddrLength(&(Relays[i].to))) <= 0)
-      {
-       cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "update_cups_browse: sendto failed for relay %d - %s.",
-                       i + 1, strerror(errno));
-       cupsFreeOptions(num_attrs, attrs);
-       return;
-      }
 
- /*
-  * Process the browse data...
-  */
+    cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
+    argv[0] = (char *)"launchctl";
+    argv[1] = (char *)(onoff ? "load" : "unload");
+    argv[2] = (char *)"-w";
+    argv[3] = LPDConfigFile + 10;
+    argv[4] = NULL;
 
-  process_browse_data(uri, host, resource, (cups_ptype_t)type,
-                      (ipp_pstate_t)state, location, info, make_model,
-                     num_attrs, attrs);
+    cupsdStartProcess("/bin/launchctl", argv, envp, -1, -1, -1, -1, -1, 1,
+                      NULL, NULL, &pid);
+  }
+#endif /* __APPLE__ */
+  else
+    cupsdLogMessage(CUPSD_LOG_INFO, "Unknown LPDConfigFile scheme!");
 }
 
 
 /*
- * 'update_polling()' - Read status messages from the poll daemons.
+ * 'update_smb()' - Update the SMB configuration as needed.
  */
 
 static void
-update_polling(void)
+update_smb(int onoff)                  /* I - 1 = turn on, 0 = turn off */
 {
-  char         *ptr,                   /* Pointer to end of line in buffer */
-               message[1024];          /* Pointer to message text */
-  int          loglevel;               /* Log level for message */
+  if (!SMBConfigFile)
+    return;
+
+  if (!strncmp(SMBConfigFile, "samba:///", 9))
+  {
+   /*
+    * Enable/disable SMB via the specified smb.conf config file...
+    */
 
+    char       newfile[1024];          /* New smb.conf.N file */
+    cups_file_t        *ofp,                   /* Original file pointer */
+               *nfp;                   /* New file pointer */
+    char       line[1024];             /* Line from file */
+    int                in_printers;            /* In [printers] section? */
 
-  while ((ptr = cupsdStatBufUpdate(PollStatusBuffer, &loglevel,
-                                   message, sizeof(message))) != NULL)
-    if (!strchr(PollStatusBuffer->buffer, '\n'))
-      break;
 
-  if (ptr == NULL && !PollStatusBuffer->bufused)
-  {
+    snprintf(newfile, sizeof(newfile), "%s.N", SMBConfigFile + 8);
+
+    if ((ofp = cupsFileOpen(SMBConfigFile + 8, "r")) == NULL)
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open \"%s\" - %s",
+                      SMBConfigFile + 8, strerror(errno));
+      return;
+    }
+
+    if ((nfp = cupsFileOpen(newfile, "w")) == NULL)
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create \"%s\" - %s",
+                      newfile, strerror(errno));
+      cupsFileClose(ofp);
+      return;
+    }
+
    /*
-    * All polling processes have died; stop polling...
+    * Copy all of the lines from the smb.conf file...
     */
 
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "update_polling: all polling processes have exited!");
-    cupsdStopPolling();
-  }
-}
+    in_printers = 0;
+
+    while (cupsFileGets(ofp, line, sizeof(line)))
+    {
+      if (in_printers && strstr(line, "printable ="))
+        snprintf(line, sizeof(line), "    printable = %s",
+                onoff ? "yes" : "no");
 
+      cupsFilePrintf(nfp, "%s\n", line);
 
-/*
- * End of "$Id: dirsvc.c 6649 2007-07-11 21:46:42Z mike $".
- */
+      if (line[0] == '[')
+        in_printers = !strcmp(line, "[printers]");
+    }
+
+    cupsFileClose(nfp);
+    cupsFileClose(ofp);
+    rename(newfile, SMBConfigFile + 8);
+  }
+  else
+    cupsdLogMessage(CUPSD_LOG_INFO, "Unknown SMBConfigFile scheme!");
+}