]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/dirsvc.c
Merge changes from CUPS 1.4svn-r7770.
[thirdparty/cups.git] / scheduler / dirsvc.c
index 5bc61f68019ddf63c153857b97e150628d4bffa7..b2960d65dbb5effc890ee323094333adb6b13948 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * "$Id: dirsvc.c 7003 2007-10-01 23:10:13Z mike $"
+ * "$Id: dirsvc.c 7676 2008-06-18 23:42:37Z mike $"
  *
  *   Directory services routines for the Common UNIX Printing System (CUPS).
  *
  *   cupsdStopBrowsing()        - Stop sending and receiving broadcast
  *                                information.
  *   cupsdStopPolling()         - Stop polling servers as needed.
- *   cupsdUpdateDNSSDBrowse()   - Handle DNS-SD queries.
+ *   cupsdUpdateDNSSDName()     - Update the computer name we use for
+ *                                browsing...
  *   cupsdUpdateLDAPBrowse()    - Scan for new printers via LDAP...
  *   cupsdUpdateSLPBrowse()     - Get browsing information via SLP.
  *   dequote()                  - Remote quotes from a string.
- *   get_hostconfig()           - Get an /etc/hostconfig service setting.
- *   is_local_queue()           - Determine whether the URI points at a local
- *                                queue.
- *   process_browse_data()      - Process new browse data.
  *   dnssdBuildTxtRecord()      - Build a TXT record from printer info.
+ *   dnssdComparePrinters()     - Compare the registered names of two printers.
  *   dnssdDeregisterPrinter()   - Stop sending broadcast information for a
  *                                printer.
  *   dnssdPackTxtRecord()       - Pack an array of key/value pairs into the TXT
  *   dnssdRegisterCallback()    - DNSServiceRegister callback.
  *   dnssdRegisterPrinter()     - Start sending broadcast information for a
  *                                printer or update the broadcast contents.
+ *   dnssdUpdate()              - Handle DNS-SD queries.
+ *   get_hostconfig()           - Get an /etc/hostconfig service setting.
+ *   is_local_queue()           - Determine whether the URI points at a local
+ *                                queue.
+ *   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.
@@ -112,6 +115,22 @@ static void        update_polling(void);
 static void    update_smb(int onoff);
 
 
+#ifdef HAVE_DNSSD
+static char    *dnssdBuildTxtRecord(int *txt_len, cupsd_printer_t *p,
+                                    int for_lpd);
+static int     dnssdComparePrinters(cupsd_printer_t *a, cupsd_printer_t *b);
+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);
+static void    dnssdUpdate(void);
+#endif /* HAVE_DNSSD */
+
 #ifdef HAVE_OPENLDAP
 static const char * const ldap_attrs[] =/* CUPS LDAP attributes */
                {
@@ -165,26 +184,6 @@ static SLPBoolean  slp_url_callback(SLPHandle hslp, const char *srvurl,
                                         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 */
-
 
 /*
  * 'cupsdDeregisterPrinter()' - Stop sending broadcast information for a 
@@ -198,7 +197,7 @@ 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...
   */
 
   if (!Browsing || !p->shared ||
@@ -226,7 +225,7 @@ cupsdDeregisterPrinter(
 #endif /* HAVE_LIBSLP */
 
 #ifdef HAVE_DNSSD
-  if (removeit && (BrowseLocalProtocols & BROWSE_DNSSD))
+  if (removeit && (BrowseLocalProtocols & BROWSE_DNSSD) && DNSSDRef)
     dnssdDeregisterPrinter(p);
 #endif /* HAVE_DNSSD */
 }
@@ -256,10 +255,10 @@ cupsdLoadRemoteCache(void)
 
 
  /*
-  * Don't load the cache if the CUPS remote protocol is disabled...
+  * Don't load the cache if the remote protocols are disabled...
   */
 
-  if (!Browsing || !(BrowseRemoteProtocols & BROWSE_CUPS))
+  if (!Browsing)
   {
     cupsdLogMessage(CUPSD_LOG_DEBUG,
                     "cupsdLoadRemoteCache: Not loading remote cache.");
@@ -558,7 +557,7 @@ cupsdLoadRemoteCache(void)
           for (value = valueptr; *valueptr && !isspace(*valueptr & 255); valueptr ++);
 
          if (*valueptr)
-            *valueptr++ = '\0';
+            *valueptr = '\0';
 
          cupsdSetString(&p->job_sheets[1], value);
        }
@@ -628,7 +627,7 @@ cupsdLoadRemoteCache(void)
 void
 cupsdRegisterPrinter(cupsd_printer_t *p)/* I - Printer */
 {
-  if (!Browsing || !BrowseLocalProtocols || !BrowseInterval || !NumBrowsers ||
+  if (!Browsing || !BrowseLocalProtocols ||
       (p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)))
     return;
 
@@ -638,7 +637,7 @@ cupsdRegisterPrinter(cupsd_printer_t *p)/* I - Printer */
 #endif /* HAVE_LIBSLP */
 
 #ifdef HAVE_DNSSD
-  if (BrowseLocalProtocols & BROWSE_DNSSD)
+  if ((BrowseLocalProtocols & BROWSE_DNSSD) && DNSSDRef)
     dnssdRegisterPrinter(p);
 #endif /* HAVE_DNSSD */
 }
@@ -803,7 +802,7 @@ cupsdSendBrowseList(void)
                        to;             /* Timeout time */
 
 
-  if (!Browsing || !BrowseLocalProtocols || !Printers)
+  if (!Browsing || !Printers)
     return;
 
  /*
@@ -817,7 +816,7 @@ cupsdSendBrowseList(void)
   * Figure out how many printers need an update...
   */
 
-  if (BrowseInterval > 0)
+  if (BrowseInterval > 0 && BrowseLocalProtocols)
   {
     int        max_count;                      /* Maximum number to update */
 
@@ -895,7 +894,7 @@ cupsdSendBrowseList(void)
   }
 
  /*
-  * Loop through all of the printers and send local updates as needed...
+  * Loop through all of the printers and timeout old printers as needed...
   */
 
   for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
@@ -906,25 +905,24 @@ cupsdSendBrowseList(void)
     * If this is a remote queue, see if it needs to be timed out...
     */
 
-    if (p->type & CUPS_PRINTER_DISCOVERED)
+    if ((p->type & CUPS_PRINTER_DISCOVERED) &&
+        !(p->type & CUPS_PRINTER_IMPLICIT) &&
+       p->browse_expire < to)
     {
-      if (p->browse_expire < to)
-      {
-       cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, p, NULL,
-                      "%s \'%s\' deleted by directory services (timeout).",
-                     (p->type & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
+      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);
 
-        cupsdLogMessage(CUPSD_LOG_DEBUG,
-                       "Remote destination \"%s\" has timed out; "
-                       "deleting it...",
-                       p->name);
-
-        cupsArraySave(Printers);
-        cupsdDeletePrinter(p, 1);
-        cupsArrayRestore(Printers);
-        cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
-      }
+      cupsArraySave(Printers);
+      cupsdDeletePrinter(p, 1);
+      cupsArrayRestore(Printers);
+      cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP | CUPSD_DIRTY_REMOTE);
     }
   }
 }
@@ -1038,6 +1036,74 @@ cupsdStartBrowsing(void)
   else
     BrowseSocket = -1;
 
+#ifdef HAVE_DNSSD
+  if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_DNSSD)
+  {
+    DNSServiceErrorType error;         /* Error from service creation */
+    cupsd_listener_t   *lis;           /* Current listening socket */
+
+
+   /*
+    * First create a "master" connection for all registrations...
+    */
+
+    if ((error = DNSServiceCreateConnection(&DNSSDRef))
+           != kDNSServiceErr_NoError)
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                     "Unable to create master DNS-SD reference: %d", error);
+    else
+    {
+     /*
+      * Add the master connection to the select list...
+      */
+
+      cupsdAddSelect(DNSServiceRefSockFD(DNSSDRef),
+                    (cupsd_selfunc_t)dnssdUpdate, NULL, NULL);
+
+     /*
+      * Then 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...
+      */
+
+      DNSSDPort = 0;
+
+      for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners);
+          lis;
+          lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
+      {
+       if (httpAddrLocalhost(&(lis->address)))
+         continue;
+
+       if (lis->address.addr.sa_family == AF_INET)
+       {
+         DNSSDPort = ntohs(lis->address.ipv4.sin_port);
+         break;
+       }
+       else if (lis->address.addr.sa_family == AF_INET6)
+       {
+         DNSSDPort = ntohs(lis->address.ipv6.sin6_port);
+         break;
+       }
+      }
+
+     /*
+      * Create an array to track the printers we share...
+      */
+
+      if (BrowseRemoteProtocols & BROWSE_DNSSD)
+        DNSSDPrinters = cupsArrayNew((cups_array_func_t)dnssdComparePrinters,
+                                    NULL);
+
+     /*
+      * Set the computer name and register the web interface...
+      */
+
+      cupsdUpdateDNSSDName();
+    }
+  }
+#endif /* HAVE_DNSSD */
+
 #ifdef HAVE_LIBSLP
   if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_SLP)
   {
@@ -1319,6 +1385,33 @@ cupsdStopBrowsing(void)
     BrowseSocket = -1;
   }
 
+#ifdef HAVE_DNSSD
+  if ((BrowseLocalProtocols & BROWSE_DNSSD) && DNSSDRef)
+  {
+    if (WebIFRef)
+    {
+      DNSServiceRefDeallocate(WebIFRef);
+      WebIFRef = NULL;
+    }
+
+    if (RemoteRef)
+    {
+      DNSServiceRefDeallocate(RemoteRef);
+      RemoteRef = NULL;
+    }
+
+    cupsdRemoveSelect(DNSServiceRefSockFD(DNSSDRef));
+
+    DNSServiceRefDeallocate(DNSSDRef);
+    DNSSDRef = NULL;
+
+    cupsArrayDelete(DNSSDPrinters);
+    DNSSDPrinters = NULL;
+
+    DNSSDPort = 0;
+  }
+#endif /* HAVE_DNSSD */
+
 #ifdef HAVE_LIBSLP
   if (((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_SLP) &&
       BrowseSLPHandle)
@@ -1383,28 +1476,71 @@ cupsdStopPolling(void)
 
 #ifdef HAVE_DNSSD
 /*
- * 'cupsdUpdateDNSSDBrowse()' - Handle DNS-SD queries.
+ * 'cupsdUpdateDNSSDName()' - Update the computer name we use for browsing...
  */
 
 void
-cupsdUpdateDNSSDBrowse(
-    cupsd_printer_t *p)                        /* I - Printer being queried */
+cupsdUpdateDNSSDName(void)
 {
-  DNSServiceErrorType  sdErr;          /* Service discovery error */
+  DNSServiceErrorType error;           /* Error from service creation */
+  char         webif[1024];            /* Web interface share name */
+#ifdef HAVE_COREFOUNDATION_H
+  CFStringRef  nameRef;                /* Computer name CFString */
+  char         nameBuffer[1024];       /* C-string buffer */
+  CFStringEncoding nameEncoding;       /* Computer name encoding */
+#endif /* HAVE_COREFOUNDATION_H */
+
+
+ /*
+  * Only share the web interface and printers when non-local listening is
+  * enabled...
+  */
+
+  if (!DNSSDPort)
+    return;
+
+ /*
+  * Get the computer name as a c-string...
+  */
 
+#ifdef HAVE_COREFOUNDATION_H
+  cupsdClearString(&DNSSDName);
 
-  if ((sdErr = DNSServiceProcessResult(p->dnssd_ipp_ref))
-          != kDNSServiceErr_NoError)
+  if ((nameRef = SCDynamicStoreCopyComputerName(NULL,
+                                               &nameEncoding)) != NULL)
   {
-    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);
+    if (CFStringGetCString(nameRef, nameBuffer, sizeof(nameBuffer),
+                          kCFStringEncodingUTF8))
+      cupsdSetString(&DNSSDName, nameBuffer);
 
-    p->dnssd_ipp_ref = NULL;
-    p->dnssd_ipp_fd  = -1;
+    CFRelease(nameRef);
   }
+
+#else
+  cupsdSetString(&DNSSDName, ServerName);
+#endif /* HAVE_COREFOUNDATION_H */
+
+ /*
+  * Then (re)register the web interface...
+  */
+
+  if (DNSSDName)
+    snprintf(webif, sizeof(webif), "CUPS @ %s", DNSSDName);
+  else
+    strlcpy(webif, "CUPS Web Interface", sizeof(webif));
+
+  if (WebIFRef)
+    DNSServiceRefDeallocate(WebIFRef);
+
+  WebIFRef = DNSSDRef;
+  if ((error = DNSServiceRegister(&WebIFRef,
+                                 kDNSServiceFlagsShareConnection,
+                                 0, webif, "_http._tcp", NULL,
+                                 NULL, htons(DNSSDPort), 7,
+                                 "\006path=/", dnssdRegisterCallback,
+                                 NULL)) != kDNSServiceErr_NoError)
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                   "DNS-SD web interface registration failed: %d", error);
 }
 #endif /* HAVE_DNSSD */
 
@@ -1624,1131 +1760,1102 @@ dequote(char       *d,                      /* I - Destination string */
 }
 
 
-#ifdef __APPLE__
+#ifdef HAVE_DNSSD
 /*
- * 'get_hostconfig()' - Get an /etc/hostconfig service setting.
+ * 'dnssdBuildTxtRecord()' - Build a TXT record from printer info.
  */
 
-static int                             /* O - 1 for YES or AUTOMATIC, 0 for NO */
-get_hostconfig(const char *name)       /* I - Name of service */
+static char *                          /* O - TXT record */
+dnssdBuildTxtRecord(
+    int             *txt_len,          /* O - TXT record length */
+    cupsd_printer_t *p,                        /* I - Printer information */
+    int             for_lpd)           /* I - 1 = LPD, 0 = IPP */
 {
-  cups_file_t  *fp;                    /* Hostconfig file */
-  char         line[1024],             /* Line from file */
-               *ptr;                   /* Pointer to value */
-  int          state = 1;              /* State of service */
+  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 */
 
 
  /*
-  * Try opening the /etc/hostconfig file; if we can't open it, assume that
-  * the service is enabled/auto.
+  * Load up the key value pairs...
   */
 
-  if ((fp = cupsFileOpen("/etc/hostconfig", "r")) != NULL)
-  {
-   /*
-    * Read lines from the file until we find the service...
-    */
+  i = 0;
 
-    while (cupsFileGets(fp, line, sizeof(line)))
-    {
-      if (line[0] == '#' || (ptr = strchr(line, '=')) == NULL)
-        continue;
+  keyvalue[i  ][0] = "txtvers";
+  keyvalue[i++][1] = "1";
 
-      *ptr++ = '\0';
+  keyvalue[i  ][0] = "qtotal";
+  keyvalue[i++][1] = "1";
 
-      if (!strcasecmp(line, name))
-      {
-       /*
-        * Found the service, see if it is set to "-NO-"...
-       */
+  keyvalue[i  ][0] = "rp";
+  keyvalue[i++][1] = rp_str;
+  if (for_lpd)
+    strlcpy(rp_str, p->name, sizeof(rp_str));
+  else
+    snprintf(rp_str, sizeof(rp_str), "%s/%s", 
+            (p->type & CUPS_PRINTER_CLASS) ? "classes" : "printers", p->name);
 
-       if (!strncasecmp(ptr, "-NO-", 4))
-         state = 0;
-        break;
-      }
-    }
+  keyvalue[i  ][0] = "ty";
+  keyvalue[i++][1] = p->make_model;
 
-    cupsFileClose(fp);
+  if (p->location && *p->location != '\0')
+  {
+    keyvalue[i  ][0] = "note";
+    keyvalue[i++][1] = p->location;
   }
 
-  return (state);
-}
-#endif /* __APPLE__ */
+  keyvalue[i  ][0] = "priority";
+  keyvalue[i++][1] = for_lpd ? "100" : "0";
 
+  keyvalue[i  ][0] = "product";
+  keyvalue[i++][1] = p->product ? p->product : "Unknown";
 
-/*
- * 'is_local_queue()' - Determine whether the URI points at a local queue.
- */
+  snprintf(type_str, sizeof(type_str), "0x%X", p->type | CUPS_PRINTER_REMOTE);
+  snprintf(state_str, sizeof(state_str), "%d", p->state);
 
-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 */
+  keyvalue[i  ][0] = "printer-state";
+  keyvalue[i++][1] = state_str;
 
+  keyvalue[i  ][0] = "printer-type";
+  keyvalue[i++][1] = type_str;
 
- /*
-  * Pull the URI apart to see if this is a local or remote printer...
-  */
+  keyvalue[i  ][0] = "Transparent";
+  keyvalue[i++][1] = "T";
 
-  if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme),
-                      username, sizeof(username), host, hostlen, &port,
-                     resource, resourcelen) < HTTP_URI_OK)
-    return (-1);
+  keyvalue[i  ][0] = "Binary";
+  keyvalue[i++][1] = "T";
 
-  DEBUG_printf(("host=\"%s\", ServerName=\"%s\"\n", host, ServerName));
+  if ((p->type & CUPS_PRINTER_FAX))
+  {
+    keyvalue[i  ][0] = "Fax";
+    keyvalue[i++][1] = "T";
+  }
 
- /*
-  * Check for local server addresses...
-  */
+  if ((p->type & CUPS_PRINTER_COLOR))
+  {
+    keyvalue[i  ][0] = "Color";
+    keyvalue[i++][1] = "T";
+  }
 
-  if (!strcasecmp(host, ServerName) && port == LocalPort)
-    return (1);
+  if ((p->type & CUPS_PRINTER_DUPLEX))
+  {
+    keyvalue[i  ][0] = "Duplex";
+    keyvalue[i++][1] = "T";
+  }
 
-  cupsdNetIFUpdate();
+  if ((p->type & CUPS_PRINTER_STAPLE))
+  {
+    keyvalue[i  ][0] = "Staple";
+    keyvalue[i++][1] = "T";
+  }
 
-  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 ((p->type & CUPS_PRINTER_COPIES))
+  {
+    keyvalue[i  ][0] = "Copies";
+    keyvalue[i++][1] = "T";
+  }
 
- /*
-  * If we get here, the printer is remote...
-  */
+  if ((p->type & CUPS_PRINTER_COLLATE))
+  {
+    keyvalue[i  ][0] = "Collate";
+    keyvalue[i++][1] = "T";
+  }
 
-  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)))
+  if ((p->type & CUPS_PRINTER_PUNCH))
   {
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "process_browse_data: Bad printer URI in browse data: %s",
-                    uri);
-    return;
+    keyvalue[i  ][0] = "Punch";
+    keyvalue[i++][1] = "T";
   }
 
-  if (strchr(resource, '?') ||
-      (!strncmp(resource, "/printers/", 10) && strchr(resource + 10, '/')) ||
-      (!strncmp(resource, "/classes/", 9) && strchr(resource + 9, '/')))
+  if ((p->type & CUPS_PRINTER_BIND))
   {
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "process_browse_data: Bad resource in browse data: %s",
-                    resource);
-    return;
+    keyvalue[i  ][0] = "Bind";
+    keyvalue[i++][1] = "T";
   }
 
- /*
-  * 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)
+  if ((p->type & CUPS_PRINTER_SORT))
   {
-   /*
-    * Just use the server-supplied options...
-    */
-
-    snprintf(finaluri, sizeof(finaluri), "%s?%s", uri, ipp_options);
-    uri = finaluri;
+    keyvalue[i  ][0] = "Sort";
+    keyvalue[i++][1] = "T";
   }
 
- /*
-  * 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, '.');
-    }
-  }
+  keyvalue[i  ][0] = "pdl";
+  keyvalue[i++][1] = p->pdl ? p->pdl : "application/postscript";
 
-  if (type & CUPS_PRINTER_CLASS)
+  if (p->num_auth_info_required)
   {
-   /*
-    * Remote destination is a class...
-    */
-
-    if (!strncmp(resource, "/classes/", 9))
-      snprintf(name, sizeof(name), "%s@%s", resource + 9, host);
-    else
-      return;
+    char       *air = air_str;         /* Pointer into string */
 
-    if (hptr && !*hptr)
-      *hptr = '.';                     /* Resource FQDN */
 
-    if ((p = cupsdFindDest(name)) == NULL && BrowseShortNames)
+    for (j = 0; j < p->num_auth_info_required; j ++)
     {
-      if ((p = cupsdFindDest(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.
-       */
+      if (air >= (air_str + sizeof(air_str) - 2))
+        break;
 
-        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...
-      */
+      if (j)
+        *air++ = ',';
 
-      cupsdSetString(&p->hostname, host);
-      cupsdSetString(&p->uri, uri);
-      cupsdSetString(&p->device_uri, uri);
-      update = 1;
+      strlcpy(air, p->auth_info_required[j], sizeof(air_str) - (air - air_str));
+      air += strlen(air);
     }
 
-    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;
-
-      cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
-    }
+    keyvalue[i  ][0] = "air";
+    keyvalue[i++][1] = air;
   }
-  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 = cupsdFindDest(name)) == NULL && BrowseShortNames)
-    {
-      if ((p = cupsdFindDest(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...
-      */
+ /*
+  * Then pack them into a proper txt record...
+  */
 
-      p = cupsdAddPrinter(name);
+  return (dnssdPackTxtRecord(txt_len, keyvalue, i));
+}
 
-      cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, p, NULL,
-                    "Printer \'%s\' added by directory services.", name);
 
-      cupsdLogMessage(CUPSD_LOG_DEBUG, "Added remote printer \"%s\"...", name);
+/*
+ * 'dnssdComparePrinters()' - Compare the registered names of two printers.
+ */
 
-     /*
-      * Force the URI to point to the real server...
-      */
+static int                             /* O - Result of comparison */
+dnssdComparePrinters(cupsd_printer_t *a,/* I - First printer */
+                     cupsd_printer_t *b)/* I - Second printer */
+{
+  return (strcasecmp(a->reg_name, b->reg_name));
+}
 
-      p->type      = type & ~CUPS_PRINTER_REJECTING;
-      p->accepting = 1;
-      cupsdSetString(&p->hostname, host);
-      cupsdSetString(&p->uri, uri);
-      cupsdSetString(&p->device_uri, uri);
 
-      update = 1;
+/*
+ * 'dnssdDeregisterPrinter()' - Stop sending broadcast information for a
+ *                              printer.
+ */
 
-      cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
-    }
-  }
+static void
+dnssdDeregisterPrinter(
+    cupsd_printer_t *p)                        /* I - Printer */
+{
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdDeregisterPrinter(%s)", p->name);
 
  /*
-  * Update the state...
+  * Closing the socket deregisters the service
   */
 
-  p->state       = state;
-  p->browse_time = time(NULL);
+  if (p->ipp_ref)
+  {
+    DNSServiceRefDeallocate(p->ipp_ref);
+    p->ipp_ref = NULL;
+  }
 
-  if ((lease_duration = cupsGetOption("lease-duration", num_attrs,
-                                      attrs)) != NULL)
+  cupsArrayRemove(DNSSDPrinters, p);
+  cupsdClearString(&p->reg_name);
+
+  if (p->ipp_txt)
   {
    /*
-    * Grab the lease-duration for the browse data; anything less then 1
-    * second or more than 1 week gets the default BrowseTimeout...
+    * p->ipp_txt is malloc'd, not _cupsStrAlloc'd...
     */
 
-    i = atoi(lease_duration);
-    if (i < 1 || i > 604800)
-      i = BrowseTimeout;
-
-    p->browse_expire = p->browse_time + i;
+    free(p->ipp_txt);
+    p->ipp_txt = NULL;
   }
-  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;
-  }
+/*
+ * 'dnssdPackTxtRecord()' - Pack an array of key/value pairs into the
+ *                          TXT record format.
+ */
 
-  if (p->type != type)
-  {
-    p->type = type;
-    update  = 1;
-  }
+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 */
 
-  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;
+ /*
+  * Calculate the buffer size
+  */
 
-    cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
-  }
+  for (length = i = 0; i < count; i++)
+    length += 1 + strlen(keyvalue[i][0]) + 
+             (keyvalue[i][1] ? 1 + strlen(keyvalue[i][1]) : 0);
 
-  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);
+ /*
+  * Allocate and fill it
+  */
 
-  if (!p->make_model || strcmp(p->make_model, local_make_model))
+  txtRecord = malloc(length);
+  if (txtRecord)
   {
-    cupsdSetString(&p->make_model, local_make_model);
-    update = 1;
-  }
+    *txt_len = length;
 
-  if (p->num_options)
-  {
-    if (!update && !(type & CUPS_PRINTER_DELETE))
+    for (cursor = txtRecord, i = 0; i < count; i++)
     {
      /*
-      * See if we need to update the attributes...
+      * Drop in the p-string style length byte followed by the data
       */
 
-      if (p->num_options != num_attrs)
-       update = 1;
-      else
+      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)
       {
-       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;
-          }
+        length2 --;
+       *cursor++ = '=';
+       memcpy(cursor, keyvalue[i][1], length2);
+       cursor += length2;
       }
     }
+  }
 
-   /*
-    * Free the old options...
-    */
+  return (txtRecord);
+}
 
-    cupsFreeOptions(p->num_options, p->options);
-  }
 
-  p->num_options = num_attrs;
-  p->options     = attrs;
+/*
+ * 'dnssdRegisterCallback()' - DNSServiceRegister callback.
+ */
 
-  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);
+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 */
+{
+  cupsd_printer_t *p = (cupsd_printer_t *)context;
+                                       /* Current printer */
 
-    cupsdExpireSubscriptions(p, NULL);
-    cupsdDeletePrinter(p, 1);
-    cupsdUpdateImplicitClasses();
-    cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
-  }
-  else if (update)
-  {
-    cupsdSetPrinterAttrs(p);
-    cupsdUpdateImplicitClasses();
-  }
 
- /*
-  * See if we have a default printer...  If not, make the first network
-  * default printer the default.
-  */
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdRegisterCallback(%s, %s) for %s",
+                  name, regtype, p ? p->name : "Web Interface");
 
-  if (DefaultPrinter == NULL && Printers != NULL && UseNetworkDefault)
+  if (errorCode)
   {
-   /*
-    * 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;
-        cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
-       break;
-      }
+    cupsdLogMessage(CUPSD_LOG_ERROR, 
+                   "DNSServiceRegister failed with error %d", (int)errorCode);
+    return;
   }
+  else if (p && strcasecmp(name, p->reg_name))
+  {
+    cupsdLogMessage(CUPSD_LOG_INFO, "Using service name \"%s\" for \"%s\"",
+                    name, p->name);
 
- /*
-  * Do auto-classing if needed...
-  */
+    cupsArrayRemove(DNSSDPrinters, p);
+    cupsdSetString(&p->reg_name, name);
+    cupsArrayAdd(DNSSDPrinters, p);
 
-  process_implicit_classes();
+    LastEvent |= CUPSD_EVENT_PRINTER_MODIFIED;
+  }
 }
 
 
-#ifdef HAVE_DNSSD
 /*
- * 'dnssdBuildTxtRecord()' - Build a TXT record from printer info.
+ * 'dnssdRegisterPrinter()' - Start sending broadcast information for a printer
+ *                           or update the broadcast contents.
  */
 
-static char *                          /* O - TXT record */
-dnssdBuildTxtRecord(
-    int             *txt_len,          /* O - TXT record length */
-    cupsd_printer_t *p)                        /* I - Printer information */
+static void 
+dnssdRegisterPrinter(cupsd_printer_t *p)/* I - Printer */
 {
-  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 */
+  DNSServiceErrorType  se;             /* dnssd errors */
+  char                 *ipp_txt,       /* IPP TXT record buffer */
+                       *printer_txt,   /* LPD TXT record buffer */
+                       name[1024],     /* Service name */
+                       *nameptr;       /* Pointer into name */
+  int                  ipp_len,        /* IPP TXT record length */
+                       printer_len;    /* LPD TXT record length */
+  char                 resource[1024]; /* Resource path for printer */
+  const char           *regtype;       /* Registration type */
+  const char           *domain;        /* Registration domain */
+  cupsd_location_t     *location,      /* Printer location */
+                       *policy;        /* Operation policy for Print-Job */
+  unsigned             address[4];     /* INADDR_ANY address */
+
 
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdRegisterPrinter(%s) %s", p->name,
+                  !p->ipp_ref ? "new" : "update");
 
  /*
-  * Load up the key value pairs...
+  * If per-printer sharing was just disabled make sure we're not
+  * registered before returning.
   */
 
-  i = 0;
+  if (!p->shared)
+  {
+    dnssdDeregisterPrinter(p);
+    return;
+  }
 
-  keyvalue[i  ][0] = "txtvers";
-  keyvalue[i++][1] = "1";
+ /*
+  * The registered name takes the form of "<printer-info> @ <computer name>"...
+  */
 
-  keyvalue[i  ][0] = "qtotal";
-  keyvalue[i++][1] = "1";
+  if (p->info && strlen(p->info) > 0)
+  {
+    if (DNSSDName)
+      snprintf(name, sizeof(name), "%s @ %s", p->info, DNSSDName);
+    else
+      strlcpy(name, p->info, sizeof(name));
+  }
+  else if (DNSSDName)
+    snprintf(name, sizeof(name), "%s @ %s", p->name, DNSSDName);
+  else
+    strlcpy(name, p->name, sizeof(name));
 
-  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);
+ /*
+  * If an existing printer was renamed, unregister it and start over...
+  */
 
-  keyvalue[i  ][0] = "ty";
-  keyvalue[i++][1] = p->make_model;
+  if (p->reg_name && strcmp(p->reg_name, name))
+    dnssdDeregisterPrinter(p);
 
-  if (p->location && *p->location != '\0')
+  if (!p->reg_name)
   {
-    keyvalue[i  ][0] = "note";
-    keyvalue[i++][1] = p->location;
+    cupsdSetString(&p->reg_name, name);
+    cupsArrayAdd(DNSSDPrinters, p);
   }
 
-  keyvalue[i  ][0] = "product";
-  keyvalue[i++][1] = p->product ? p->product : "Unknown";
+ /*
+  * If 'Allow printing from the Internet' is enabled (i.e. from any address)
+  * let dnssd decide on the domain, otherwise restrict it to ".local".
+  */
 
-  snprintf(type_str, sizeof(type_str), "0x%X", p->type | CUPS_PRINTER_REMOTE);
-  snprintf(state_str, sizeof(state_str), "%d", p->state);
+  if (p->type & CUPS_PRINTER_CLASS)
+    snprintf(resource, sizeof(resource), "/classes/%s", p->name);
+  else
+    snprintf(resource, sizeof(resource), "/printers/%s", p->name);
 
-  keyvalue[i  ][0] = "printer-state";
-  keyvalue[i++][1] = state_str;
+  address[0] = address[1] = address[2] = address[3] = 0;
+  location   = cupsdFindBest(resource, HTTP_POST);
+  policy     = cupsdFindPolicyOp(p->op_policy_ptr, IPP_PRINT_JOB);
 
-  keyvalue[i  ][0] = "printer-type";
-  keyvalue[i++][1] = type_str;
+  if ((location && !cupsdCheckAccess(address, "", 0, location)) ||
+      (policy && !cupsdCheckAccess(address, "", 0, policy)))
+    domain = "local.";
+  else
+    domain = NULL;
 
-  keyvalue[i  ][0] = "Transparent";
-  keyvalue[i++][1] = "T";
+ /*
+  * Register IPP and (optionally) LPD...
+  */
 
-  keyvalue[i  ][0] = "Binary";
-  keyvalue[i++][1] = "T";
+  ipp_len    = 0;                      /* anti-compiler-warning-code */
+  ipp_txt = dnssdBuildTxtRecord(&ipp_len, p, 0);
 
-  if ((p->type & CUPS_PRINTER_FAX))
+  if (!p->ipp_ref)
   {
-    keyvalue[i  ][0] = "Fax";
-    keyvalue[i++][1] = "T";
-  }
+   /*
+    * Initial registration.  Use the _fax subtype for fax queues...
+    */
 
-  if ((p->type & CUPS_PRINTER_COLOR))
-  {
-    keyvalue[i  ][0] = "Color";
-    keyvalue[i++][1] = "T";
-  }
+    regtype = (p->type & CUPS_PRINTER_FAX) ? "_fax-ipp._tcp" :
+                                             "_ipp._tcp,_cups";
 
-  if ((p->type & CUPS_PRINTER_DUPLEX))
-  {
-    keyvalue[i  ][0] = "Duplex";
-    keyvalue[i++][1] = "T";
-  }
+    cupsdLogMessage(CUPSD_LOG_DEBUG, 
+                   "Registering DNS-SD printer %s with name \"%s\", "
+                   "type \"%s\", and domain \"%s\"", p->name, name, regtype,
+                   domain ? domain : "(null)");
 
-  if ((p->type & CUPS_PRINTER_STAPLE))
-  {
-    keyvalue[i  ][0] = "Staple";
-    keyvalue[i++][1] = "T";
-  }
+   /*
+    * Register the queue, dropping characters as needed until we succeed...
+    */
 
-  if ((p->type & CUPS_PRINTER_COPIES))
-  {
-    keyvalue[i  ][0] = "Copies";
-    keyvalue[i++][1] = "T";
-  }
+    nameptr = name + strlen(name);
 
-  if ((p->type & CUPS_PRINTER_COLLATE))
-  {
-    keyvalue[i  ][0] = "Collate";
-    keyvalue[i++][1] = "T";
-  }
+    do
+    {
+      p->ipp_ref = DNSSDRef;
+      if ((se = DNSServiceRegister(&p->ipp_ref, kDNSServiceFlagsShareConnection,
+                                   0, name, regtype, domain, NULL,
+                                  htons(DNSSDPort), ipp_len, ipp_txt,
+                                  dnssdRegisterCallback,
+                                  p)) == kDNSServiceErr_BadParam)
+      {
+       /*
+        * Name is too long, drop trailing characters, taking into account
+       * UTF-8 encoding...
+       */
 
-  if ((p->type & CUPS_PRINTER_PUNCH))
-  {
-    keyvalue[i  ][0] = "Punch";
-    keyvalue[i++][1] = "T";
-  }
+        nameptr --;
 
-  if ((p->type & CUPS_PRINTER_BIND))
-  {
-    keyvalue[i  ][0] = "Bind";
-    keyvalue[i++][1] = "T";
-  }
+        while (nameptr > name && (*nameptr & 0xc0) == 0x80)
+         nameptr --;
 
-  if ((p->type & CUPS_PRINTER_SORT))
+        if (nameptr > name)
+          *nameptr = '\0';
+      }
+    }
+    while (se == kDNSServiceErr_BadParam && nameptr > name);
+
+    if (se == kDNSServiceErr_NoError)
+    {
+      p->ipp_txt = ipp_txt;
+      p->ipp_len = ipp_len;
+      ipp_txt    = NULL;
+    }
+    else
+      cupsdLogMessage(CUPSD_LOG_WARN,
+                      "DNS-SD IPP registration of \"%s\" failed: %d",
+                     p->name, se);
+  }
+  else if (ipp_len != p->ipp_len || memcmp(ipp_txt, p->ipp_txt, ipp_len))
   {
-    keyvalue[i  ][0] = "Sort";
-    keyvalue[i++][1] = "T";
+   /*
+    * Update the existing registration...
+    */
+
+    /* A TTL of 0 means use record's original value (Radar 3176248) */
+    DNSServiceUpdateRecord(p->ipp_ref, NULL, 0, ipp_len, ipp_txt, 0);
+
+    if (p->ipp_txt)
+      free(p->ipp_txt);
+
+    p->ipp_txt = ipp_txt;
+    p->ipp_len = ipp_len;
+    ipp_txt    = NULL;
   }
 
-  keyvalue[i  ][0] = "pdl";
-  keyvalue[i++][1] = p->pdl ? p->pdl : "application/postscript";
+  if (ipp_txt)
+    free(ipp_txt);
 
-  if (p->num_auth_info_required)
+  if (BrowseLocalProtocols & BROWSE_LPD)
   {
-    char       *air = air_str;         /* Pointer into string */
+    printer_len    = 0;                        /* anti-compiler-warning-code */
+    printer_txt = dnssdBuildTxtRecord(&printer_len, p, 1);
 
+    if (!p->printer_ref)
+    {
+     /*
+      * Initial registration...
+      */
 
-    for (j = 0; j < p->num_auth_info_required; j ++)
+      cupsdLogMessage(CUPSD_LOG_DEBUG, 
+                     "Registering DNS-SD printer %s with name \"%s\", "
+                     "type \"_printer._tcp\", and domain \"%s\"", p->name,
+                     name, domain ? domain : "(null)");
+
+      p->printer_ref = DNSSDRef;
+      if ((se = DNSServiceRegister(&p->printer_ref,
+                                   kDNSServiceFlagsShareConnection,
+                                  0, name, "_printer._tcp", domain, NULL,
+                                  htons(515), printer_len, printer_txt,
+                                  dnssdRegisterCallback,
+                                  p)) == kDNSServiceErr_NoError)
+      {
+       p->printer_txt = printer_txt;
+       p->printer_len = printer_len;
+       printer_txt    = NULL;
+      }
+      else
+       cupsdLogMessage(CUPSD_LOG_WARN,
+                       "DNS-SD LPD registration of \"%s\" failed: %d",
+                       p->name, se);
+    }
+    else if (printer_len != p->printer_len ||
+             memcmp(printer_txt, p->printer_txt, printer_len))
     {
-      if (air >= (air_str + sizeof(air_str) - 2))
-        break;
+     /*
+      * Update the existing registration...
+      */
 
-      if (j)
-        *air++ = ',';
+      /* A TTL of 0 means use record's original value (Radar 3176248) */
+      DNSServiceUpdateRecord(p->printer_ref, NULL, 0, printer_len,
+                             printer_txt, 0);
 
-      strlcpy(air, p->auth_info_required[j], sizeof(air_str) - (air - air_str));
-      air += strlen(air);
+      if (p->printer_txt)
+       free(p->printer_txt);
+
+      p->printer_txt = printer_txt;
+      p->printer_len = printer_len;
+      printer_txt    = NULL;
     }
 
-    keyvalue[i  ][0] = "air";
-    keyvalue[i++][1] = air;
+    if (printer_txt)
+      free(printer_txt);
   }
+}
+
+
+/*
+ * 'dnssdUpdate()' - Handle DNS-SD queries.
+ */
+
+static void
+dnssdUpdate(void)
+{
+  DNSServiceErrorType  sdErr;          /* Service discovery error */
 
- /*
-  * Then pack them into a proper txt record...
-  */
 
-  return (dnssdPackTxtRecord(txt_len, keyvalue, i));
+  if ((sdErr = DNSServiceProcessResult(DNSSDRef)) != kDNSServiceErr_NoError)
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "DNS Service Discovery registration error %d!",
+                   sdErr);
 }
+#endif /* HAVE_DNSSD */
 
 
+#ifdef __APPLE__
 /*
- * 'dnssdDeregisterPrinter()' - Stop sending broadcast information for a
- *                              printer.
+ * 'get_hostconfig()' - Get an /etc/hostconfig service setting.
  */
 
-static void
-dnssdDeregisterPrinter(
-    cupsd_printer_t *p)                        /* I - Printer */
+static int                             /* O - 1 for YES or AUTOMATIC, 0 for NO */
+get_hostconfig(const char *name)       /* I - Name of service */
 {
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdDeregisterPrinter(%s)", p->name);
+  cups_file_t  *fp;                    /* Hostconfig file */
+  char         line[1024],             /* Line from file */
+               *ptr;                   /* Pointer to value */
+  int          state = 1;              /* State of service */
+
 
  /*
-  * Closing the socket deregisters the service
+  * Try opening the /etc/hostconfig file; if we can't open it, assume that
+  * the service is enabled/auto.
   */
 
-  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)
+  if ((fp = cupsFileOpen("/etc/hostconfig", "r")) != NULL)
   {
    /*
-    * p->txt_record is malloc'd, not _cupsStrAlloc'd...
+    * Read lines from the file until we find the service...
     */
 
-    free(p->txt_record);
-    p->txt_record = NULL;
+    while (cupsFileGets(fp, line, sizeof(line)))
+    {
+      if (line[0] == '#' || (ptr = strchr(line, '=')) == NULL)
+        continue;
+
+      *ptr++ = '\0';
+
+      if (!strcasecmp(line, name))
+      {
+       /*
+        * Found the service, see if it is set to "-NO-"...
+       */
+
+       if (!strncasecmp(ptr, "-NO-", 4))
+         state = 0;
+        break;
+      }
+    }
+
+    cupsFileClose(fp);
   }
+
+  return (state);
 }
+#endif /* __APPLE__ */
 
 
 /*
- * 'dnssdPackTxtRecord()' - Pack an array of key/value pairs into the
- *                          TXT record format.
+ * 'is_local_queue()' - Determine whether the URI points at a local queue.
  */
 
-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 */
+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 */
 {
-  int  i;                              /* Looping var */
-  int  length;                         /* Length of TXT record */
-  int  length2;                                /* Length of value */
-  char *txtRecord;                     /* TXT record buffer */
-  char *cursor;                                /* Looping pointer */
+  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 */
 
 
  /*
-  * Calculate the buffer size
+  * Pull the URI apart to see if this is a local or remote printer...
   */
 
-  for (length = i = 0; i < count; i++)
-    length += 1 + strlen(keyvalue[i][0]) + 
-             (keyvalue[i][1] ? 1 + strlen(keyvalue[i][1]) : 0);
+  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));
 
  /*
-  * Allocate and fill it
+  * Check for local server addresses...
   */
 
-  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;
+  if (!strcasecmp(host, ServerName) && port == LocalPort)
+    return (1);
 
-      *cursor++ = (unsigned char)(length + length2);
+  cupsdNetIFUpdate();
 
-      memcpy(cursor, keyvalue[i][0], length);
-      cursor += length;
+  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 (length2)
-      {
-        length2 --;
-       *cursor++ = '=';
-       memcpy(cursor, keyvalue[i][1], length2);
-       cursor += length2;
-      }
-    }
-  }
+ /*
+  * If we get here, the printer is remote...
+  */
 
-  return (txtRecord);
+  return (0);
 }
 
 
 /*
- * 'dnssdRegisterCallback()' - DNSServiceRegister callback.
+ * 'process_browse_data()' - Process new browse data.
  */
 
 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 */
+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 */
 {
-  cupsd_printer_t *p = (cupsd_printer_t *)context;
-                                       /* Current printer */
+  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 */
+  const char   *shortname;             /* Short queue name (queue) */
+  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 */
+  int          is_class;               /* Is this queue a class? */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdRegisterCallback(%s, %s) for %s",
-                  name, regtype, p->name);
+  cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                  "process_browse_data(uri=\"%s\", host=\"%s\", "
+                 "resource=\"%s\", type=%x, state=%d, location=\"%s\", "
+                 "info=\"%s\", make_model=\"%s\", num_attrs=%d, attrs=%p)",
+                 uri, host, resource, type, state,
+                 location ? location : "(nil)", info ? info : "(nil)",
+                 make_model ? make_model : "(nil)", num_attrs, attrs);
 
-  if (errorCode)
+ /*
+  * 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, 
-                   "DNSServiceRegister failed with error %d", (int)errorCode);
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Bad printer URI in browse data: %s", uri);
     return;
   }
-  else if (strcasecmp(name, p->reg_name))
-  {
-    cupsdLogMessage(CUPSD_LOG_INFO, "Using service name \"%s\" for \"%s\"",
-                    name, p->name);
 
-    cupsdSetString(&p->reg_name, name);
-    LastEvent |= CUPSD_EVENT_PRINTER_MODIFIED;
+  if (strchr(resource, '?') ||
+      (!strncmp(resource, "/printers/", 10) && strchr(resource + 10, '/')) ||
+      (!strncmp(resource, "/classes/", 9) && strchr(resource + 9, '/')))
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "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);
 
-/*
- * 'dnssdRegisterPrinter()' - Start sending broadcast information for a printer
- *                           or update the broadcast contents.
- */
+  if (BrowseRemoteOptions)
+  {
+    if (BrowseRemoteOptions[0] == '?')
+    {
+     /*
+      * Override server-supplied options...
+      */
 
-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                 resource[1024], /* Resource path for printer */
-                       str_buffer[1024];
-                                       /* C-string buffer */
-  const char           *computerName;  /* Computer name c-string ptr */
-  const char           *regtype;       /* Registration type */
-  const char           *domain;        /* Registration domain */
-  cupsd_location_t     *location,      /* Printer location */
-                       *policy;        /* Operation policy for Print-Job */
-  unsigned             address[4];     /* INADDR_ANY address */
-#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 */
+      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...
+      */
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdRegisterPrinter(%s) %s", p->name,
-                  !p->dnssd_ipp_ref ? "new" : "update");
+      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;
+  }
 
  /*
-  * If per-printer sharing was just disabled make sure we're not
-  * registered before returning.
+  * See if we already have it listed in the Printers list, and add it if not...
   */
 
-  if (!p->shared)
-  {
-    dnssdDeregisterPrinter(p);
-    return;
+  type     |= CUPS_PRINTER_REMOTE | CUPS_PRINTER_DISCOVERED;
+  type     &= ~CUPS_PRINTER_IMPLICIT;
+  update   = 0;
+  hptr     = strchr(host, '.');
+  sptr     = strchr(ServerName, '.');
+  is_class = type & CUPS_PRINTER_CLASS;
+
+  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, '.');
+    }
   }
 
- /*
-  * 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 */
+  if (is_class)
+  {
+   /*
+    * Remote destination is a class...
+    */
 
- /*
-  * The registered name takes the form of "<printer-info> @ <computer name>"...
-  */
+    if (!strncmp(resource, "/classes/", 9))
+      snprintf(name, sizeof(name), "%s@%s", resource + 9, host);
+    else
+      return;
 
-  name = NULL;
-  if (computerName)
-    cupsdSetStringf(&name, "%s @ %s",
-                    (p->info && strlen(p->info)) ? p->info : p->name,
-                   computerName);
+    shortname = resource + 9;
+  }
   else
-    cupsdSetString(&name, (p->info && strlen(p->info)) ? p->info : p->name);
-
-#ifdef HAVE_COREFOUNDATION_H
-  if (computerNameRef)
-    CFRelease(computerNameRef);
-#endif /* HAVE_COREFOUNDATION_H */
+  {
+   /*
+    * Remote destination is a printer...
+    */
 
- /*
-  * If an existing printer was renamed, unregister it and start over...
-  */
+    if (!strncmp(resource, "/printers/", 10))
+      snprintf(name, sizeof(name), "%s@%s", resource + 10, host);
+    else
+      return;
 
-  if (p->reg_name && strcmp(p->reg_name, name))
-    dnssdDeregisterPrinter(p);
+    shortname = resource + 10;
+  }
 
-  txt_len    = 0;                      /* anti-compiler-warning-code */
-  txt_record = dnssdBuildTxtRecord(&txt_len, p);
+  if (hptr && !*hptr)
+    *hptr = '.';                       /* Resource FQDN */
 
-  if (!p->dnssd_ipp_ref)
+  if ((p = cupsdFindDest(name)) == NULL && BrowseShortNames)
   {
    /*
-    * Initial registration...
+    * Long name doesn't exist, try short name...
     */
 
-    cupsdSetString(&p->reg_name, name);
+    cupsdLogMessage(CUPSD_LOG_DEBUG, "process_browse_data: %s not found...",
+                    name);
 
-    port = ippPort();
+    if ((p = cupsdFindDest(shortname)) == NULL)
+    {
+     /*
+      * Short name doesn't exist, use it for this shared queue.
+      */
 
-    for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners);
-        lis;
-        lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
+      cupsdLogMessage(CUPSD_LOG_DEBUG2, "process_browse_data: %s not found...",
+                     shortname);
+      strlcpy(name, shortname, sizeof(name));
+    }
+    else
     {
-      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)
+     /*
+      * Short name exists...
+      */
+
+      cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                      "process_browse_data: %s found, type=%x, hostname=%s...",
+                     shortname, p->type, p->hostname ? p->hostname : "(nil)");
+
+      if (p->type & CUPS_PRINTER_IMPLICIT)
+        p = NULL;                      /* Don't replace implicit classes */
+      else if (p->hostname && strcasecmp(p->hostname, host))
       {
-       port = ntohs(lis->address.ipv6.sin6_port);
-       break;
+       /*
+       * Short name exists but is for a different host.  If this is a remote
+       * queue, rename it and use the long name...
+       */
+
+       if (p->type & CUPS_PRINTER_REMOTE)
+       {
+         cupsdLogMessage(CUPSD_LOG_DEBUG,
+                         "Renamed remote %s \"%s\" to \"%s@%s\"...",
+                         is_class ? "class" : "printer", p->name, p->name,
+                         p->hostname);
+         cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, p, NULL,
+                       "%s \'%s\' deleted by directory services.",
+                       is_class ? "Class" : "Printer", p->name);
+
+         snprintf(newname, sizeof(newname), "%s@%s", p->name, p->hostname);
+         cupsdRenamePrinter(p, newname);
+
+         cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, p, NULL,
+                       "%s \'%s\' added by directory services.",
+                       is_class ? "Class" : "Printer", p->name);
+       }
+
+       /*
+        * Force creation with long name...
+       */
+
+       p = NULL;
       }
     }
+  }
+  else if (p)
+    cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                   "process_browse_data: %s found, type=%x, hostname=%s...",
+                   name, p->type, p->hostname ? p->hostname : "(nil)");
 
+  if (!p)
+  {
    /*
-    * If 'Allow printing from the Internet' is enabled (i.e. from any address)
-    * let dnssd decide on the domain, otherwise restrict it to ".local".
+    * Queue doesn't exist; add it...
     */
 
-    if (p->type & CUPS_PRINTER_CLASS)
-      snprintf(resource, sizeof(resource), "/classes/%s", p->name);
+    if (is_class)
+      p = cupsdAddClass(name);
     else
-      snprintf(resource, sizeof(resource), "/printers/%s", p->name);
+      p = cupsdAddPrinter(name);
 
-    address[0] = address[1] = address[2] = address[3] = 0;
-    location   = cupsdFindBest(resource, HTTP_POST);
-    policy     = cupsdFindPolicyOp(p->op_policy_ptr, IPP_PRINT_JOB);
+    cupsdClearString(&(p->hostname));
 
-    if ((location && !cupsdCheckAccess(address, "", 0, location)) ||
-        (policy && !cupsdCheckAccess(address, "", 0, policy)))
-      domain = "local.";
-    else
-      domain = NULL;
+    cupsdLogMessage(CUPSD_LOG_DEBUG, "Added remote %s \"%s\"...",
+                    is_class ? "class" : "printer", name);
+
+    cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, p, NULL,
+                 "%s \'%s\' added by directory services.",
+                 is_class ? "Class" : "Printer", name);
 
    /*
-    * Use the _fax subtype for fax queues...
+    * Force the URI to point to the real server...
     */
 
-    regtype = (p->type & CUPS_PRINTER_FAX) ? dnssdIPPFaxRegType :
-                                             dnssdIPPRegType;
+    p->type      = type & ~CUPS_PRINTER_REJECTING;
+    p->accepting = 1;
+
+    cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
+  }
 
+  if (!p)
+    return;
 
-    cupsdLogMessage(CUPSD_LOG_DEBUG, 
-                   "dnssdRegisterPrinter(%s) type, domain is \"%s\", \"%s\"",
-                   p->name, regtype, domain ? domain : "(null)");
+  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;
+
+    cupsdMarkDirty(CUPSD_DIRTY_REMOTE);
+  }
+
+ /*
+  * Update the state...
+  */
 
-    se = DNSServiceRegister(&p->dnssd_ipp_ref, 0, 0, name, regtype, 
-                           domain, NULL, htons(port), txt_len, txt_record,
-                           dnssdRegisterCallback, p);
+  p->state       = state;
+  p->browse_time = time(NULL);
 
+  if ((lease_duration = cupsGetOption("lease-duration", num_attrs,
+                                      attrs)) != NULL)
+  {
    /*
-    * In case the name is too long, try shortening the string one character
-    * at a time...
+    * Grab the lease-duration for the browse data; anything less then 1
+    * second or more than 1 week gets the default BrowseTimeout...
     */
 
-    if (se == kDNSServiceErr_BadParam)
-    {
-#ifdef HAVE_COREFOUNDATION_H
-      if ((shortNameRef = CFStringCreateMutable(NULL, 0)) != NULL)
-      {
-       CFStringAppendCString(shortNameRef, name, kCFStringEncodingUTF8);
-        nameLength = CFStringGetLength(shortNameRef);
+    i = atoi(lease_duration);
+    if (i < 1 || i > 604800)
+      i = BrowseTimeout;
 
-       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);
-         }
-       }
+    p->browse_expire = p->browse_time + i;
+  }
+  else
+    p->browse_expire = p->browse_time + BrowseTimeout;
 
-       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 */
-    }
+  if (type & CUPS_PRINTER_REJECTING)
+  {
+    type &= ~CUPS_PRINTER_REJECTING;
 
-    if (se == kDNSServiceErr_NoError)
+    if (p->accepting)
     {
-      p->dnssd_ipp_fd = DNSServiceRefSockFD(p->dnssd_ipp_ref);
-      p->txt_record   = txt_record;
-      p->txt_len      = txt_len;
-      txt_record      = NULL;
-
-      cupsdAddSelect(p->dnssd_ipp_fd, (cupsd_selfunc_t)cupsdUpdateDNSSDBrowse,
-                     NULL, (void *)p);
+      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;
+
+    cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP | CUPSD_DIRTY_REMOTE);
+  }
+
+  if (!make_model || !make_model[0])
+  {
+    if (is_class)
+      snprintf(local_make_model, sizeof(local_make_model),
+               "Remote Class on %s", host);
     else
-      cupsdLogMessage(CUPSD_LOG_WARN,
-                      "DNS-SD registration of \"%s\" failed with %d",
-                     p->name, se);
+      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;
   }
-  else if (txt_len != p->txt_len || memcmp(txt_record, p->txt_record, txt_len))
+
+  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;
+          }
+      }
+    }
+
    /*
-    * Update the existing registration...
+    * Free the old options...
     */
 
-    /* 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);
+    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.",
+                 is_class ? "Class" : "Printer", p->name);
+
+    cupsdExpireSubscriptions(p, NULL);
+    cupsdDeletePrinter(p, 1);
+    cupsdUpdateImplicitClasses();
+    cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP | CUPSD_DIRTY_REMOTE);
+  }
+  else if (update)
+  {
+    cupsdSetPrinterAttrs(p);
+    cupsdUpdateImplicitClasses();
+  }
 
-    if (p->txt_record)
-      free(p->txt_record);
+ /*
+  * 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...
+    */
 
-    p->txt_record = txt_record;
-    p->txt_len    = txt_len;
-    txt_record    = NULL;
+    for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
+         p;
+        p = (cupsd_printer_t *)cupsArrayNext(Printers))
+      if (p->type & CUPS_PRINTER_DEFAULT)
+      {
+        DefaultPrinter = p;
+        cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP | CUPSD_DIRTY_REMOTE);
+       break;
+      }
   }
 
-  if (txt_record)
-    free(txt_record);
+ /*
+  * Do auto-classing if needed...
+  */
 
-  cupsdClearString(&name);
+  process_implicit_classes();
 }
-#endif /* HAVE_DNSSD */
 
 
 /*
@@ -2837,7 +2944,7 @@ process_implicit_classes(void)
 
         update = 1;
 
-       cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
+       cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP | CUPSD_DIRTY_REMOTE);
 
         cupsdLogMessage(CUPSD_LOG_DEBUG, "Added implicit class \"%s\"...",
                        name);
@@ -3155,7 +3262,7 @@ send_ldap_browse(cupsd_printer_t *p)      /* I - Printer to register */
                  NULL
                };
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_ldap_browse: %s\n", p->name);
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_ldap_browse: %s", p->name);
 
  /*
   * Everything in ldap is ** so we fudge around it...
@@ -3679,10 +3786,19 @@ update_cups_browse(void)
     {
       cupsdLogMessage(CUPSD_LOG_ERROR, "Browse recv failed - %s.",
                       strerror(errno));
-      cupsdLogMessage(CUPSD_LOG_ERROR, "Browsing turned off.");
+      cupsdLogMessage(CUPSD_LOG_ERROR, "CUPS browsing turned off.");
+
+#ifdef WIN32
+      closesocket(BrowseSocket);
+#else
+      close(BrowseSocket);
+#endif /* WIN32 */
+
+      cupsdRemoveSelect(BrowseSocket);
+      BrowseSocket = -1;
 
-      cupsdStopBrowsing();
-      Browsing = 0;
+      BrowseLocalProtocols  &= ~BROWSE_CUPS;
+      BrowseRemoteProtocols &= ~BROWSE_CUPS;
     }
 
     return;
@@ -4100,5 +4216,5 @@ update_smb(int onoff)                     /* I - 1 = turn on, 0 = turn off */
 
 
 /*
- * End of "$Id: dirsvc.c 7003 2007-10-01 23:10:13Z mike $".
+ * End of "$Id: dirsvc.c 7676 2008-06-18 23:42:37Z mike $".
  */