Blob Blame History Raw
diff -up cups-filters-1.0.35/utils/cups-browsed.c.browsed-efficiency cups-filters-1.0.35/utils/cups-browsed.c
--- cups-filters-1.0.35/utils/cups-browsed.c.browsed-efficiency	2015-06-25 16:48:33.667228224 +0100
+++ cups-filters-1.0.35/utils/cups-browsed.c	2015-06-25 16:48:48.215194502 +0100
@@ -23,15 +23,16 @@
 
 #include <ctype.h>
 #include <errno.h>
-#include <ifaddrs.h>
 #if defined(__OpenBSD__)
 #include <sys/socket.h>
 #endif /* __OpenBSD__ */
+#include <sys/types.h>
 #include <net/if.h>
 #include <netinet/in.h>
+#include <ifaddrs.h>
 #include <resolv.h>
 #include <stdio.h>
-#include <sys/types.h>
+#include <sys/stat.h>
 #include <assert.h>
 #include <stdlib.h>
 #include <time.h>
@@ -62,17 +63,20 @@
 
 /* Status of remote printer */
 typedef enum printer_status_e {
-  STATUS_UNCONFIRMED = 0,
-  STATUS_CONFIRMED,
-  STATUS_TO_BE_CREATED,
-  STATUS_BROWSE_PACKET_RECEIVED,
-  STATUS_DISAPPEARED
+  STATUS_UNCONFIRMED = 0,	/* Generated in a previous session */
+  STATUS_CONFIRMED,		/* Avahi confirms UNCONFIRMED printer */
+  STATUS_TO_BE_CREATED,		/* Scheduled for creation */
+  STATUS_BROWSE_PACKET_RECEIVED,/* Scheduled for creation with timeout */
+  STATUS_DISAPPEARED		/* Scheduled for removal */
 } printer_status_t;
 
 /* Data structure for remote printers */
 typedef struct remote_printer_s {
   char *name;
   char *uri;
+  char *ppd;
+  char *model;
+  char *ifscript;
   printer_status_t status;
   time_t timeout;
   int duplicate;
@@ -100,13 +104,62 @@ typedef struct allow_s {
   http_addr_t mask;
 } allow_t;
 
+/* Data struct for a printer discovered using BrowsePoll */
+typedef struct browsepoll_printer_s {
+  char *uri_supported;
+  char *info;
+} browsepoll_printer_t;
+
+/* Data structure for a BrowsePoll server */
+typedef struct browsepoll_s {
+  char *server;
+  int port;
+  int major;
+  int minor;
+  gboolean can_subscribe;
+  int subscription_id;
+  int sequence_number;
+
+  /* Remember which printers we discovered. This way we can just ask
+   * if anything has changed, and if not we know these printers are
+   * still there. */
+  GList *printers; /* of browsepoll_printer_t */
+} browsepoll_t;
+
+/* Local printer (key is name) */
+typedef struct local_printer_s {
+  char *device_uri;
+  gboolean cups_browsed_controlled;
+} local_printer_t;
+
+/* Browse data to send for local printer */
+typedef struct browse_data_s {
+  int type;
+  int state;
+  char *uri;
+  char *location;
+  char *info;
+  char *make_model;
+  char *browse_options;
+} browse_data_t;
+
 cups_array_t *remote_printers;
 static cups_array_t *netifs;
 static cups_array_t *browseallow;
+static gboolean browseallow_all = FALSE;
+
+static GHashTable *local_printers;
+static browsepoll_t *local_printers_context = NULL;
+static http_t *local_conn = NULL;
+static gboolean inhibit_local_printers_update = FALSE;
+
+static GList *browse_data = NULL;
 
 static GMainLoop *gmainloop = NULL;
 #ifdef HAVE_AVAHI
 static AvahiGLibPoll *glib_poll = NULL;
+static AvahiClient *client = NULL;
+static AvahiServiceBrowser *sb1 = NULL, *sb2 = NULL;
 #endif /* HAVE_AVAHI */
 static guint queues_timer_id = (guint) -1;
 static int browsesocket = -1;
@@ -118,12 +171,22 @@ static unsigned int BrowseRemoteProtocol
 static unsigned int BrowseInterval = 60;
 static unsigned int BrowseTimeout = 300;
 static uint16_t BrowsePort = 631;
-static char **BrowsePoll = NULL;
+static browsepoll_t **BrowsePoll = NULL;
 static size_t NumBrowsePoll = 0;
+static char *DomainSocket = NULL;
+static unsigned int CreateIPPPrinterQueues = 0;
+static int autoshutdown = 0;
+static int autoshutdown_avahi = 0;
+static int autoshutdown_timeout = 30;
+static guint autoshutdown_exec_id = 0;
 
 static int debug = 0;
 
 static void recheck_timer (void);
+static void browse_poll_create_subscription (browsepoll_t *context,
+					     http_t *conn);
+static gboolean browse_poll_get_notifications (browsepoll_t *context,
+					       http_t *conn);
 
 #if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 5)
 #define HAVE_CUPS_1_6 1
@@ -211,6 +274,15 @@ ippNextAttribute(ipp_t *ipp)
   return (ipp->current = ipp->current->next);
 }
 
+int
+ippSetVersion(ipp_t *ipp, int major, int minor)
+{
+  if (!ipp || major < 0 || minor < 0)
+    return (0);
+  ipp->request.any.version[0] = major;
+  ipp->request.any.version[1] = minor;
+  return (1);
+}
 #endif
 
 void debug_printf(const char *format, ...) {
@@ -223,16 +295,425 @@ void debug_printf(const char *format, ..
   }
 }
 
+static const char *
+password_callback (const char *prompt,
+		   http_t *http,
+		   const char *method,
+		   const char *resource,
+		   void *user_data)
+{
+  return NULL;
+}
+
+static http_t *
+http_connect_local (void)
+{
+  if (!local_conn)
+    local_conn = httpConnectEncrypt(cupsServer(), ippPort(),
+				    cupsEncryption());
+
+  return local_conn;
+}
+
+static void
+http_close_local (void)
+{
+  if (local_conn) {
+    httpClose (local_conn);
+    local_conn = NULL;
+  }
+}
+
+static local_printer_t *
+new_local_printer (const char *device_uri,
+		   gboolean cups_browsed_controlled)
+{
+  local_printer_t *printer = g_malloc (sizeof (local_printer_t));
+  printer->device_uri = strdup (device_uri);
+  printer->cups_browsed_controlled = cups_browsed_controlled;
+  return printer;
+}
+
+static void
+free_local_printer (gpointer data)
+{
+  local_printer_t *printer = data;
+  free (printer->device_uri);
+  free (printer);
+}
+
+static gboolean
+local_printer_has_uri (gpointer key,
+		       gpointer value,
+		       gpointer user_data)
+{
+  local_printer_t *printer = value;
+  char *device_uri = user_data;
+  return g_str_equal (printer->device_uri, device_uri);
+}
+
+static void
+local_printers_create_subscription (http_t *conn)
+{
+  if (!local_printers_context) {
+    local_printers_context = g_malloc0 (sizeof (browsepoll_t));
+    local_printers_context->server = "localhost";
+    local_printers_context->port = BrowsePort;
+    local_printers_context->can_subscribe = TRUE;
+  }
+
+  browse_poll_create_subscription (local_printers_context, conn);
+}
+
+static void
+get_local_printers (void)
+{
+  cups_dest_t *dests = NULL;
+  int num_dests = cupsGetDests (&dests);
+  debug_printf ("cups-browsed [BrowsePoll localhost:631]: cupsGetDests\n");
+  g_hash_table_remove_all (local_printers);
+  for (int i = 0; i < num_dests; i++) {
+    const char *val;
+    cups_dest_t *dest = &dests[i];
+    local_printer_t *printer;
+    gboolean cups_browsed_controlled;
+    const char *device_uri = cupsGetOption ("device-uri",
+					    dest->num_options,
+					    dest->options);
+    val = cupsGetOption (CUPS_BROWSED_MARK,
+			 dest->num_options,
+			 dest->options);
+    cups_browsed_controlled = val && (!strcasecmp (val, "yes") ||
+				      !strcasecmp (val, "on") ||
+				      !strcasecmp (val, "true"));
+    printer = new_local_printer (device_uri,
+				 cups_browsed_controlled);
+    g_hash_table_insert (local_printers,
+			 g_strdup (dest->name),
+			 printer);
+  }
+
+  cupsFreeDests (num_dests, dests);
+}
+
+static browse_data_t *
+new_browse_data (int type, int state, const gchar *uri,
+		 const gchar *location, const gchar *info,
+		 const gchar *make_model, const gchar *browse_options)
+{
+  browse_data_t *data = g_malloc (sizeof (browse_data_t));
+  data->type = type;
+  data->state = state;
+  data->uri = g_strdup (uri);
+  data->location = g_strdup (location);
+  data->info = g_strdup (info);
+  data->make_model = g_strdup (make_model);
+  data->browse_options = g_strdup (browse_options);
+  return data;
+}
+
+static void
+browse_data_free (gpointer data)
+{
+  browse_data_t *bdata = data;
+  g_free (bdata->uri);
+  g_free (bdata->location);
+  g_free (bdata->info);
+  g_free (bdata->make_model);
+  g_free (bdata->browse_options);
+  g_free (bdata);
+}
+
+static void
+prepare_browse_data (void)
+{
+  static const char * const rattrs[] = { "printer-type",
+					 "printer-state",
+					 "printer-uri-supported",
+					 "printer-info",
+					 "printer-location",
+					 "printer-make-and-model",
+					 "auth-info-required",
+					 "printer-uuid",
+					 "job-template" };
+  ipp_t *request, *response = NULL;
+  ipp_attribute_t *attr;
+  http_t *conn = NULL;
+
+  conn = http_connect_local ();
+
+  if (conn == NULL) {
+    debug_printf("cups-browsed: browse send failed to connect to localhost\n");
+    goto fail;
+  }
+
+  request = ippNewRequest(CUPS_GET_PRINTERS);
+  ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+		 "requested-attributes", sizeof (rattrs) / sizeof (rattrs[0]),
+		 NULL, rattrs);
+  ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+		"requesting-user-name", NULL, cupsUser ());
+
+  debug_printf("cups-browsed: preparing browse data\n");
+  response = cupsDoRequest (conn, request, "/");
+  if (cupsLastError() > IPP_OK_CONFLICT) {
+    debug_printf("cups-browsed: browse send failed for localhost: %s\n",
+		 cupsLastErrorString ());
+    goto fail;
+  }
+
+  g_list_free_full (browse_data, browse_data_free);
+  browse_data = NULL;
+  for (attr = ippFirstAttribute(response); attr;
+       attr = ippNextAttribute(response)) {
+    int type = -1, state = -1;
+    const char *uri = NULL;
+    gchar *location = NULL;
+    gchar *info = NULL;
+    gchar *make_model = NULL;
+    GString *browse_options = g_string_new ("");
+
+    /* Skip any non-printer attributes */
+    while (attr && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
+      attr = ippNextAttribute(response);
+
+    if (!attr)
+      break;
+
+    while (attr && ippGetGroupTag(attr) == IPP_TAG_PRINTER) {
+      const char *attrname = ippGetName(attr);
+      int value_tag = ippGetValueTag(attr);
+
+      if (!strcasecmp(attrname, "printer-type") &&
+	  value_tag == IPP_TAG_ENUM) {
+	type = ippGetInteger(attr, 0);
+	if (type & CUPS_PRINTER_NOT_SHARED) {
+	  /* Skip CUPS queues not marked as shared */
+	  state = -1;
+	  type = -1;
+	  break;
+	}
+      } else if (!strcasecmp(attrname, "printer-state") &&
+	       value_tag == IPP_TAG_ENUM)
+	state = ippGetInteger(attr, 0);
+      else if (!strcasecmp(attrname, "printer-uri-supported") &&
+	       value_tag == IPP_TAG_URI)
+	uri = ippGetString(attr, 0, NULL);
+      else if (!strcasecmp(attrname, "printer-location") &&
+	       value_tag == IPP_TAG_TEXT) {
+	/* Remove quotes */
+	gchar **tokens = g_strsplit (ippGetString(attr, 0, NULL), "\"", -1);
+	location = g_strjoinv ("", tokens);
+	g_strfreev (tokens);
+      } else if (!strcasecmp(attrname, "printer-info") &&
+		 value_tag == IPP_TAG_TEXT) {
+	/* Remove quotes */
+	gchar **tokens = g_strsplit (ippGetString(attr, 0, NULL), "\"", -1);
+	info = g_strjoinv ("", tokens);
+	g_strfreev (tokens);
+      } else if (!strcasecmp(attrname, "printer-make-and-model") &&
+		 value_tag == IPP_TAG_TEXT) {
+	/* Remove quotes */
+	gchar **tokens = g_strsplit (ippGetString(attr, 0, NULL), "\"", -1);
+	make_model = g_strjoinv ("", tokens);
+	g_strfreev (tokens);
+      } else if (!strcasecmp(attrname, "auth-info-required") &&
+		 value_tag == IPP_TAG_KEYWORD) {
+	if (strcasecmp (ippGetString(attr, 0, NULL), "none"))
+	  g_string_append_printf (browse_options, "auth-info-required=%s ",
+				  ippGetString(attr, 0, NULL));
+      } else if (!strcasecmp(attrname, "printer-uuid") &&
+		 value_tag == IPP_TAG_URI)
+	g_string_append_printf (browse_options, "uuid=%s ",
+				ippGetString(attr, 0, NULL));
+      else if (!strcasecmp(attrname, "job-sheets-default") &&
+	       value_tag == IPP_TAG_NAME &&
+	       ippGetCount(attr) == 2)
+	g_string_append_printf (browse_options, "job-sheets=%s,%s ",
+				ippGetString(attr, 0, NULL),
+				ippGetString(attr, 1, NULL));
+      else if (strstr(attrname, "-default")) {
+	gchar *name = g_strdup (attrname);
+	gchar *value = NULL;
+	*strstr (name, "-default") = '\0';
+
+	switch (value_tag) {
+	  gchar **tokens;
+
+	case IPP_TAG_KEYWORD:
+	case IPP_TAG_STRING:
+	case IPP_TAG_NAME:
+	  /* Escape value */
+	  tokens = g_strsplit_set (ippGetString(attr, 0, NULL),
+				   " \"\'\\", -1);
+	  value = g_strjoinv ("\\", tokens);
+	  g_strfreev (tokens);
+	  break;
+
+	default:
+	  /* other values aren't needed? */
+	  debug_printf("cups-browsed: skipping %s (%d)\n", name, value_tag);
+	  break;
+	}
+
+	if (value) {
+	  g_string_append_printf (browse_options, "%s=%s ", name, value);
+	  g_free (value);
+	}
+
+	g_free (name);
+      }
+
+      attr = ippNextAttribute(response);
+    }
+
+    if (type != -1 && state != -1 && uri && location && info && make_model) {
+      gchar *browse_options_str = g_string_free (browse_options, FALSE);
+      browse_data_t *data;
+      browse_options = NULL;
+      g_strchomp (browse_options_str);
+      data = new_browse_data (type, state, uri, location,
+			      info, make_model, browse_options_str);
+      browse_data = g_list_insert (browse_data, data, 0);
+      g_free (browse_options_str);
+    }
+
+    if (make_model)
+      g_free (make_model);
+
+    if (info)
+      g_free (info);
+
+    if (location)
+      g_free (location);
+
+    if (browse_options)
+      g_string_free (browse_options, TRUE);
+
+    if (!attr)
+      break;
+  }
+
+ fail:
+  if (response)
+    ippDelete(response);
+}
+
+static void
+update_local_printers (void)
+{
+  gboolean get_printers = FALSE;
+  http_t *conn;
+
+  if (inhibit_local_printers_update)
+    return;
+
+  conn = http_connect_local ();
+  if (conn &&
+      (!local_printers_context || local_printers_context->can_subscribe)) {
+    if (!local_printers_context ||
+	local_printers_context->subscription_id == -1) {
+      /* No subscription yet. First, create the subscription. */
+      local_printers_create_subscription (conn);
+      get_printers = TRUE;
+    } else
+      /* We already have a subscription, so use it. */
+
+      /* Note: for the moment, browse_poll_get_notifications() just
+       * tells us whether we should re-fetch the printer list, so it
+       * is safe to use here. */
+      get_printers = browse_poll_get_notifications (local_printers_context,
+						    conn);
+  } else
+    get_printers = TRUE;
+
+  if (get_printers) {
+    get_local_printers ();
+
+    if (BrowseLocalProtocols & BROWSE_CUPS)
+      prepare_browse_data ();
+  }
+}
+
+gboolean
+autoshutdown_execute (gpointer data)
+{
+  /* Are we still in auto shutdown mode and are we still without queues */
+  if (autoshutdown && cupsArrayCount(remote_printers) == 0) {
+    debug_printf("cups-browsed: Automatic shutdown as there are no print queues maintained by us for %d sec.\n",
+		 autoshutdown_timeout);
+    g_main_loop_quit(gmainloop);
+  }
+
+  /* Stop this timeout handler, we needed it only once */
+  return FALSE;
+}
+
 static remote_printer_t *
 create_local_queue (const char *name,
 		    const char *uri,
 		    const char *host,
 		    const char *info,
 		    const char *type,
-		    const char *domain)
+		    const char *domain,
+		    const char *pdl,
+		    const char *make_model,
+		    int is_cups_queue)
 {
   remote_printer_t *p;
   remote_printer_t *q;
+  int		fd = 0;			/* Script file descriptor */
+  char		tempfile[1024];		/* Temporary file */
+  char		buffer[8192];		/* Buffer for creating script */
+  int           bytes;
+  const char	*cups_serverbin;	/* CUPS_SERVERBIN environment variable */
+#if 0
+  int i, uri_status, port, status;
+  http_t *http;
+  char scheme[10], userpass[1024], host_name[1024], resource[1024];
+  ipp_t *request, *response;
+  ipp_attribute_t *attr;
+  static const char * const requested_attrs[] =
+    {	/* Requested attributes for getting IPP network printer capabilities */
+	/* Explicit attribute listings for the case that "all" does not cover
+	   everything */
+      "job-template",
+      "printer-description",
+      /*"document-format-supported",
+      "color-supported",
+      "pages-per-minute",
+      "pages-per-minute-color",
+      "media-supported",
+      "media-ready",
+      "media-default",
+      "media-type-supported",
+      "media-source-supported",*/
+      "media-col-database",
+      /*"sides-supported",
+      "sides-default",
+      "output-bin-supported",
+      "output-bin-default",
+      "finishings-supported",
+      "finishings-default",
+      "print-color-mode-supported",
+      "print-color-mode-default",
+      "output-mode-supported",
+      "output-mode-default",
+      "print-quality-supported",
+      "print-quality-default",
+      "printer-resolution-supported",
+      "printer-resolution-default",
+      "copies-supported",
+      "copies-default",*/
+      /* Catch things which were forgotten above or newly introduced */
+      "all"
+    };
+  static int versions_to_try[] =
+    {
+      20,
+      11
+    };
+#endif /* 0 */
 
   /* Mark this as a queue to be created locally pointing to the printer */
   if ((p = (remote_printer_t *)calloc(1, sizeof(remote_printer_t))) == NULL) {
@@ -264,39 +745,219 @@ create_local_queue (const char *name,
     goto fail;
 
   p->domain = strdup (domain);
-  if (!p->domain) {
-  fail:
-    debug_printf("cups-browsed: ERROR: Unable to allocate memory.\n");
-    free (p->type);
-    free (p->service_name);
-    free (p->host);
-    free (p->uri);
-    free (p->name);
-    free (p);
-    return NULL;
-  }
+  if (!p->domain)
+    goto fail;
 
   /* Schedule for immediate creation of the CUPS queue */
   p->status = STATUS_TO_BE_CREATED;
   p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
 
-  /* Check whether we have an equally named queue already from another
-     server */
-  for (q = (remote_printer_t *)cupsArrayFirst(remote_printers);
-       q;
-       q = (remote_printer_t *)cupsArrayNext(remote_printers))
-    if (!strcmp(q->name, p->name))
-      break;
-  p->duplicate = q ? 1 : 0;
+  if (is_cups_queue) {
+    /* Our local queue must be raw, so that the PPD file and driver
+       on the remote CUPS server get used */
+    p->ppd = NULL;
+    p->model = NULL;
+    p->ifscript = NULL;
+    /* Check whether we have an equally named queue already from another
+       server */
+    for (q = (remote_printer_t *)cupsArrayFirst(remote_printers);
+	 q;
+	 q = (remote_printer_t *)cupsArrayNext(remote_printers))
+      if (!strcasecmp(q->name, p->name))
+	break;
+    p->duplicate = (q && q->status != STATUS_DISAPPEARED &&
+		    q->status != STATUS_UNCONFIRMED) ? 1 : 0;
+    if (p->duplicate)
+      debug_printf("cups-browsed: Printer %s already available through host %s.\n",
+		   p->name, q->host);
+    else if (q) {
+      q->duplicate = 1;
+      debug_printf("cups-browsed: Unconfirmed/disappeared printer %s already available through host %s, marking that printer duplicate of the newly found one.\n",
+		   p->name, q->host);
+    }
+  } else {
+    /* Non-CUPS printer broadcasts are most probably from printers
+       directly connected to the network and using the IPP protocol.
+       We check whether we can set them up without a device-specific
+       driver, only using page description languages which the
+       operating system provides: PCL 5c/5e/6/XL, PostScript, PDF, PWG
+       Raster. Especially IPP Everywhere printers and PDF-capable 
+       AirPrint printers will work this way. Making only driverless
+       queues we can get an easy, configuration-less way to print
+       from mobile devices, even if there is no CUPS server with
+       shared printers around. */
+
+    if (CreateIPPPrinterQueues == 0) {
+      debug_printf("cups-browsed: Printer %s (%s) is an IPP network printer and cups-browsed id not configured to set up such printers automatically, ignoring this printer.\n",
+		   p->name, p->uri);
+      goto fail;
+    }
+
+    if (!pdl || pdl[0] == '\0' || (!strcasestr(pdl, "application/postscript") && !strcasestr(pdl, "application/pdf") && !strcasestr(pdl, "image/pwg-raster") && !strcasestr(pdl, "application/vnd.hp-PCL") && !strcasestr(pdl, "application/vnd.hp-PCLXL"))) {
+      debug_printf("cups-browsed: Cannot create remote printer %s (%s) as its PDLs are not known, ignoring this printer.\n",
+		   p->name, p->uri);
+      goto fail;
+    }
+
+#if 0
+    uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, uri,
+				 scheme, sizeof(scheme),
+				 userpass, sizeof(userpass),
+				 host_name, sizeof(host_name),
+				 &(port),
+				 resource, sizeof(resource));
+    if (uri_status != HTTP_URI_OK)
+      goto fail;
+    if ((http = httpConnect(host_name, port)) ==
+	NULL) {
+      debug_printf("cups-browsed: Cannot connect to remote printer %s (%s:%d), ignoring this printer.\n",
+		   p->uri, host_name, port);
+      goto fail;
+    }
+    for (i = 0;
+	 i < sizeof(versions_to_try) / sizeof(versions_to_try[0]);
+	 i ++) {
+      /* Create IPP request */
+      request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
+      /* Set IPP version */
+      ippSetVersion(request, versions_to_try[i] / 10, versions_to_try[i] % 10);
+      /* Printer URI */
+      ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+		   "printer-uri", NULL, p->uri);
+      /* Requested IPP attributes */
+      ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+		    "requested-attributes",
+		    sizeof(requested_attrs) / sizeof(requested_attrs[0]),
+		    NULL, requested_attrs);
+      /* Do it */
+      response = cupsDoRequest(http, request, resource);
+      if (response == NULL) {
+	debug_printf("cups-browsed: No answer to Get-Printer-Attributes IPP request from remote printer %s, ignoring this printer (IPP Error: %s %s).\n",
+		     p->uri, ippErrorString(cupsLastError()),
+		     cupsLastErrorString());
+	httpClose(http);
+	goto fail;
+      }
+      status = cupsLastError();
+      debug_printf("cups-browsed: Remote printer %s, IPP %3.1f: %s (%s)\n",
+		   p->uri, versions_to_try[i] / 10.0,
+		   ippErrorString(cupsLastError()),
+		   cupsLastErrorString());
+      /* If succeeded, go on, on error try a lower IPP version */
+      if (status < 0x0400)
+	break;
+    }
+    if (i >= sizeof(versions_to_try) / sizeof(versions_to_try[0])) {
+      /* All IPP versions failed */
+      debug_printf("cups-browsed: Remote printer %s: All IPP versions failed\n",
+		   p->uri);
+      goto fail;
+    }
+    /* Read out the printer's capabilities */
+    attr = ippFirstAttribute(response);
+    while (attr) {
+      debug_printf("Attr: %s\n",
+		   ippGetName(attr));
+      for (i = 0; i < ippGetCount(attr); i ++)
+	debug_printf("Keyword: %s\n",
+		     ippGetString(attr, i, NULL));
+      attr = ippNextAttribute(response);
+    }
+    attr = ippFindAttribute(response,
+			    "document-format-supported",
+			    IPP_TAG_ZERO);
+    if (attr)
+      for (i = 0; i < ippGetCount(attr); i ++)
+	debug_printf("Format: %s\n",
+		     ippGetString(attr, i, NULL));
+    else
+      debug_printf("No formats\n");
+    
+    /* Clean up */
+    ippDelete(response);
+    httpClose(http);
+#endif /* 0 */
+
+    p->duplicate = 0;
+
+    /*p->model = "drv:///sample.drv/laserjet.ppd";
+      debug_printf("cups-browsed: PPD from system for %s: %s\n", p->name, p->model);*/
+
+    /*p->ppd = "/usr/share/ppd/cupsfilters/pxlcolor.ppd";
+      debug_printf("cups-browsed: PPD from file for %s: %s\n", p->name, p->ppd);*/
+
+    /*p->ifscript = "/usr/lib/cups/filter/pdftoippprinter-wrapper";
+      debug_printf("cups-browsed: System V Interface script for %s: %s\n", p->name, p->ifscript);*/
+
+    if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
+      cups_serverbin = CUPS_SERVERBIN;
+
+    if ((fd = cupsTempFd(tempfile, sizeof(tempfile))) < 0) {
+      debug_printf("Unable to create interface script file\n");
+      goto fail;
+    }
+
+    debug_printf("Creating temp script file \"%s\"\n", tempfile);
+
+    snprintf(buffer, sizeof(buffer),
+	     "#!/bin/sh\n"
+	     "# System V interface script for printer %s generated by cups-browsed\n"
+	     "\n"
+	     "if [ $# -lt 5 -o $# -gt 6 ]; then\n"
+	     "  echo \"ERROR: $0 job-id user title copies options [file]\" >&2\n"
+	     "  exit 1\n"
+	     "fi\n"
+	     "\n"
+	     "# Read from given file\n"
+	     "if [ -n \"$6\" ]; then\n"
+	     "  exec \"$0\" \"$1\" \"$2\" \"$3\" \"$4\" \"$5\" < \"$6\"\n"
+	     "fi\n"
+	     "\n"
+	     "extra_options=\"output-format=%s make-and-model=%s\"\n"
+	     "\n"
+	     "%s/filter/pdftoippprinter \"$1\" \"$2\" \"$3\" \"$4\" \"$5 $extra_options\"\n",
+	     p->name, pdl, make_model, cups_serverbin);
+
+    bytes = write(fd, buffer, strlen(buffer));
+    if (bytes != strlen(buffer)) {
+      debug_printf("Unable to write interface script into the file\n");
+      goto fail;
+    }
+
+    close(fd);
+
+    p->ppd = NULL;
+    p->model = NULL;
+    p->ifscript = strdup(tempfile);
+  }
 
   /* Add the new remote printer entry */
   cupsArrayAdd(remote_printers, p);
 
-  if (p->duplicate)
-    debug_printf("cups-browsed: Printer already available through host %s.\n",
-		 q->host);
+  /* If auto shutdown is active we have perhaps scheduled a timer to shut down
+     due to not having queues any more to maintain, kill the timer now */
+  if (autoshutdown && autoshutdown_exec_id &&
+      cupsArrayCount(remote_printers) > 0) {
+    debug_printf ("cups-browsed: New printers there to make available, killing auto shutdown timer.\n");
+    g_source_destroy(g_main_context_find_source_by_id(NULL,
+						      autoshutdown_exec_id));
+    autoshutdown_exec_id = 0;
+  }
 
   return p;
+
+ fail:
+  debug_printf("cups-browsed: ERROR: Unable to create print queue, ignoring printer.\n");
+  free (p->type);
+  free (p->service_name);
+  free (p->host);
+  free (p->uri);
+  free (p->name);
+  if (p->ppd) free (p->ppd);
+  if (p->model) free (p->model);
+  if (p->ifscript) free (p->ifscript);
+  free (p);
+  return NULL;
 }
 
 /*
@@ -414,8 +1075,7 @@ gboolean handle_cups_queues(gpointer unu
 
       /* Remove the CUPS queue */
       if (!p->duplicate) { /* Duplicates do not have a CUPS queue */
-	if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
-				       cupsEncryption())) == NULL) {
+	if ((http = http_connect_local ()) == NULL) {
 	  debug_printf("cups-browsed: Unable to connect to CUPS!\n");
 	  p->timeout = current_time + TIMEOUT_RETRY;
 	  break;
@@ -429,7 +1089,6 @@ gboolean handle_cups_queues(gpointer unu
 	if (num_jobs != 0) { /* error or jobs */
 	  debug_printf("cups-browsed: Queue has still jobs or CUPS error!\n");
 	  cupsFreeJobs(num_jobs, jobs);
-	  httpClose(http);
 	  /* Schedule the removal of the queue for later */
 	  p->timeout = current_time + TIMEOUT_RETRY;
 	  break;
@@ -454,7 +1113,7 @@ gboolean handle_cups_queues(gpointer unu
 	    if (attr) {
 	      for (; attr && ippGetGroupTag(attr) == IPP_TAG_PRINTER;
 		   attr = ippNextAttribute(response)) {
-		if (!strcmp(ippGetName(attr), "printer-name") &&
+		if (!strcasecmp(ippGetName(attr), "printer-name") &&
 		    ippGetValueTag(attr) == IPP_TAG_NAME) {
 		  default_printer_name = ippGetString(attr, 0, NULL);
 		  break;
@@ -469,9 +1128,9 @@ gboolean handle_cups_queues(gpointer unu
 	    !strcasecmp(default_printer_name, p->name)) {
 	  /* Printer is currently the system's default printer,
 	     do not remove it */
-	  httpClose(http);
 	  /* Schedule the removal of the queue for later */
 	  p->timeout = current_time + TIMEOUT_RETRY;
+	  ippDelete(response);
 	  break;
 	}
 	if (response)
@@ -492,10 +1151,8 @@ gboolean handle_cups_queues(gpointer unu
 	if (cupsLastError() > IPP_OK_CONFLICT) {
 	  debug_printf("cups-browsed: Unable to remove CUPS queue!\n");
 	  p->timeout = current_time + TIMEOUT_RETRY;
-	  httpClose(http);
 	  break;
 	}
-	httpClose(http);
       }
 
       /* CUPS queue removed, remove the list entry */
@@ -506,7 +1163,22 @@ gboolean handle_cups_queues(gpointer unu
       if (p->service_name) free (p->service_name);
       if (p->type) free (p->type);
       if (p->domain) free (p->domain);
+      if (p->ppd) free (p->ppd);
+      if (p->model) free (p->model);
+      if (p->ifscript) free (p->ifscript);
       free(p);
+      p = NULL;
+
+      /* If auto shutdown is active and all printers we have set up got removed
+	 again, schedule the shutdown in autoshutdown_timeout seconds */
+      if (autoshutdown && !autoshutdown_exec_id &&
+	  cupsArrayCount(remote_printers) == 0) {
+	debug_printf ("cups-browsed: No printers there any more to make available, shutting down in %d sec...\n", autoshutdown_timeout);
+	autoshutdown_exec_id =
+	  g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
+				 NULL);
+      }
+
       break;
 
     /* Bonjour has reported a new remote printer, create a CUPS queue for it,
@@ -530,8 +1202,7 @@ gboolean handle_cups_queues(gpointer unu
 		   p->name);
 
       /* Create a new CUPS queue or modify the existing queue */
-      if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
-				     cupsEncryption())) == NULL) {
+      if ((http = http_connect_local ()) == NULL) {
 	debug_printf("cups-browsed: Unable to connect to CUPS!\n");
 	p->timeout = current_time + TIMEOUT_RETRY;
 	break;
@@ -556,7 +1227,7 @@ gboolean handle_cups_queues(gpointer unu
       num_options = cupsAddOption("device-uri", p->uri,
 				  num_options, &options);
       /* Option cups-browsed=true, marking that we have created this queue */
-      num_options = cupsAddOption("cups-browsed-default", "true",
+      num_options = cupsAddOption(CUPS_BROWSED_MARK "-default", "true",
 				  num_options, &options);
       /* Do not share a queue which serves only to point to a remote printer */
       num_options = cupsAddOption("printer-is-shared", "false",
@@ -568,16 +1239,34 @@ gboolean handle_cups_queues(gpointer unu
       num_options = cupsAddOption("printer-location", p->host,
 				  num_options, &options);
       cupsEncodeOptions2(request, num_options, options, IPP_TAG_PRINTER);
+      /* PPD from system's CUPS installation */
+      if (p->model) {
+	debug_printf("cups-browsed: Non-raw queue %s with system PPD: %s\n", p->name, p->model);
+	p->ppd = cupsGetServerPPD(http, p->model);
+      }
       /* Do it */
-      ippDelete(cupsDoRequest(http, request, "/admin/"));
+      if (p->ppd) {
+	debug_printf("cups-browsed: Non-raw queue %s with PPD file: %s\n", p->name, p->ppd);
+	ippDelete(cupsDoFileRequest(http, request, "/admin/", p->ppd));
+	if (p->model) {
+	  unlink(p->ppd);
+	  free(p->ppd);
+	  p->ppd = NULL;
+	}
+      } else if (p->ifscript) {
+	debug_printf("cups-browsed: Non-raw queue %s with interface script: %s\n", p->name, p->ifscript);
+	ippDelete(cupsDoFileRequest(http, request, "/admin/", p->ifscript));
+	unlink(p->ifscript);
+	free(p->ifscript);
+	p->ifscript = NULL;
+      } else
+	ippDelete(cupsDoRequest(http, request, "/admin/"));
       cupsFreeOptions(num_options, options);
       if (cupsLastError() > IPP_OK_CONFLICT) {
 	debug_printf("cups-browsed: Unable to create CUPS queue!\n");
 	p->timeout = current_time + TIMEOUT_RETRY;
-	httpClose(http);
 	break;
       }
-      httpClose(http);
 
       if (p->status == STATUS_BROWSE_PACKET_RECEIVED) {
 	p->status = STATUS_DISAPPEARED;
@@ -637,40 +1326,123 @@ recheck_timer (void)
   }
 }
 
-void generate_local_queue(const char *host,
-			  uint16_t port,
-			  char *resource,
-			  const char *name,
-			  const char *type,
-			  const char *domain) {
-  char *remote_queue = NULL, *remote_host = NULL;
+static remote_printer_t *
+generate_local_queue(const char *host,
+		     uint16_t port,
+		     char *resource,
+		     const char *name,
+		     const char *type,
+		     const char *domain,
+		     void *txt) {
+
+  char uri[HTTP_MAX_URI];
+  char *remote_queue = NULL, *remote_host = NULL, *pdl = NULL;
+#ifdef HAVE_AVAHI
+  char *fields[] = { "product", "usb_MDL", "ty", NULL }, **f;
+  AvahiStringList *entry = NULL;
+  char *key = NULL, *value = NULL;
+#endif /* HAVE_AVAHI */
   remote_printer_t *p;
+  local_printer_t *local_printer;
   char *backup_queue_name = NULL, *local_queue_name = NULL;
-  cups_dest_t *dests = NULL, *dest = NULL;
-  int i, num_dests;
+  int is_cups_queue;
   size_t hl = 0;
-  const char *val = NULL;
+  gboolean create = TRUE;
+  
 
-  /* This is a remote CUPS queue, find queue name and host name */
-  if (strncasecmp(resource, "printers/", 9)) {
-    debug_printf("cups-browsed: resource does not begin 'printers/'\n");
-    return;
-  }
+  is_cups_queue = 0;
+  memset(uri, 0, sizeof(uri));
 
-  remote_queue = remove_bad_chars(resource + 9, 0);
+  /* Determine the device URI of the remote printer */
+  httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri) - 1,
+		   (strcasestr(type, "_ipps") ? "ipps" : "ipp"), NULL,
+		   host, port, "/%s", resource);
   /* Find the remote host name.
    * Used in constructing backup_queue_name, so need to sanitize.
    * strdup() is called inside remove_bad_chars() and result is free()-able.
    */
   remote_host = remove_bad_chars(host, 1);
   hl = strlen(remote_host);
-  if (hl > 6 && !strcmp(remote_host + strlen(remote_host) - 6, ".local"))
-    remote_host[strlen(remote_host) - 6] = '\0';
-  if (hl > 7 && !strcmp(remote_host + strlen(remote_host) - 7, ".local."))
-    remote_host[strlen(remote_host) - 7] = '\0';
-  debug_printf("cups-browsed: Found CUPS queue: %s on host %s.\n",
-	       remote_queue, remote_host);
-
+  if (hl > 6 && !strcasecmp(remote_host + hl - 6, ".local"))
+    remote_host[hl - 6] = '\0';
+  if (hl > 7 && !strcasecmp(remote_host + hl - 7, ".local."))
+    remote_host[hl - 7] = '\0';
+
+  /* Check by the resource whether the discovered printer is a CUPS queue */
+  if (!strncasecmp(resource, "printers/", 9)) {
+    /* This is a remote CUPS queue, use the remote queue name for the
+       local queue */
+    is_cups_queue = 1;
+    /* Not directly used in script generation input later, but taken from
+       packet, so better safe than sorry. (consider second loop with
+       backup_queue_name) */
+    remote_queue = remove_bad_chars(resource + 9, 0);
+    debug_printf("cups-browsed: Found CUPS queue: %s on host %s.\n",
+		 remote_queue, remote_host);
+#ifdef HAVE_AVAHI
+    /* If the remote queue has a PPD file, the "product" field of the
+       TXT record is populated. If it has no PPD file the remote queue
+       is a raw queue and so we do not know enough about the printer
+       behind it for auto-creating a local queue pointing to it. */
+    int raw_queue = 0;
+    if (txt) {
+      entry = avahi_string_list_find((AvahiStringList *)txt, "product");
+      if (entry) {
+	avahi_string_list_get_pair(entry, &key, &value, NULL);
+	if (!key || !value || strcasecmp(key, "product") || value[0] != '(' ||
+	    value[strlen(value) - 1] != ')') {
+	  raw_queue = 1;
+	}
+      } else
+	raw_queue = 1;
+    } else if (domain && domain[0] != '\0')
+      raw_queue = 1;
+    if (raw_queue) {
+      /* The remote CUPS queue is raw, ignore it */
+      debug_printf("cups-browsed: Remote Bonjour-advertised CUPS queue %s on host %s is raw, ignored.\n",
+		   remote_queue, remote_host);
+      free (remote_host);
+      return NULL;
+    }
+#endif /* HAVE_AVAHI */
+  } else if (!strncasecmp(resource, "classes/", 8)) {
+    /* This is a remote CUPS queue, use the remote queue name for the
+       local queue */
+    is_cups_queue = 1;
+    /* Not directly used in script generation input later, but taken from
+       packet, so better safe than sorry. (consider second loop with
+       backup_queue_name) */
+    remote_queue = remove_bad_chars(resource + 8, 0);
+    debug_printf("cups-browsed: Found CUPS queue: %s on host %s.\n",
+		 remote_queue, remote_host);
+  } else {
+    /* This is an IPP-based network printer */
+    is_cups_queue = 0;
+    /* Determine the queue name by the model */
+    remote_queue = strdup("printer");
+#ifdef HAVE_AVAHI
+    if (txt) {
+      for (f = fields; *f; f ++) {
+	entry = avahi_string_list_find((AvahiStringList *)txt, *f);
+	if (entry) {
+	  avahi_string_list_get_pair(entry, &key, &value, NULL);
+	  if (key && value && !strcasecmp(key, *f) && strlen(value) >= 3) {
+	    remote_queue = remove_bad_chars(value, 0);
+	    break;
+	  }
+	}
+      }
+      /* Find out which PDLs the printer understands */
+      entry = avahi_string_list_find((AvahiStringList *)txt, "pdl");
+      if (entry) {
+	avahi_string_list_get_pair(entry, &key, &value, NULL);
+	if (key && value && !strcasecmp(key, "pdl") && strlen(value) >= 3) {
+	  pdl = remove_bad_chars(value, 1);
+	}
+      }
+    }
+#endif /* HAVE_AVAHI */
+  }
   /* Check if there exists already a CUPS queue with the
      requested name Try name@host in such a case and if
      this is also taken, ignore the printer */
@@ -683,136 +1455,150 @@ void generate_local_queue(const char *ho
   sprintf(backup_queue_name, "%s@%s", remote_queue, remote_host);
 
   /* Get available CUPS queues */
-  num_dests = cupsGetDests(&dests);
+  update_local_printers ();
 
   local_queue_name = remote_queue;
-  if (num_dests > 0) {
+
+  /* Is there a local queue with the same URI as the remote queue? */
+  if (g_hash_table_find (local_printers,
+			 local_printer_has_uri,
+			 uri))
+    create = FALSE;
+
+  if (create) {
     /* Is there a local queue with the name of the remote queue? */
-    for (i = num_dests, dest = dests; i > 0; i --, dest ++)
+    local_printer = g_hash_table_lookup (local_printers,
+					 local_queue_name);
       /* Only consider CUPS queues not created by us */
-      if ((((val =
-	     cupsGetOption(CUPS_BROWSED_MARK, dest->num_options,
-			   dest->options)) == NULL) ||
-	   (strcasecmp(val, "yes") != 0 &&
-	    strcasecmp(val, "on") != 0 &&
-	    strcasecmp(val, "true") != 0)) &&
-	  !strcmp(local_queue_name, dest->name))
-	break;
-    if (i > 0) {
+    if (local_printer && !local_printer->cups_browsed_controlled) {
       /* Found local queue with same name as remote queue */
       /* Is there a local queue with the name <queue>@<host>? */
       local_queue_name = backup_queue_name;
       debug_printf("cups-browsed: %s already taken, using fallback name: %s\n",
 		   remote_queue, local_queue_name);
-      for (i = num_dests, dest = dests; i > 0; i --, dest ++)
-	/* Only consider CUPS queues not created by us */
-	if ((((val =
-	       cupsGetOption(CUPS_BROWSED_MARK, dest->num_options,
-			     dest->options)) == NULL) ||
-	     (strcasecmp(val, "yes") != 0 &&
-	      strcasecmp(val, "on") != 0 &&
-	      strcasecmp(val, "true") != 0)) &&
-	    !strcmp(local_queue_name, dest->name))
-	  break;
-      if (i > 0) {
+      local_printer = g_hash_table_lookup (local_printers,
+					   local_queue_name);
+      if (local_printer && !local_printer->cups_browsed_controlled) {
 	/* Found also a local queue with name <queue>@<host>, so
 	   ignore this remote printer */
 	debug_printf("cups-browsed: %s also taken, printer ignored.\n",
 		     local_queue_name);
 	free (backup_queue_name);
 	free (remote_host);
+	free (pdl);
 	free (remote_queue);
-	cupsFreeDests(num_dests, dests);
-	return;
+	return NULL;
       }
     }
-    cupsFreeDests(num_dests, dests);
   }
 
   /* Check if we have already created a queue for the discovered
      printer */
   for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
        p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
-    if (!strcmp(p->name, local_queue_name) &&
+    if (!strcasecmp(p->name, local_queue_name) &&
 	(p->host[0] == '\0' ||
-	 !strcmp(p->host, remote_host)))
+	 p->status == STATUS_UNCONFIRMED ||
+	 p->status == STATUS_DISAPPEARED ||
+	 !strcasecmp(p->host, remote_host)))
       break;
 
+  if (!create) {
+    free (remote_host);
+    free (backup_queue_name);
+    free (pdl);
+    free (remote_queue);
+    if (p) {
+      return p;
+    } else {
+      /* Found a local queue with the same URI as our discovered printer
+	 would get, so ignore this remote printer */
+      debug_printf("cups-browsed: Printer with URI %s already exists, printer ignored.\n",
+		   uri);
+      return NULL;
+    }
+  }
+
   if (p) {
     /* We have already created a local queue, check whether the
-       discovered service allows us to upgrade the queue to IPPS */
-    if (strcasestr(type, "_ipps") &&
-	!strncmp(p->uri, "ipp:", 4)) {
-
-      /* Schedule local queue for upgrade to ipps: */
-      if ((p->uri = realloc(p->uri, strlen(p->uri) + 2)) == NULL){
-	debug_printf("cups-browsed: ERROR: Unable to allocate memory.\n");
-	exit(1);
-      }
-      memmove((void *)(p->uri + 4), (const void *)(p->uri + 3),
-	      strlen(p->uri) - 2);
-      p->uri[3] = 's';
+       discovered service allows us to upgrade the queue to IPPS
+       or whether the URI part after ipp(s):// has changed */
+    if ((strcasestr(type, "_ipps") &&
+	 !strncasecmp(p->uri, "ipp:", 4)) ||
+	strcasecmp(strchr(p->uri, ':'), strchr(uri, ':'))) {
+
+      /* Schedule local queue for upgrade to ipps: or for URI change */
+      if (strcasestr(type, "_ipps") &&
+	  !strncasecmp(p->uri, "ipp:", 4))
+	debug_printf("cups-browsed: Upgrading printer %s (Host: %s) to IPPS. New URI: %s\n",
+		     p->name, remote_host, uri);
+      if (strcasecmp(strchr(p->uri, ':'), strchr(uri, ':')))
+	debug_printf("cups-browsed: Changing URI of printer %s (Host: %s) to %s.\n",
+		     p->name, remote_host, uri);
+      free(p->uri);
+      free(p->host);
+      free(p->service_name);
+      free(p->type);
+      free(p->domain);
+      p->uri = strdup(uri);
       p->status = STATUS_TO_BE_CREATED;
       p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
       p->host = strdup(remote_host);
       p->service_name = strdup(name);
       p->type = strdup(type);
       p->domain = strdup(domain);
-      debug_printf("cups-browsed: Upgrading printer %s (Host: %s) to IPPS. New URI: %s\n",
-		   p->name, p->host, p->uri);
 
     } else {
 
       /* Nothing to do, mark queue entry as confirmed if the entry
 	 is unconfirmed */
-      debug_printf("cups-browsed: Entry for %s (Host: %s, URI: %s) already exists.\n",
-		   p->name, p->host, p->uri);
-      if (p->status == STATUS_UNCONFIRMED) {
+      debug_printf("cups-browsed: Entry for %s (URI: %s) already exists.\n",
+		   p->name, p->uri);
+      if (p->status == STATUS_UNCONFIRMED ||
+	  p->status == STATUS_DISAPPEARED) {
 	p->status = STATUS_CONFIRMED;
 	p->timeout = (time_t) -1;
-	debug_printf("cups-browsed: Marking entry for %s (Host: %s, URI: %s) as confirmed.\n",
-		     p->name, p->host, p->uri);
+	debug_printf("cups-browsed: Marking entry for %s (URI: %s) as confirmed.\n",
+		     p->name, p->uri);
       }
 
     }
-    if (p->host[0] == '\0')
+    if (p->host[0] == '\0') {
+      free (p->host);
       p->host = strdup(remote_host);
-    if (p->service_name[0] == '\0' && name)
+    }
+    if (p->service_name[0] == '\0' && name) {
+      free (p->service_name);
       p->service_name = strdup(name);
-    if (p->type[0] == '\0' && type)
+    }
+    if (p->type[0] == '\0' && type) {
+      free (p->type);
       p->type = strdup(type);
-    if (p->domain[0] == '\0' && domain)
+    }
+    if (p->domain[0] == '\0' && domain) {
+      free (p->domain);
       p->domain = strdup(domain);
-
+    }
   } else {
 
     /* We need to create a local queue pointing to the
        discovered printer */
-
-    /* Device URI: ipp(s)://<remote host>:631/printers/<remote queue> */
-    char *uri = malloc(strlen(host) +
-		       strlen(remote_queue) + 34);
-    if (uri == NULL) {
-      debug_printf("cups-browsed: ERROR: Unable to allocate memory.\n");
-      exit(1);
-    }
-    sprintf(uri, "ipp%s://%s:%u/printers/%s",
-	    (strcasestr(type, "_ipps") ? "s" : ""), host,
-	    port, remote_queue);
-
     p = create_local_queue (local_queue_name, uri, remote_host,
-			    name ? name : "", type, domain);
-    free (uri);
+			    name ? name : "", type, domain, pdl, remote_queue,
+			    is_cups_queue);
   }
 
   free (backup_queue_name);
   free (remote_host);
+  free (pdl);
   free (remote_queue);
 
   if (p)
     debug_printf("cups-browsed: Bonjour IDs: Service name: \"%s\", "
 		 "Service type: \"%s\", Domain: \"%s\"\n",
 		 p->service_name, p->type, p->domain);
+
+  return p;
 }
 
 #ifdef HAVE_AVAHI
@@ -852,30 +1638,47 @@ static void resolve_callback(
     debug_printf("cups-browsed: Avahi Resolver: Service '%s' of type '%s' in domain '%s'.\n",
 		 name, type, domain);
 
-    /* Check if we have a remote CUPS queue, other remote printers are not
-       handled by us */
     rp_entry = avahi_string_list_find(txt, "rp");
-    adminurl_entry = avahi_string_list_find(txt, "adminurl");
-    if (rp_entry && adminurl_entry) {
+    if (rp_entry)
       avahi_string_list_get_pair(rp_entry, &rp_key, &rp_value, NULL);
+    else {
+      rp_key = strdup("rp");
+      rp_value = strdup("");
+    }
+    adminurl_entry = avahi_string_list_find(txt, "adminurl");
+    if (adminurl_entry)
       avahi_string_list_get_pair(adminurl_entry, &adminurl_key,
 				 &adminurl_value, NULL);
+    else {
+      adminurl_key = strdup("adminurl");
+      if ((adminurl_value = malloc(strlen(host_name) + 8)) != NULL)
+	sprintf(adminurl_value, "http://%s", host_name);
+      else
+	adminurl_value = strdup("");
+    }
 
-      /* Check by "rp" and "adminurl" TXT record fields whether
-	 the discovered printer is a CUPS queue */
-      if (rp_key && rp_value && adminurl_key && adminurl_value &&
-	  !strcmp(rp_key, "rp") && !strncmp(rp_value, "printers/", 9) &&
-	  !strcmp(adminurl_key, "adminurl") &&
-	  !strcmp(adminurl_value + strlen(adminurl_value) -
-		  strlen(rp_value), rp_value)) {
-	generate_local_queue(host_name, port, rp_value, name, type, domain);
-      }
+    if (rp_key && rp_value && adminurl_key && adminurl_value &&
+	!strcasecmp(rp_key, "rp") && !strcasecmp(adminurl_key, "adminurl")) {
+      /* Check remote printer type and create appropriate local queue to
+         point to it */
+      generate_local_queue(host_name, port, rp_value, name, type, domain, txt);
+    }
+
+    /* Clean up */
 
-      /* Clean up */
+    if (rp_entry) {
       avahi_free(rp_key);
       avahi_free(rp_value);
+    } else {
+      free(rp_key);
+      free(rp_value);
+    }
+    if (adminurl_entry) {
       avahi_free(adminurl_key);
       avahi_free(adminurl_value);
+    } else {
+      free(adminurl_key);
+      free(adminurl_value);
     }
     break;
   }
@@ -905,7 +1708,7 @@ static void browse_callback(
 
   switch (event) {
 
-  /* Avah browser error */
+  /* Avahi browser error */
   case AVAHI_BROWSER_FAILURE:
 
     debug_printf("cups-browsed: Avahi Browser: ERROR: %s\n",
@@ -947,9 +1750,9 @@ static void browse_callback(
     /* Check whether we have listed this printer */
     for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
 	 p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
-      if (!strcmp(p->service_name, name) &&
-	  !strcmp(p->type, type) &&
-	  !strcmp(p->domain, domain))
+      if (!strcasecmp(p->service_name, name) &&
+	  !strcasecmp(p->type, type) &&
+	  !strcasecmp(p->domain, domain))
 	break;
     if (p) {
       /* Check whether this queue has a duplicate from another server */
@@ -958,8 +1761,8 @@ static void browse_callback(
 	for (q = (remote_printer_t *)cupsArrayFirst(remote_printers);
 	     q;
 	     q = (remote_printer_t *)cupsArrayNext(remote_printers))
-	  if (!strcmp(q->name, p->name) &&
-	      strcmp(q->host, p->host) &&
+	  if (!strcasecmp(q->name, p->name) &&
+	      strcasecmp(q->host, p->host) &&
 	      q->duplicate)
 	    break;
       }
@@ -970,16 +1773,22 @@ static void browse_callback(
 	free (p->service_name);
 	free (p->type);
 	free (p->domain);
+	if (p->ppd) free (p->ppd);
+	if (p->model) free (p->model);
+	if (p->ifscript) free (p->ifscript);
 	/* Replace the data with the data of the duplicate printer */
 	p->uri = strdup(q->uri);
 	p->host = strdup(q->host);
 	p->service_name = strdup(q->service_name);
 	p->type = strdup(q->type);
 	p->domain = strdup(q->domain);
+	if (q->ppd) p->ppd = strdup(q->ppd);
+	if (q->model) p->model = strdup(q->model);
+	if (q->ifscript) p->ifscript = strdup(q->ifscript);
 	/* Schedule this printer for updating the CUPS queue */
 	p->status = STATUS_TO_BE_CREATED;
 	p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
-	/* Schedule the remote printer for removal */
+	/* Schedule the duplicate printer entry for removal */
 	q->status = STATUS_DISAPPEARED;
 	q->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
 
@@ -1015,20 +1824,171 @@ static void browse_callback(
 
 }
 
+void avahi_browser_shutdown() {
+  remote_printer_t *p;
+
+  /* Remove all queues which we have set up based on Bonjour discovery*/
+  for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
+       p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
+    if (p->type && p->type[0]) {
+      p->status = STATUS_DISAPPEARED;
+      p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
+    }
+  }
+  handle_cups_queues(NULL);
+
+  /* Free the data structures for Bonjour browsing */
+  if (sb1) {
+    avahi_service_browser_free(sb1);
+    sb1 = NULL;
+  }
+  if (sb2) {
+    avahi_service_browser_free(sb2);
+    sb2 = NULL;
+  }
+
+  /* Switch on auto shutdown mode */
+  if (autoshutdown_avahi) {
+    autoshutdown = 1;
+    debug_printf("cups-browsed: Avahi server disappeared, switching to auto shutdown mode ...\n");
+    /* If there are no printers schedule the shutdown in autoshutdown_timeout
+       seconds */
+    if (!autoshutdown_exec_id &&
+	cupsArrayCount(remote_printers) == 0) {
+      debug_printf ("cups-browsed: We entered auto shutdown mode and no printers are there to make available, shutting down in %d sec...\n", autoshutdown_timeout);
+      autoshutdown_exec_id =
+	g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
+			       NULL);
+    }
+  }
+}
+
+void avahi_shutdown() {
+  avahi_browser_shutdown();
+  if (client) {
+    avahi_client_free(client);
+    client = NULL;
+  }
+  if (glib_poll) {
+    avahi_glib_poll_free(glib_poll);
+    glib_poll = NULL;
+  }
+}
+
 static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) {
+  int error;
+
   assert(c);
 
   /* Called whenever the client or server state changes */
+  switch (state) {
 
-  if (state == AVAHI_CLIENT_FAILURE) {
-    debug_printf("cups-browsed: ERROR: Avahi server connection failure: %s\n",
-		 avahi_strerror(avahi_client_errno(c)));
-    g_main_loop_quit(gmainloop);
+  /* avahi-daemon available */
+  case AVAHI_CLIENT_S_REGISTERING:
+  case AVAHI_CLIENT_S_RUNNING:
+  case AVAHI_CLIENT_S_COLLISION:
+
+    debug_printf("cups-browsed: Avahi server connection got available, setting up service browsers.\n");
+
+    /* Create the service browsers */
+    if (!sb1)
+      if (!(sb1 =
+	    avahi_service_browser_new(c, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+				      "_ipp._tcp", NULL, 0, browse_callback,
+				      c))) {
+	debug_printf("cups-browsed: ERROR: Failed to create service browser for IPP: %s\n",
+		     avahi_strerror(avahi_client_errno(c)));
+      }
+    if (!sb2)
+      if (!(sb2 =
+	    avahi_service_browser_new(c, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+				      "_ipps._tcp", NULL, 0, browse_callback,
+				      c))) {
+	debug_printf("cups-browsed: ERROR: Failed to create service browser for IPPS: %s\n",
+		     avahi_strerror(avahi_client_errno(c)));
+      }
+
+    /* switch off auto shutdown mode */
+    if (autoshutdown_avahi) {
+      autoshutdown = 0;
+      debug_printf("cups-browsed: Avahi server available, switching to permanent mode ...\n");
+      /* If there is still an active auto shutdown timer, kill it */
+      if (autoshutdown_exec_id > 0) {
+	debug_printf ("cups-browsed: We have left auto shutdown mode, killing auto shutdown timer.\n");
+	g_source_destroy(g_main_context_find_source_by_id(NULL,
+							  autoshutdown_exec_id));
+	autoshutdown_exec_id = 0;
+      }
+    }
+
+    break;
+
+  /* Avahi client error */
+  case AVAHI_CLIENT_FAILURE:
+
+    if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) {
+      debug_printf("cups-browsed: Avahi server disappeared, shutting down service browsers, removing Bonjour-discovered print queues.\n");
+      avahi_browser_shutdown();
+      /* Renewing client */
+      avahi_client_free(client);
+      client = avahi_client_new(avahi_glib_poll_get(glib_poll),
+				AVAHI_CLIENT_NO_FAIL,
+				client_callback, NULL, &error);
+      if (!client) {
+	debug_printf("cups-browsed: ERROR: Failed to create client: %s\n",
+		     avahi_strerror(error));
+	BrowseRemoteProtocols &= ~BROWSE_DNSSD;
+	avahi_shutdown();
+      }
+    } else {
+      debug_printf("cups-browsed: ERROR: Avahi server connection failure: %s\n",
+		   avahi_strerror(avahi_client_errno(c)));
+      g_main_loop_quit(gmainloop);
+    }
+    break;
+
+  default:
+    break;
   }
+}
+
+void avahi_init() {
+  int error;
+
+  if (BrowseRemoteProtocols & BROWSE_DNSSD) {
+    /* Allocate main loop object */
+    if (!glib_poll)
+      if (!(glib_poll = avahi_glib_poll_new(NULL, G_PRIORITY_DEFAULT))) {
+	debug_printf("cups-browsed: ERROR: Failed to create glib poll object.\n");
+	goto avahi_init_fail;
+      }
+
+    /* Allocate a new client */
+    if (!client)
+      client = avahi_client_new(avahi_glib_poll_get(glib_poll),
+				AVAHI_CLIENT_NO_FAIL,
+				client_callback, NULL, &error);
 
+    /* Check wether creating the client object succeeded */
+    if (!client) {
+      debug_printf("cups-browsed: ERROR: Failed to create client: %s\n",
+		   avahi_strerror(error));
+      goto avahi_init_fail;
+    }
+
+    return;
+
+  avahi_init_fail:
+    BrowseRemoteProtocols &= ~BROWSE_DNSSD;
+    avahi_shutdown();
+  }
 }
 #endif /* HAVE_AVAHI */
 
+/*
+ * A CUPS printer has been discovered via CUPS Browsing
+ * or with BrowsePoll
+ */
 void
 found_cups_printer (const char *remote_host, const char *uri,
 		    const char *info)
@@ -1041,6 +2001,7 @@ found_cups_printer (const char *remote_h
   netif_t *iface;
   char local_resource[HTTP_MAX_URI];
   char *c;
+  remote_printer_t *printer;
 
   memset(scheme, 0, sizeof(scheme));
   memset(username, 0, sizeof(username));
@@ -1059,7 +2020,7 @@ found_cups_printer (const char *remote_h
   for (iface = cupsArrayFirst (netifs);
        iface;
        iface = cupsArrayNext (netifs))
-    if (!strcmp (host, iface->address))
+    if (!strcasecmp (host, iface->address))
       break;
   if (iface) {
     debug_printf("cups-browsed: ignoring own broadcast on %s\n",
@@ -1067,7 +2028,8 @@ found_cups_printer (const char *remote_h
     return;
   }
 
-  if (strncmp (resource, "/printers/", 10)) {
+  if (strncasecmp (resource, "/printers/", 10) &&
+      strncasecmp (resource, "/classes/", 9)) {
     debug_printf("cups-browsed: don't understand URI: %s\n", uri);
     return;
   }
@@ -1081,15 +2043,26 @@ found_cups_printer (const char *remote_h
   debug_printf("cups-browsed: browsed queue name is %s\n",
 	       local_resource + 9);
 
-  generate_local_queue(host, port, local_resource, info ? info : "", "", "");
+  printer = generate_local_queue(host, port, local_resource,
+				 info ? info : "",
+				 "", "", NULL);
+
+  if (printer) {
+    if (printer->status == STATUS_TO_BE_CREATED)
+      printer->status = STATUS_BROWSE_PACKET_RECEIVED;
+    else {
+      printer->status = STATUS_DISAPPEARED;
+      printer->timeout = time(NULL) + BrowseTimeout;
+    }
+  }
 }
 
 static gboolean
 allowed (struct sockaddr *srcaddr)
 {
   allow_t *allow;
-  if (cupsArrayCount(browseallow) == 0) {
-    /* No "BrowseAllow" line, allow all servers */
+  if (browseallow_all || cupsArrayCount(browseallow) == 0) {
+    /* "BrowseAllow All", or no "BrowseAllow" line, so allow all servers */
     return TRUE;
   }
   for (allow = cupsArrayFirst (browseallow);
@@ -1237,7 +2210,9 @@ process_browse_data (GIOChannel *source,
   if (c >= end)
     return TRUE;
 
-  found_cups_printer (remote_host, uri, info);
+  if (!(type & CUPS_PRINTER_DELETE))
+    found_cups_printer (remote_host, uri, info);
+
   recheck_timer ();
 
   /* Don't remove this I/O source */
@@ -1326,12 +2301,10 @@ update_netifs (void)
   freeifaddrs (ifaddr);
 }
 
-void
-broadcast_browse_packets (int type, int state,
-			  const char *local_uri, const char *location,
-			  const char *info, const char *make_model,
-			  const char *browse_options)
+static void
+broadcast_browse_packets (gpointer data, gpointer user_data)
 {
+  browse_data_t *bdata = data;
   netif_t *browse;
   char packet[2048];
   char uri[HTTP_MAX_URI];
@@ -1345,262 +2318,102 @@ broadcast_browse_packets (int type, int
        browse != NULL;
        browse = (netif_t *)cupsArrayNext (netifs)) {
     /* Replace 'localhost' with our IP address on this interface */
-    httpSeparateURI(HTTP_URI_CODING_ALL, local_uri,
+    httpSeparateURI(HTTP_URI_CODING_ALL, bdata->uri,
 		    scheme, sizeof(scheme),
 		    username, sizeof(username),
 		    host, sizeof(host),
 		    &port,
 		    resource, sizeof(resource));
-    httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof (uri),
-		    scheme, username, browse->address, port, resource);
-
-    if (snprintf (packet, sizeof (packet),
-		  "%x "     /* type */
-		  "%x "     /* state */
-		  "%s "     /* uri */
-		  "\"%s\" " /* location */
-		  "\"%s\" " /* info */
-		  "\"%s\" " /* make-and-model */
-		  "lease-duration=%d" /* BrowseTimeout */
-		  "%s%s" /* other browse options */
-		  "\n",
-		  type, state, uri, location,
-		  info, make_model,
-		  BrowseTimeout,
-		  browse_options ? " " : "",
-		  browse_options ? browse_options : "") >= sizeof (packet)) {
-      debug_printf ("cups-browsed: oversize packet not sent\n");
-      continue;
-    }
-
-    debug_printf("cups-browsed: packet to send:\n%s", packet);
-
-    int err = sendto (browsesocket, packet,
-		      strlen (packet), 0,
-		      &browse->broadcast.addr,
-		      httpAddrLength (&browse->broadcast));
-    if (err)
-      debug_printf("cupsd-browsed: sendto returned %d: %s\n",
-		   err, strerror (errno));
-  }
-}
-
-gboolean
-send_browse_data (gpointer data)
-{
-  static const char * const rattrs[] = { "printer-type",
-					 "printer-state",
-					 "printer-uri-supported",
-					 "printer-info",
-					 "printer-location",
-					 "printer-make-and-model",
-					 "auth-info-required",
-					 "printer-uuid",
-					 "job-template" };
-  ipp_t *request, *response = NULL;
-  ipp_attribute_t *attr;
-  http_t *conn = NULL;
-
-  update_netifs ();
-  res_init ();
-  conn = httpConnectEncrypt ("localhost", BrowsePort,
-			     HTTP_ENCRYPT_IF_REQUESTED);
-
-  if (conn == NULL) {
-    debug_printf("cups-browsed: browse send failed to connect to localhost\n");
-    goto fail;
-  }
-
-  request = ippNewRequest(CUPS_GET_PRINTERS);
-  ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
-		 "requested-attributes", sizeof (rattrs) / sizeof (rattrs[0]),
-		 NULL, rattrs);
-  ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
-		"requesting-user-name", NULL, cupsUser ());
-
-  response = cupsDoRequest (conn, request, "/");
-  if (cupsLastError() > IPP_OK_CONFLICT) {
-    debug_printf("cups-browsed: browse send failed for localhost: %s\n",
-		 cupsLastErrorString ());
-    goto fail;
-  }
-
-  for (attr = ippFirstAttribute(response); attr;
-       attr = ippNextAttribute(response)) {
-    int type = -1, state = -1;
-    const char *uri = NULL;
-    gchar *location = NULL;
-    gchar *info = NULL;
-    gchar *make_model = NULL;
-    GString *browse_options = g_string_new ("");
-
-    /* Skip any non-printer attributes */
-    while (attr && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
-      attr = ippNextAttribute(response);
-
-    if (!attr)
-      break;
-
-    while (attr && ippGetGroupTag(attr) == IPP_TAG_PRINTER) {
-      const char *attrname = ippGetName(attr);
-      int value_tag = ippGetValueTag(attr);
-
-      if (!strcmp(attrname, "printer-type") &&
-	  value_tag == IPP_TAG_ENUM) {
-	type = ippGetInteger(attr, 0);
-	if (type & CUPS_PRINTER_NOT_SHARED) {
-	  /* Skip CUPS queues not marked as shared */
-	  state = -1;
-	  type = -1;
-	  break;
-	}
-      } else if (!strcmp(attrname, "printer-state") &&
-	       value_tag == IPP_TAG_ENUM)
-	state = ippGetInteger(attr, 0);
-      else if (!strcmp(attrname, "printer-uri-supported") &&
-	       value_tag == IPP_TAG_URI)
-	uri = ippGetString(attr, 0, NULL);
-      else if (!strcmp(attrname, "printer-location") &&
-	       value_tag == IPP_TAG_TEXT) {
-	/* Remove quotes */
-	gchar **tokens = g_strsplit (ippGetString(attr, 0, NULL), "\"", -1);
-	location = g_strjoinv ("", tokens);
-	g_strfreev (tokens);
-      } else if (!strcmp(attrname, "printer-info") &&
-		 value_tag == IPP_TAG_TEXT) {
-	/* Remove quotes */
-	gchar **tokens = g_strsplit (ippGetString(attr, 0, NULL), "\"", -1);
-	info = g_strjoinv ("", tokens);
-	g_strfreev (tokens);
-      } else if (!strcmp(attrname, "printer-make-and-model") &&
-		 value_tag == IPP_TAG_TEXT) {
-	/* Remove quotes */
-	gchar **tokens = g_strsplit (ippGetString(attr, 0, NULL), "\"", -1);
-	make_model = g_strjoinv ("", tokens);
-	g_strfreev (tokens);
-      } else if (!strcmp(attrname, "auth-info-required") &&
-		 value_tag == IPP_TAG_KEYWORD) {
-	if (strcmp (ippGetString(attr, 0, NULL), "none"))
-	  g_string_append_printf (browse_options, "auth-info-required=%s ",
-				  ippGetString(attr, 0, NULL));
-      } else if (!strcmp(attrname, "printer-uuid") &&
-		 value_tag == IPP_TAG_URI)
-	g_string_append_printf (browse_options, "uuid=%s ",
-				ippGetString(attr, 0, NULL));
-      else if (!strcmp(attrname, "job-sheets-default") &&
-	       value_tag == IPP_TAG_NAME &&
-	       ippGetCount(attr) == 2)
-	g_string_append_printf (browse_options, "job-sheets=%s,%s ",
-				ippGetString(attr, 0, NULL),
-				ippGetString(attr, 1, NULL));
-      else if (strstr(attrname, "-default")) {
-	gchar *name = g_strdup (attrname);
-	gchar *value = NULL;
-	*strstr (name, "-default") = '\0';
-
-	switch (value_tag) {
-	  gchar **tokens;
-
-	case IPP_TAG_KEYWORD:
-	case IPP_TAG_STRING:
-	case IPP_TAG_NAME:
-	  /* Escape value */
-	  tokens = g_strsplit_set (ippGetString(attr, 0, NULL),
-				   " \"\'\\", -1);
-	  value = g_strjoinv ("\\", tokens);
-	  g_strfreev (tokens);
-	  break;
-
-	default:
-	  /* other values aren't needed? */
-	  debug_printf("cups-browsed: skipping %s (%d)\n", name, value_tag);
-	  break;
-	}
-
-	if (value) {
-	  g_string_append_printf (browse_options, "%s=%s ", name, value);
-	  g_free (value);
-	}
-
-	g_free (name);
-      }
-
-      attr = ippNextAttribute(response);
-    }
-
-    if (type != -1 && state != -1 && uri && location && info && make_model) {
-      gchar *browse_options_str = g_string_free (browse_options, FALSE);
-      browse_options = NULL;
-      g_strchomp (browse_options_str);
-
-      broadcast_browse_packets (type, state, uri, location,
-				info, make_model,
-				browse_options_str);
-
-      g_free (browse_options_str);
-    }
-
-    if (make_model)
-      g_free (make_model);
-
-    if (info)
-      g_free (info);
+    httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof (uri),
+		    scheme, username, browse->address, port, resource);
 
-    if (location)
-      g_free (location);
+    if (snprintf (packet, sizeof (packet),
+		  "%x "     /* type */
+		  "%x "     /* state */
+		  "%s "     /* uri */
+		  "\"%s\" " /* location */
+		  "\"%s\" " /* info */
+		  "\"%s\" " /* make-and-model */
+		  "lease-duration=%d" /* BrowseTimeout */
+		  "%s%s" /* other browse options */
+		  "\n",
+		  bdata->type,
+		  bdata->state,
+		  uri,
+		  bdata->location,
+		  bdata->info,
+		  bdata->make_model,
+		  BrowseTimeout,
+		  bdata->browse_options ? " " : "",
+		  bdata->browse_options ? bdata->browse_options : "")
+	>= sizeof (packet)) {
+      debug_printf ("cups-browsed: oversize packet not sent\n");
+      continue;
+    }
 
-    if (browse_options)
-      g_string_free (browse_options, TRUE);
+    debug_printf("cups-browsed: packet to send:\n%s", packet);
 
-    if (!attr)
-      break;
+    int err = sendto (browsesocket, packet,
+		      strlen (packet), 0,
+		      &browse->broadcast.addr,
+		      httpAddrLength (&browse->broadcast));
+    if (err == -1)
+      debug_printf("cupsd-browsed: sendto returned %d: %s\n",
+		   err, strerror (errno));
   }
+}
 
- fail:
-  if (response)
-    ippDelete(response);
-
-  if (conn)
-    httpClose (conn);
-
+gboolean
+send_browse_data (gpointer data)
+{
+  update_netifs ();
+  res_init ();
+  update_local_printers ();
+  g_list_foreach (browse_data, broadcast_browse_packets, NULL);
   g_timeout_add_seconds (BrowseInterval, send_browse_data, NULL);
 
   /* Stop this timeout handler, we called a new one */
   return FALSE;
 }
 
-gboolean
-browse_poll (gpointer data)
+static browsepoll_printer_t *
+new_browsepoll_printer (const char *uri_supported,
+			const char *info)
+{
+  browsepoll_printer_t *printer = g_malloc (sizeof (browsepoll_printer_t));
+  printer->uri_supported = g_strdup (uri_supported);
+  printer->info = g_strdup (info);
+  return printer;
+}
+
+static void
+browsepoll_printer_free (gpointer data)
+{
+  browsepoll_printer_t *printer = data;
+  free (printer->uri_supported);
+  free (printer->info);
+  free (printer);
+}
+
+static void
+browse_poll_get_printers (browsepoll_t *context, http_t *conn)
 {
-  static const char * const rattrs[] = { "printer-uri-supported" };
-  char *server = strdup (data);
+  static const char * const rattrs[] = { "printer-uri-supported",
+					 "printer-info"};
   ipp_t *request, *response = NULL;
   ipp_attribute_t *attr;
-  http_t *conn;
-  int port = BrowsePort;
-  char *colon;
-
-  debug_printf ("cups-browsed: browse polling %s\n", server);
-
-  colon = strchr (server, ':');
-  if (colon) {
-    char *endptr;
-    unsigned long n;
-    *colon++ = '\0';
-    n = strtoul (colon, &endptr, 10);
-    if (endptr != colon && n < INT_MAX)
-      port = (int) n;
-  }
-
-  res_init ();
-  conn = httpConnectEncrypt (server, port, HTTP_ENCRYPT_IF_REQUESTED);
+  GList *printers = NULL;
 
-  if (conn == NULL) {
-    debug_printf("cups-browsed: browse poll failed to connect to %s\n", server);
-    goto fail;
-  }
+  debug_printf ("cups-browsed [BrowsePoll %s:%d]: CUPS-Get-Printers\n",
+		context->server, context->port);
 
   request = ippNewRequest(CUPS_GET_PRINTERS);
+  if (context->major > 0) {
+    debug_printf("cups-browsed [BrowsePoll %s:%d]: setting IPP version %d.%d\n",
+		 context->server, context->port, context->major,
+		 context->minor);
+    ippSetVersion (request, context->major, context->minor);
+  }
 
   ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
 		 "requested-attributes", sizeof (rattrs) / sizeof (rattrs[0]),
@@ -1621,13 +2434,14 @@ browse_poll (gpointer data)
 
   response = cupsDoRequest(conn, request, "/");
   if (cupsLastError() > IPP_OK_CONFLICT) {
-    debug_printf("cups-browsed: browse poll failed for server %s: %s\n",
-		 server, cupsLastErrorString ());
+    debug_printf("cups-browsed [BrowsePoll %s:%d]: failed: %s\n",
+		 context->server, context->port, cupsLastErrorString ());
     goto fail;
   }
 
   for (attr = ippFirstAttribute(response); attr;
        attr = ippNextAttribute(response)) {
+    browsepoll_printer_t *printer;
     const char *uri, *info;
 
     while (attr && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
@@ -1639,35 +2453,287 @@ browse_poll (gpointer data)
     uri = NULL;
     info = NULL;
     while (attr && ippGetGroupTag(attr) == IPP_TAG_PRINTER) {
-      if (!strcmp (ippGetName(attr), "printer-uri-supported") &&
+
+      if (!strcasecmp (ippGetName(attr), "printer-uri-supported") &&
 	  ippGetValueTag(attr) == IPP_TAG_URI)
 	uri = ippGetString(attr, 0, NULL);
-      else if (!strcmp (ippGetName(attr), "printer-info") &&
+      else if (!strcasecmp (ippGetName(attr), "printer-info") &&
 	       ippGetValueTag(attr) == IPP_TAG_TEXT)
 	info = ippGetString(attr, 0, NULL);
 
       attr = ippNextAttribute(response);
     }
 
-    if (uri)
-      found_cups_printer (server, uri, info);
+    if (uri) {
+      found_cups_printer (context->server, uri, info);
+      printer = new_browsepoll_printer (uri, info);
+      printers = g_list_insert (printers, printer, 0);
+    }
 
     if (!attr)
       break;
   }
 
+  g_list_free_full (context->printers, browsepoll_printer_free);
+  context->printers = printers;
   recheck_timer ();
 
 fail:
   if (response)
     ippDelete(response);
+}
+
+static void
+browse_poll_create_subscription (browsepoll_t *context, http_t *conn)
+{
+  static const char * const events[] = { "printer-added",
+					 "printer-changed",
+					 "printer-config-changed",
+					 "printer-modified",
+					 "printer-deleted",
+					 "printer-state-changed" };
+  ipp_t *request, *response = NULL;
+  ipp_attribute_t *attr;
+
+  debug_printf ("cups-browsed [BrowsePoll %s:%d]: IPP-Create-Subscription\n",
+		context->server, context->port);
+
+  request = ippNewRequest(IPP_CREATE_PRINTER_SUBSCRIPTION);
+  if (context->major > 0) {
+    debug_printf("cups-browsed [BrowsePoll %s:%d]: setting IPP version %d.%d\n",
+		 context->server, context->port, context->major,
+		 context->minor);
+    ippSetVersion (request, context->major, context->minor);
+  }
+
+  ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
+		"printer-uri", NULL, "/");
+  ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
+		"notify-pull-method", NULL, "ippget");
+  ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_CHARSET,
+		"notify-charset", NULL, "utf-8");
+  ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_NAME,
+		"requesting-user-name", NULL, cupsUser ());
+  ippAddStrings (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
+		 "notify-events", sizeof (events) / sizeof (events[0]),
+		 NULL, events);
+  ippAddInteger (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
+		 "notify-time-interval", BrowseInterval);
+
+  response = cupsDoRequest (conn, request, "/");
+  if (!response || ippGetStatusCode (response) > IPP_OK_CONFLICT) {
+    debug_printf("cupsd-browsed [BrowsePoll %s:%d]: failed: %s\n",
+		 context->server, context->port, cupsLastErrorString ());
+    context->subscription_id = -1;
+    context->can_subscribe = FALSE;
+    goto fail;
+  }
+
+  for (attr = ippFirstAttribute(response); attr;
+       attr = ippNextAttribute(response)) {
+    if (ippGetGroupTag (attr) == IPP_TAG_SUBSCRIPTION) {
+      if (ippGetValueTag (attr) == IPP_TAG_INTEGER &&
+	  !strcasecmp (ippGetName (attr), "notify-subscription-id")) {
+	context->subscription_id = ippGetInteger (attr, 0);
+	debug_printf("cups-browsed [BrowsePoll %s:%d]: subscription ID=%d\n",
+		     context->server, context->port, context->subscription_id);
+	break;
+      }
+    }
+  }
+
+  if (!attr) {
+    debug_printf("cups-browsed [BrowsePoll %s:%d]: no ID returned\n",
+		 context->server, context->port);
+    context->subscription_id = -1;
+    context->can_subscribe = FALSE;
+  }
+
+fail:
+  if (response)
+    ippDelete(response);
+}
+
+static void
+browse_poll_cancel_subscription (browsepoll_t *context)
+{
+  ipp_t *request, *response = NULL;
+  http_t *conn = httpConnectEncrypt (context->server, context->port,
+				     HTTP_ENCRYPT_IF_REQUESTED);
+
+  if (conn == NULL) {
+    debug_printf("cups-browsed [BrowsePoll %s:%d]: connection failure "
+		 "attempting to cancel\n", context->server, context->port);
+    return;
+  }
+
+  debug_printf ("cups-browsed [BrowsePoll %s:%d]: IPP-Cancel-Subscription\n",
+		context->server, context->port);
+
+  request = ippNewRequest(IPP_CANCEL_SUBSCRIPTION);
+  if (context->major > 0) {
+    debug_printf("cups-browsed [BrowsePoll %s:%d]: setting IPP version %d.%d\n",
+		 context->server, context->port, context->major,
+		 context->minor);
+    ippSetVersion (request, context->major, context->minor);
+  }
+
+  ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
+		"printer-uri", NULL, "/");
+  ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+		"requesting-user-name", NULL, cupsUser ());
+  ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
+		 "notify-subscription-id", context->subscription_id);
+
+  response = cupsDoRequest (conn, request, "/");
+  if (!response || ippGetStatusCode (response) > IPP_OK_CONFLICT)
+    debug_printf("cupsd-browsed [BrowsePoll %s:%d]: failed: %s\n",
+		 context->server, context->port, cupsLastErrorString ());
+
+  if (response)
+    ippDelete(response);
+
+  httpClose (conn);
+}
+
+static gboolean
+browse_poll_get_notifications (browsepoll_t *context, http_t *conn)
+{
+  ipp_t *request, *response = NULL;
+  ipp_status_t status;
+  gboolean get_printers = FALSE;
+
+  debug_printf ("cups-browsed [BrowsePoll %s:%d]: IPP-Get-Notifications\n",
+		context->server, context->port);
+
+  request = ippNewRequest(IPP_GET_NOTIFICATIONS);
+  if (context->major > 0) {
+    debug_printf("cups-browsed [BrowsePoll %s:%d]: setting IPP version %d.%d\n",
+		 context->server, context->port, context->major,
+		 context->minor);
+    ippSetVersion (request, context->major, context->minor);
+  }
+
+  ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
+		"printer-uri", NULL, "/");
+  ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+		"requesting-user-name", NULL, cupsUser ());
+  ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
+		 "notify-subscription-ids", context->subscription_id);
+  ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
+		 "notify-sequence-numbers", context->sequence_number + 1);
+
+  response = cupsDoRequest (conn, request, "/");
+  if (!response)
+    status = cupsLastError ();
+  else
+    status = ippGetStatusCode (response);
+
+  if (status == IPP_NOT_FOUND) {
+    /* Subscription lease has expired. */
+    debug_printf ("cups-browsed [BrowsePoll %s:%d]: Lease expired\n",
+		  context->server, context->port);
+    browse_poll_create_subscription (context, conn);
+    get_printers = TRUE;
+  } else if (status > IPP_OK_CONFLICT) {
+    debug_printf("cupsd-browsed [BrowsePoll %s:%d]: failed: %s\n",
+		 context->server, context->port, cupsLastErrorString ());
+    context->can_subscribe = FALSE;
+    browse_poll_cancel_subscription (context);
+    context->subscription_id = -1;
+    context->sequence_number = 0;
+    get_printers = TRUE;
+  }
+
+  if (!get_printers) {
+    ipp_attribute_t *attr;
+    gboolean seen_event = FALSE;
+    int last_seq = context->sequence_number;
+    assert (response != NULL);
+    for (attr = ippFirstAttribute(response); attr;
+	 attr = ippNextAttribute(response))
+      if (ippGetGroupTag (attr) == IPP_TAG_EVENT_NOTIFICATION) {
+	/* There is a printer-* event here. */
+	seen_event = TRUE;
+
+	if (!strcmp (ippGetName (attr), "notify-sequence-number") &&
+	    ippGetValueTag (attr) == IPP_TAG_INTEGER)
+	  last_seq = ippGetInteger (attr, 0);
+      }
+
+    if (seen_event) {
+      debug_printf("cups-browsed [BrowsePoll %s:%d]: printer-* event\n",
+		   context->server, context->port);
+      context->sequence_number = last_seq;
+      get_printers = TRUE;
+    } else
+      debug_printf("cups-browsed [BrowsePoll %s:%d]: no events\n",
+		   context->server, context->port);
+  }
+
+  if (response)
+    ippDelete (response);
+
+  return get_printers;
+}
+
+static void
+browsepoll_printer_keepalive (gpointer data, gpointer user_data)
+{
+  browsepoll_printer_t *printer = data;
+  const char *server = user_data;
+  found_cups_printer (server, printer->uri_supported, printer->info);
+}
+
+gboolean
+browse_poll (gpointer data)
+{
+  browsepoll_t *context = data;
+  http_t *conn = NULL;
+  gboolean get_printers = FALSE;
+
+  debug_printf ("cups-browsed: browse polling %s:%d\n",
+		context->server, context->port);
+
+  res_init ();
+
+  conn = httpConnectEncrypt (context->server, context->port,
+			     HTTP_ENCRYPT_IF_REQUESTED);
+  if (conn == NULL) {
+    debug_printf("cups-browsed [BrowsePoll %s:%d]: failed to connect\n",
+		 context->server, context->port);
+    goto fail;
+  }
+
+  if (context->can_subscribe) {
+    if (context->subscription_id == -1) {
+      /* The first time this callback is run we need to create the IPP
+       * subscription to watch to printer-* events. */
+      browse_poll_create_subscription (context, conn);
+      get_printers = TRUE;
+    } else
+      /* On subsequent runs, check for notifications using our
+       * subscription. */
+      get_printers = browse_poll_get_notifications (context, conn);
+  }
+  else
+    get_printers = TRUE;
+
+  update_local_printers ();
+  inhibit_local_printers_update = TRUE;
+  if (get_printers)
+    browse_poll_get_printers (context, conn);
+  else
+    g_list_foreach (context->printers, browsepoll_printer_keepalive,
+		    context->server);
+
+  inhibit_local_printers_update = FALSE;
+
+fail:
 
   if (conn)
     httpClose (conn);
 
-  if (server)
-    free (server);
-
   /* Call a new timeout handler so that we run again */
   g_timeout_add_seconds (BrowseInterval, browse_poll, data);
 
@@ -1686,7 +2752,7 @@ compare_pointers (void *a, void *b, void
 }
 
 int compare_remote_printers (remote_printer_t *a, remote_printer_t *b) {
-  return strcmp(a->name, b->name);
+  return strcasecmp(a->name, b->name);
 }
 
 static void
@@ -1698,12 +2764,53 @@ sigterm_handler(int sig) {
   debug_printf("cups-browsed: Caught signal %d, shutting down ...\n", sig);
 }
 
+static void
+sigusr1_handler(int sig) {
+  (void)sig;    /* remove compiler warnings... */
+
+  /* Turn off auto shutdown mode... */
+  autoshutdown = 0;
+  debug_printf("cups-browsed: Caught signal %d, switching to permanent mode ...\n", sig);
+  /* If there is still an active auto shutdown timer, kill it */
+  if (autoshutdown_exec_id > 0) {
+    debug_printf ("cups-browsed: We have left auto shutdown mode, killing auto shutdown timer.\n");
+    g_source_destroy(g_main_context_find_source_by_id(NULL,
+						      autoshutdown_exec_id));
+    autoshutdown_exec_id = 0;
+  }
+}
+
+static void
+sigusr2_handler(int sig) {
+  (void)sig;    /* remove compiler warnings... */
+
+  /* Turn on auto shutdown mode... */
+  autoshutdown = 1;
+  debug_printf("cups-browsed: Caught signal %d, switching to auto shutdown mode ...\n", sig);
+  /* If there are no printers schedule the shutdown in autoshutdown_timeout
+     seconds */
+  if (!autoshutdown_exec_id &&
+      cupsArrayCount(remote_printers) == 0) {
+    debug_printf ("cups-browsed: We entered auto shutdown mode and no printers are there to make available, shutting down in %d sec...\n", autoshutdown_timeout);
+    autoshutdown_exec_id =
+      g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
+			       NULL);
+  }
+}
+
 static int
 read_browseallow_value (const char *value)
 {
   char *p;
   struct in_addr addr;
-  allow_t *allow = calloc (1, sizeof (allow_t));
+  allow_t *allow;
+
+  if (value && !strcasecmp (value, "all")) {
+    browseallow_all = TRUE;
+    return 0;
+  }
+  
+  allow = calloc (1, sizeof (allow_t));
   if (value == NULL)
     goto fail;
   p = strchr (value, '/');
@@ -1785,7 +2892,7 @@ read_configuration (const char *filename
 	else if (!strcasecmp(p, "cups"))
 	  protocols |= BROWSE_CUPS;
 	else if (strcasecmp(p, "none"))
-	  debug_printf("cups-browsed: unknown protocol '%s'\n", p);
+	  debug_printf("cups-browsed: Unknown protocol '%s'\n", p);
 
 	p = strtok_r (NULL, delim, &saveptr);
       }
@@ -1797,44 +2904,153 @@ read_configuration (const char *filename
       else
 	BrowseLocalProtocols = BrowseRemoteProtocols = protocols;
     } else if (!strcasecmp(line, "BrowsePoll") && value) {
-      char **old = BrowsePoll;
-      BrowsePoll = realloc (BrowsePoll, (NumBrowsePoll + 1) * sizeof (char *));
+      browsepoll_t **old = BrowsePoll;
+      BrowsePoll = realloc (BrowsePoll,
+			    (NumBrowsePoll + 1) *
+			    sizeof (browsepoll_t *));
       if (!BrowsePoll) {
 	debug_printf("cups-browsed: unable to realloc: ignoring BrowsePoll line\n");
 	BrowsePoll = old;
       } else {
+	char *colon, *slash;
+	browsepoll_t *b = g_malloc0 (sizeof (browsepoll_t));
 	debug_printf("cups-browsed: Adding BrowsePoll server: %s\n", value);
-	BrowsePoll[NumBrowsePoll++] = strdup (value);
+	b->server = strdup (value);
+	b->port = BrowsePort;
+	b->can_subscribe = TRUE; /* first assume subscriptions work */
+	b->subscription_id = -1;
+	slash = strchr (b->server, '/');
+	if (slash) {
+	  *slash++ = '\0';
+	  if (!strcasecmp (slash, "version=1.0")) {
+	    b->major = 1;
+	    b->minor = 0;
+	  } else if (!strcasecmp (slash, "version=1.1")) {
+	    b->major = 1;
+	    b->minor = 1;
+	  } else if (!strcasecmp (slash, "version=2.0")) {
+	    b->major = 2;
+	    b->minor = 0;
+	  } else if (!strcasecmp (slash, "version=2.1")) {
+	    b->major = 2;
+	    b->minor = 1;
+	  } else if (!strcasecmp (slash, "version=2.2")) {
+	    b->major = 2;
+	    b->minor = 2;
+	  } else {
+	    debug_printf ("ignoring unknown server option: %s\n", slash);
+	  }
+	} else
+	  b->major = 0;
+
+	colon = strchr (b->server, ':');
+	if (colon) {
+	  char *endptr;
+	  unsigned long n;
+	  *colon++ = '\0';
+	  n = strtoul (colon, &endptr, 10);
+	  if (endptr != colon && n < INT_MAX)
+	    b->port = (int) n;
+	}
+
+	BrowsePoll[NumBrowsePoll++] = b;
       }
-    } else if (!strcasecmp(line, "BrowseAllow"))
+    } else if (!strcasecmp(line, "BrowseAllow")) {
       if (read_browseallow_value (value))
 	debug_printf ("cups-browsed: BrowseAllow value \"%s\" not understood\n",
 		      value);
+    } else if (!strcasecmp(line, "DomainSocket") && value) {
+      if (value[0] != '\0')
+	DomainSocket = strdup(value);
+    } else if (!strcasecmp(line, "CreateIPPPrinterQueues") && value) {
+      if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
+	  !strcasecmp(value, "on") || !strcasecmp(value, "1"))
+	CreateIPPPrinterQueues = 1;
+      else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
+	  !strcasecmp(value, "off") || !strcasecmp(value, "0"))
+	CreateIPPPrinterQueues = 0;
+    } else if (!strcasecmp(line, "AutoShutdown") && value) {
+      char *p, *saveptr;
+      p = strtok_r (value, delim, &saveptr);
+      while (p) {
+	if (!strcasecmp(p, "On") || !strcasecmp(p, "Yes") ||
+	    !strcasecmp(p, "True") || !strcasecmp(p, "1")) {
+	  autoshutdown = 1;
+	  debug_printf("cups-browsed: Turning on auto shutdown mode.\n");
+	} else if (!strcasecmp(p, "Off") || !strcasecmp(p, "No") ||
+	    !strcasecmp(p, "False") || !strcasecmp(p, "0")) {
+	  autoshutdown = 0;
+	  debug_printf("cups-browsed: Turning off auto shutdown mode (permanent mode).\n");
+	} else if (!strcasecmp(p, "avahi")) {
+	  autoshutdown_avahi = 1;
+	  debug_printf("cups-browsed: Turning on auto shutdown control by appearing and disappearing of the Avahi server.\n");
+	} else if (strcasecmp(p, "none"))
+	  debug_printf("cups-browsed: Unknown mode '%s'\n", p);
+	p = strtok_r (NULL, delim, &saveptr);
+      }
+    } else if (!strcasecmp(line, "AutoShutdownTimeout") && value) {
+      int t = atoi(value);
+      if (t >= 0) {
+	autoshutdown_timeout = t;
+	debug_printf("cups-browsed: Set auto shutdown timeout to %d sec.\n",
+		     t);
+      } else
+	debug_printf("cups-browsed: Invalid auto shutdown timeout value: %d\n",
+		     t);
+    }
   }
 
   cupsFileClose(fp);
 }
 
+static void
+find_previous_queue (gpointer key,
+		     gpointer value,
+		     gpointer user_data)
+{
+  const char *name = key;
+  const local_printer_t *printer = value;
+  remote_printer_t *p;
+  if (printer->cups_browsed_controlled) {
+    /* Queue found, add to our list */
+    p = create_local_queue (name,
+			    printer->device_uri,
+			    "", "", "", "", NULL, NULL, 1);
+    if (p) {
+      /* Mark as unconfirmed, if no Avahi report of this queue appears
+	 in a certain time frame, we will remove the queue */
+      p->status = STATUS_UNCONFIRMED;
+
+      if (BrowseRemoteProtocols & BROWSE_CUPS)
+	p->timeout = time(NULL) + BrowseTimeout;
+      else
+	p->timeout = time(NULL) + TIMEOUT_CONFIRM;
+
+      p->duplicate = 0;
+      debug_printf("cups-browsed: Found CUPS queue %s (URI: %s) from previous session.\n",
+		   p->name, p->uri);
+    } else {
+      debug_printf("cups-browsed: ERROR: Unable to allocate memory.\n");
+      exit(1);
+    }
+  }
+}
+
 int main(int argc, char*argv[]) {
-#ifdef HAVE_AVAHI
-  AvahiClient *client = NULL;
-  AvahiServiceBrowser *sb1 = NULL, *sb2 = NULL;
-  int error;
-#endif /* HAVE_AVAHI */
   int ret = 1;
   http_t *http;
-  cups_dest_t *dests,
-              *dest;
-  int i,
-      num_dests;
+  int i;
   const char *val;
   remote_printer_t *p;
 
   /* Turn on debug mode if requested */
-  if (argc >= 2 &&
-      (!strcmp(argv[1], "--debug") || !strcmp(argv[1], "-d") ||
-       !strncmp(argv[1], "-v", 2)))
-    debug = 1;
+  if (argc >= 2)
+    for (i = 1; i < argc; i++)
+      if (!strcasecmp(argv[i], "--debug") || !strcasecmp(argv[i], "-d") ||
+	  !strncasecmp(argv[i], "-v", 2)) {
+	debug = 1;
+	debug_printf("cups-browsed: Reading command line: %s\n", argv[i]);
+      }
 
   /* Initialise the browseallow array */
   browseallow = cupsArrayNew(compare_pointers, NULL);
@@ -1842,6 +3058,79 @@ int main(int argc, char*argv[]) {
   /* Read in cups-browsed.conf */
   read_configuration (NULL);
 
+  /* Parse command line options after reading the config file to override
+     config file settings */
+  if (argc >= 2) {
+    for (i = 1; i < argc; i++)
+      if (!strncasecmp(argv[i], "--autoshutdown-timeout", 22)) {
+	debug_printf("cups-browsed: Reading command line: %s\n", argv[i]);
+	if (argv[i][22] == '=' && argv[i][23])
+	  val = argv[i] + 23;
+	else if (!argv[i][22] && i < argc -1) {
+	  i++;
+	  debug_printf("cups-browsed: Reading command line: %s\n", argv[i]);
+	  val = argv[i];
+	} else {
+	  fprintf(stderr, "cups-browsed: Expected auto shutdown timeout setting after \"--autoshutdown-timeout\" option.\n");
+	  exit(1);
+	}
+	int t = atoi(val);
+	if (t >= 0) {
+	  autoshutdown_timeout = t;
+	  debug_printf("cups-browsed: Set auto shutdown timeout to %d sec.\n",
+		       t);
+	} else {
+	  debug_printf("cups-browsed: Invalid auto shutdown timeout value: %d\n",
+		       t);
+	  exit(1);
+	}
+      } else if (!strncasecmp(argv[i], "--autoshutdown", 14)) {
+	debug_printf("cups-browsed: Reading command line: %s\n", argv[i]);
+	if (argv[i][14] == '=' && argv[i][15])
+	  val = argv[i] + 15;
+	else if (!argv[i][14] && i < argc -1) {
+	  i++;
+	  debug_printf("cups-browsed: Reading command line: %s\n", argv[i]);
+	  val = argv[i];
+	} else {
+	  fprintf(stderr, "cups-browsed: Expected auto shutdown setting after \"--autoshutdown\" option.\n");
+	  exit(1);
+	}
+	if (!strcasecmp(val, "On") || !strcasecmp(val, "Yes") ||
+	    !strcasecmp(val, "True") || !strcasecmp(val, "1")) {
+	  autoshutdown = 1;
+	  debug_printf("cups-browsed: Turning on auto shutdown mode.\n");
+	} else if (!strcasecmp(val, "Off") || !strcasecmp(val, "No") ||
+	    !strcasecmp(val, "False") || !strcasecmp(val, "0")) {
+	  autoshutdown = 0;
+	  debug_printf("cups-browsed: Turning off auto shutdown mode (permanent mode).\n");
+	} else if (!strcasecmp(val, "avahi")) {
+	  autoshutdown_avahi = 1;
+	  debug_printf("cups-browsed: Turning on auto shutdown control by appearing and disappearing of the Avahi server.\n");
+	} else if (strcasecmp(val, "none")) {
+	  debug_printf("cups-browsed: Unknown mode '%s'\n", val);
+	  exit(1);
+	}
+      }
+  }
+
+  /* Set the CUPS_SERVER environment variable to assure that cups-browsed
+     always works with the local CUPS daemon and never with a remote one
+     specified by a client.conf file */
+#ifdef CUPS_DEFAULT_DOMAINSOCKET
+  if (DomainSocket == NULL)
+    DomainSocket = CUPS_DEFAULT_DOMAINSOCKET;
+#endif
+  if (DomainSocket != NULL) {
+    struct stat sockinfo;               /* Domain socket information */
+    if (!stat(DomainSocket, &sockinfo) &&
+        (sockinfo.st_mode & S_IRWXO) == S_IRWXO)
+      setenv("CUPS_SERVER", DomainSocket, 1);
+    else
+      setenv("CUPS_SERVER", "localhost", 1);
+  } else
+    setenv("CUPS_SERVER", "localhost", 1);
+
   if (BrowseLocalProtocols & BROWSE_DNSSD) {
     fprintf(stderr, "Local support for DNSSD not implemented\n");
     BrowseLocalProtocols &= ~BROWSE_DNSSD;
@@ -1855,60 +3144,34 @@ int main(int argc, char*argv[]) {
 #endif /* HAVE_AVAHI */
 
   /* Wait for CUPS daemon to start */
-  while ((http = httpConnectEncrypt(cupsServer(), ippPort(),
-				    cupsEncryption())) == NULL)
+  while ((http = http_connect_local ()) == NULL)
     sleep(1);
 
   /* Initialise the array of network interfaces */
   netifs = cupsArrayNew(compare_pointers, NULL);
   update_netifs ();
 
+  local_printers = g_hash_table_new_full (g_str_hash,
+					  g_str_equal,
+					  g_free,
+					  free_local_printer);
+
   /* Read out the currently defined CUPS queues and find the ones which we
      have added in an earlier session */
-  num_dests = cupsGetDests(&dests);
+  update_local_printers ();
   remote_printers = cupsArrayNew((cups_array_func_t)compare_remote_printers,
 				 NULL);
-  if (num_dests > 0) {
-    for (i = num_dests, dest = dests; i > 0; i --, dest ++) {
-      if ((val = cupsGetOption(CUPS_BROWSED_MARK, dest->num_options,
-			       dest->options)) != NULL) {
-	if (strcasecmp(val, "no") != 0 && strcasecmp(val, "off") != 0 &&
-	    strcasecmp(val, "false") != 0) {
-	  /* Queue found, add to our list */
-	  p = create_local_queue (dest->name,
-				  strdup(cupsGetOption("device-uri",
-						       dest->num_options,
-						       dest->options)),
-				  "", "", "", "");
-	  if (p) {
-	    /* Mark as unconfirmed, if no Avahi report of this queue appears
-	       in a certain time frame, we will remove the queue */
-	    p->status = STATUS_UNCONFIRMED;
-
-	    if (BrowseRemoteProtocols & BROWSE_CUPS)
-	      p->timeout = time(NULL) + BrowseTimeout;
-	    else
-	      p->timeout = time(NULL) + TIMEOUT_CONFIRM;
-
-	    p->duplicate = 0;
-	    debug_printf("cups-browsed: Found CUPS queue %s (URI: %s) from previous session.\n",
-			 p->name, p->uri);
-	  } else {
-	    debug_printf("cups-browsed: ERROR: Unable to allocate memory.\n");
-	    exit(1);
-	  }
-	}
-      }
-    }
-    cupsFreeDests(num_dests, dests);
-  }
-  httpClose(http);
+  g_hash_table_foreach (local_printers, find_previous_queue, NULL);
 
   /* Redirect SIGINT and SIGTERM so that we do a proper shutdown, removing
-     the CUPS queues which we have created */
+     the CUPS queues which we have created
+     Use SIGUSR1 and SIGUSR2 to turn off and turn on auto shutdown mode
+     resp. */
 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
   sigset(SIGTERM, sigterm_handler);
   sigset(SIGINT, sigterm_handler);
+  sigset(SIGUSR1, sigusr1_handler);
+  sigset(SIGUSR2, sigusr2_handler);
   debug_printf("cups-browsed: Using signal handler SIGSET\n");
 #elif defined(HAVE_SIGACTION)
   struct sigaction action; /* Actions for POSIX signals */
@@ -1921,65 +3184,27 @@ int main(int argc, char*argv[]) {
   sigaddset(&action.sa_mask, SIGINT);
   action.sa_handler = sigterm_handler;
   sigaction(SIGINT, &action, NULL);
+  sigemptyset(&action.sa_mask);
+  sigaddset(&action.sa_mask, SIGUSR1);
+  action.sa_handler = sigusr1_handler;
+  sigaction(SIGUSR1, &action, NULL);
+  sigemptyset(&action.sa_mask);
+  sigaddset(&action.sa_mask, SIGUSR2);
+  action.sa_handler = sigusr2_handler;
+  sigaction(SIGUSR2, &action, NULL);
   debug_printf("cups-browsed: Using signal handler SIGACTION\n");
 #else
   signal(SIGTERM, sigterm_handler);
   signal(SIGINT, sigterm_handler);
+  signal(SIGUSR1, sigusr1_handler);
+  signal(SIGUSR2, sigusr2_handler);
   debug_printf("cups-browsed: Using signal handler SIGNAL\n");
 #endif /* HAVE_SIGSET */
 
 #ifdef HAVE_AVAHI
-  if (BrowseRemoteProtocols & BROWSE_DNSSD) {
-    /* Allocate main loop object */
-    if (!(glib_poll = avahi_glib_poll_new(NULL, G_PRIORITY_DEFAULT))) {
-      debug_printf("cups-browsed: ERROR: Failed to create glib poll object.\n");
-      goto avahi_fail;
-    }
-
-    /* Allocate a new client */
-    client = avahi_client_new(avahi_glib_poll_get(glib_poll), 0,
-			      client_callback, NULL, &error);
-
-    /* Check wether creating the client object succeeded */
-    if (!client) {
-      debug_printf("cups-browsed: ERROR: Failed to create client: %s\n",
-		   avahi_strerror(error));
-      goto avahi_fail;
-    }
-
-    /* Create the service browsers */
-    if (!(sb1 =
-	  avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
-				    "_ipp._tcp", NULL, 0, browse_callback,
-				    client))) {
-      debug_printf("cups-browsed: ERROR: Failed to create service browser for IPP: %s\n",
-		   avahi_strerror(avahi_client_errno(client)));
-      goto avahi_fail;
-    }
-    if (!(sb2 =
-	  avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
-				    "_ipps._tcp", NULL, 0, browse_callback,
-				    client))) {
-      debug_printf("cups-browsed: ERROR: Failed to create service browser for IPPS: %s\n",
-		   avahi_strerror(avahi_client_errno(client)));
-    avahi_fail:
-      BrowseRemoteProtocols &= ~BROWSE_DNSSD;
-      if (sb2) {
-	avahi_service_browser_free(sb2);
-	sb2 = NULL;
-      }
-
-      if (client) {
-	avahi_client_free(client);
-	client = NULL;
-      }
-
-      if (glib_poll) {
-	avahi_glib_poll_free(glib_poll);
-	glib_poll = NULL;
-      }
-    }
-  }
+  if (autoshutdown_avahi)
+    autoshutdown = 1;
+  avahi_init();
 #endif /* HAVE_AVAHI */
 
   if (BrowseLocalProtocols & BROWSE_CUPS ||
@@ -2025,6 +3250,10 @@ int main(int argc, char*argv[]) {
     goto fail;
   }
 
+  /* Override the default password callback so we don't end up
+   * prompting for it. */
+  cupsSetPasswordCB2 (password_callback, NULL);
+
   /* Run the main loop */
   gmainloop = g_main_loop_new (NULL, FALSE);
   recheck_timer ();
@@ -2047,11 +3276,20 @@ int main(int argc, char*argv[]) {
 	 index < NumBrowsePoll;
 	 index++) {
       debug_printf ("cups-browsed: will browse poll %s every %ds\n",
-		    BrowsePoll[index], BrowseInterval);
+		    BrowsePoll[index]->server, BrowseInterval);
       g_idle_add (browse_poll, BrowsePoll[index]);
     }
   }
 
+  /* If auto shutdown is active and we do not find any printers initially,
+     schedule the shutdown in autoshutdown_timeout seconds */
+  if (autoshutdown && !autoshutdown_exec_id &&
+      cupsArrayCount(remote_printers) == 0) {
+    debug_printf ("cups-browsed: No printers found to make available, shutting down in %d sec...\n", autoshutdown_timeout);
+    autoshutdown_exec_id =
+      g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute, NULL);
+  }
+
   g_main_loop_run (gmainloop);
 
   debug_printf("cups-browsed: main loop exited\n");
@@ -2071,22 +3309,44 @@ fail:
   }
   handle_cups_queues(NULL);
 
-#ifdef HAVE_AVAHI
-  /* Free the data structures for Bonjour browsing */
-  if (sb1)
-    avahi_service_browser_free(sb1);
-  if (sb2)
-    avahi_service_browser_free(sb2);
+  if (BrowsePoll) {
+    size_t index;
+    for (index = 0;
+	 index < NumBrowsePoll;
+	 index++) {
+      if (BrowsePoll[index]->can_subscribe &&
+	  BrowsePoll[index]->subscription_id != -1)
+	browse_poll_cancel_subscription (BrowsePoll[index]);
 
-  if (client)
-    avahi_client_free(client);
+      free (BrowsePoll[index]->server);
+      g_list_free_full (BrowsePoll[index]->printers,
+			browsepoll_printer_free);
+      free (BrowsePoll[index]);
+    }
 
-  if (glib_poll)
-    avahi_glib_poll_free(glib_poll);
+    free (BrowsePoll);
+  }
+
+  if (local_printers_context) {
+    browse_poll_cancel_subscription (local_printers_context);
+    g_list_free_full (local_printers_context->printers,
+		      browsepoll_printer_free);
+    free (local_printers_context);
+  }
+
+  http_close_local ();
+
+#ifdef HAVE_AVAHI
+  avahi_shutdown();
 #endif /* HAVE_AVAHI */
 
   if (browsesocket != -1)
       close (browsesocket);
 
+  g_hash_table_destroy (local_printers);
+
+  if (BrowseLocalProtocols & BROWSE_CUPS)
+    g_list_free_full (browse_data, browse_data_free);
+
   return ret;
 }