[gtk+/gtk-2-24] printing: List Avahi printers



commit a057ed26dc623dff0fc0c62ef287f6583b2710d0
Author: Marek Kasik <mkasik redhat com>
Date:   Tue Jun 25 14:34:15 2013 +0200

    printing: List Avahi printers
    
    Show printers advertised by avahi on local network. CUPS
    backend now looks for _ipps._tcp and _ipp._tcp services
    offered by avahi. If it finds such a service (printer)
    it requests its attributes through IPP_GET_PRINTER_ATTRIBUTES
    ipp request and adds it to the list of printers. Such printer
    behaves like a remote printer then.
    If an avahi printer is a default printer then it is considered
    default by the backend only if there is no local or remote
    default printer.
    This functionality is enabled when building Gtk+ with CUPS 1.6
    or later because it replaces browsing protocol removed in CUPS 1.6.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=702455

 configure.ac                                     |    2 +-
 modules/printbackends/cups/gtkcupsutils.c        |   22 +
 modules/printbackends/cups/gtkcupsutils.h        |    3 +
 modules/printbackends/cups/gtkprintbackendcups.c | 1290 +++++++++++++++++++---
 modules/printbackends/cups/gtkprintercups.c      |   29 +-
 modules/printbackends/cups/gtkprintercups.h      |   11 +
 6 files changed, 1205 insertions(+), 152 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 5f8a787..50e27b0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1479,7 +1479,7 @@ else
            $CUPS_API_MAJOR -eq 1 -a $CUPS_API_MINOR -ge 6; then
       AC_DEFINE(HAVE_CUPS_API_1_6, 1,
                 [Define to 1 if CUPS 1.6 API is available])
-
+      have_cups_api_1_6=yes
     fi
 
     AC_SUBST(CUPS_API_MAJOR)
diff --git a/modules/printbackends/cups/gtkcupsutils.c b/modules/printbackends/cups/gtkcupsutils.c
index 4a3f0ad..6b10db3 100644
--- a/modules/printbackends/cups/gtkcupsutils.c
+++ b/modules/printbackends/cups/gtkcupsutils.c
@@ -89,6 +89,20 @@ static GtkCupsRequestStateFunc get_states[] = {
 #define ippSetState(ipp_request, ipp_state) ipp_request->state = ipp_state
 #define ippGetString(attr, index, foo) attr->values[index].string.text
 #define ippGetCount(attr) attr->num_values
+
+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
 
 static void
@@ -659,6 +673,14 @@ gtk_cups_request_encode_option (GtkCupsRequest *request,
 }
                                
 
+void
+gtk_cups_request_set_ipp_version (GtkCupsRequest     *request,
+                                 gint                major,
+                                 gint                minor)
+{
+  ippSetVersion (request->ipp_request, major, minor);
+}
+
 static void
 _connect (GtkCupsRequest *request)
 {
diff --git a/modules/printbackends/cups/gtkcupsutils.h b/modules/printbackends/cups/gtkcupsutils.h
index 2adfc16..53171ed 100644
--- a/modules/printbackends/cups/gtkcupsutils.h
+++ b/modules/printbackends/cups/gtkcupsutils.h
@@ -182,6 +182,9 @@ gboolean                gtk_cups_request_is_done           (GtkCupsRequest     *
 void                    gtk_cups_request_encode_option     (GtkCupsRequest     *request,
                                                            const gchar        *option,
                                                            const gchar        *value);
+void                    gtk_cups_request_set_ipp_version   (GtkCupsRequest     *request,
+                                                           gint                major,
+                                                           gint                minor);
 gboolean                gtk_cups_result_is_error           (GtkCupsResult      *result);
 ipp_t                 * gtk_cups_result_get_response       (GtkCupsResult      *result);
 GtkCupsErrorType        gtk_cups_result_get_error_type     (GtkCupsResult      *result);
diff --git a/modules/printbackends/cups/gtkprintbackendcups.c 
b/modules/printbackends/cups/gtkprintbackendcups.c
index 79033dd..60c2f7e 100644
--- a/modules/printbackends/cups/gtkprintbackendcups.c
+++ b/modules/printbackends/cups/gtkprintbackendcups.c
@@ -71,6 +71,17 @@ typedef struct _GtkPrintBackendCupsClass GtkPrintBackendCupsClass;
 #define _CUPS_MAX_ATTEMPTS 10 
 #define _CUPS_MAX_CHUNK_SIZE 8192
 
+#ifdef HAVE_CUPS_API_1_6
+#define AVAHI_IF_UNSPEC -1
+#define AVAHI_PROTO_INET 0
+#define AVAHI_PROTO_INET6 1
+#define AVAHI_PROTO_UNSPEC -1
+
+#define AVAHI_BUS "org.freedesktop.Avahi"
+#define AVAHI_SERVER_IFACE "org.freedesktop.Avahi.Server"
+#define AVAHI_SERVICE_BROWSER_IFACE "org.freedesktop.Avahi.ServiceBrowser"
+#endif
+
 /* define this to see warnings about ignored ppd options */
 #undef PRINT_IGNORED_OPTIONS
 
@@ -133,6 +144,14 @@ struct _GtkPrintBackendCups
   GHashTable *auth;
   gchar      *username;
   gboolean    authentication_lock;
+#ifdef HAVE_CUPS_API_1_6
+  GDBusConnection *dbus_connection;
+  gchar           *avahi_default_printer;
+  guint            avahi_service_browser_subscription_id;
+  guint            avahi_service_browser_subscription_ids[2];
+  gchar           *avahi_service_browser_paths[2];
+  GCancellable    *avahi_cancellable;
+#endif
 };
 
 static GObjectClass *backend_parent_class;
@@ -199,6 +218,10 @@ void                        overwrite_and_free                      (gpointer
 static gboolean             is_address_local                        (const gchar                      
*address);
 static gboolean             request_auth_info                       (gpointer                          data);
 
+#ifdef HAVE_CUPS_API_1_6
+static void                 avahi_request_printer_list              (GtkPrintBackendCups              
*cups_backend);
+#endif
+
 static void
 gtk_print_backend_cups_register_type (GTypeModule *module)
 {
@@ -257,6 +280,15 @@ pb_module_create (void)
 #define ippGetName(attr) attr->name
 #define ippGetCount(attr) attr->num_values
 #define ippGetGroupTag(attr) attr->group_tag
+
+static int
+ippGetRange (ipp_attribute_t *attr,
+             int element,
+             int *upper)
+{
+  *upper = attr->values[element].range.upper;
+  return (attr->values[element].range.lower);
+}
 #endif
 /*
  * GtkPrintBackendCups
@@ -580,7 +612,7 @@ gtk_print_backend_cups_print_stream (GtkPrintBackend         *print_backend,
   GtkPrinterCups *cups_printer;
   CupsPrintStreamData *ps;
   CupsOptionsData *options_data;
-  GtkCupsRequest *request;
+  GtkCupsRequest *request = NULL;
   GtkPrintSettings *settings;
   const gchar *title;
   char  printer_absolute_uri[HTTP_MAX_URI];
@@ -599,23 +631,82 @@ gtk_print_backend_cups_print_stream (GtkPrintBackend         *print_backend,
                                                 cups_printer->device_uri,
                                                 GTK_PRINT_BACKEND_CUPS (print_backend)->username);
 
+#ifdef HAVE_CUPS_API_1_6
+  if (cups_printer->avahi_browsed)
+    {
+      http_t *http;
+
+      http = httpConnect (cups_printer->hostname, cups_printer->port);
+      if (http)
+        {
+          request = gtk_cups_request_new_with_username (http,
+                                                        GTK_CUPS_POST,
+                                                        IPP_PRINT_JOB,
+                                                        data_io,
+                                                        cups_printer->hostname,
+                                                        cups_printer->device_uri,
+                                                        GTK_PRINT_BACKEND_CUPS (print_backend)->username);
+          g_snprintf (printer_absolute_uri, HTTP_MAX_URI, "%s", cups_printer->printer_uri);
+        }
+      else
+        {
+          GError *error = NULL;
+
+          GTK_NOTE (PRINTING,
+                    g_warning ("CUPS Backend: Error connecting to %s:%d",
+                               cups_printer->hostname,
+                               cups_printer->port));
+
+          error = g_error_new (gtk_print_error_quark (),
+                               GTK_CUPS_ERROR_GENERAL,
+                               "Error connecting to %s",
+                               cups_printer->hostname);
+
+          gtk_print_job_set_status (job, GTK_PRINT_STATUS_FINISHED_ABORTED);
+
+          if (callback)
+            {
+              callback (job, user_data, error);
+            }
+
+          g_clear_error (&error);
+
+          return;
+        }
+    }
+  else
+#endif
+    {
+      request = gtk_cups_request_new_with_username (NULL,
+                                                    GTK_CUPS_POST,
+                                                    IPP_PRINT_JOB,
+                                                    data_io,
+                                                    NULL,
+                                                    cups_printer->device_uri,
+                                                    GTK_PRINT_BACKEND_CUPS (print_backend)->username);
+
 #if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 2) || CUPS_VERSION_MAJOR > 1
-  httpAssembleURIf (HTTP_URI_CODING_ALL,
-                    printer_absolute_uri,
-                    sizeof (printer_absolute_uri),
-                    "ipp",
-                    NULL,
-                    "localhost",
-                    ippPort (),
-                    "/printers/%s",
-                    gtk_printer_get_name (gtk_print_job_get_printer (job)));
+      httpAssembleURIf (HTTP_URI_CODING_ALL,
+                        printer_absolute_uri,
+                        sizeof (printer_absolute_uri),
+                        "ipp",
+                        NULL,
+                        "localhost",
+                        ippPort (),
+                        "/printers/%s",
+                        gtk_printer_get_name (gtk_print_job_get_printer (job)));
 #else
-  g_snprintf (printer_absolute_uri,
-              sizeof (printer_absolute_uri),
-              "ipp://localhost:%d/printers/%s",
-              ippPort (),
-              gtk_printer_get_name (gtk_print_job_get_printer (job)));
+      g_snprintf (printer_absolute_uri,
+                  sizeof (printer_absolute_uri),
+                  "ipp://localhost:%d/printers/%s",
+                  ippPort (),
+                  gtk_printer_get_name (gtk_print_job_get_printer (job)));
 #endif
+    }
+
+  gtk_cups_request_set_ipp_version (request,
+                                    cups_printer->ipp_version_major,
+                                    cups_printer->ipp_version_minor);
 
   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, 
                                    IPP_TAG_URI, "printer-uri",
@@ -663,6 +754,10 @@ void overwrite_and_free (gpointer data)
 static void
 gtk_print_backend_cups_init (GtkPrintBackendCups *backend_cups)
 {
+#ifdef HAVE_CUPS_API_1_6
+  gint i;
+#endif
+
   backend_cups->list_printers_poll = FALSE;  
   backend_cups->got_default_printer = FALSE;  
   backend_cups->list_printers_pending = FALSE;
@@ -681,6 +776,17 @@ gtk_print_backend_cups_init (GtkPrintBackendCups *backend_cups)
 
   backend_cups->username = NULL;
 
+#ifdef HAVE_CUPS_API_1_6
+  backend_cups->dbus_connection = NULL;
+  backend_cups->avahi_default_printer = NULL;
+  backend_cups->avahi_service_browser_subscription_id = 0;
+  for (i = 0; i < 2; i++)
+    {
+      backend_cups->avahi_service_browser_paths[i] = NULL;
+      backend_cups->avahi_service_browser_subscription_ids[i] = 0;
+    }
+#endif
+
   cups_get_local_default_printer (backend_cups);
 }
 
@@ -707,6 +813,12 @@ gtk_print_backend_cups_finalize (GObject *object)
 
   g_free (backend_cups->username);
 
+#ifdef HAVE_CUPS_API_1_6
+  g_clear_object (&backend_cups->avahi_cancellable);
+  g_clear_pointer (&backend_cups->avahi_default_printer, g_free);
+  g_clear_object (&backend_cups->dbus_connection);
+#endif
+
   backend_parent_class->finalize (object);
 }
 
@@ -714,6 +826,9 @@ static void
 gtk_print_backend_cups_dispose (GObject *object)
 {
   GtkPrintBackendCups *backend_cups;
+#ifdef HAVE_CUPS_API_1_6
+  gint                 i;
+#endif
 
   GTK_NOTE (PRINTING,
             g_print ("CUPS Backend: %s\n", G_STRFUNC));
@@ -729,6 +844,44 @@ gtk_print_backend_cups_dispose (GObject *object)
     g_source_remove (backend_cups->default_printer_poll);
   backend_cups->default_printer_poll = 0;
 
+#ifdef HAVE_CUPS_API_1_6
+  g_cancellable_cancel (backend_cups->avahi_cancellable);
+
+  for (i = 0; i < 2; i++)
+    {
+      if (backend_cups->avahi_service_browser_subscription_ids[i] > 0)
+        {
+          g_dbus_connection_signal_unsubscribe (backend_cups->dbus_connection,
+                                                backend_cups->avahi_service_browser_subscription_ids[i]);
+          backend_cups->avahi_service_browser_subscription_ids[i] = 0;
+        }
+
+      if (backend_cups->avahi_service_browser_paths[i])
+        {
+          g_dbus_connection_call (backend_cups->dbus_connection,
+                                  AVAHI_BUS,
+                                  backend_cups->avahi_service_browser_paths[i],
+                                  AVAHI_SERVICE_BROWSER_IFACE,
+                                  "Free",
+                                  NULL,
+                                  NULL,
+                                  G_DBUS_CALL_FLAGS_NONE,
+                                  -1,
+                                  NULL,
+                                  NULL,
+                                  NULL);
+          g_clear_pointer (&backend_cups->avahi_service_browser_paths[i], g_free);
+        }
+    }
+
+  if (backend_cups->avahi_service_browser_subscription_id > 0)
+    {
+      g_dbus_connection_signal_unsubscribe (backend_cups->dbus_connection,
+                                            backend_cups->avahi_service_browser_subscription_id);
+      backend_cups->avahi_service_browser_subscription_id = 0;
+    }
+#endif
+
   backend_parent_class->dispose (object);
 }
 
@@ -1716,6 +1869,30 @@ static const char * printer_strings[] =
     N_("There is a problem on printer '%s'.")
   };
 
+/* Attributes we're interested in for printers */
+static const char * const printer_attrs[] =
+  {
+    "printer-name",
+    "printer-uri-supported",
+    "member-uris",
+    "printer-location",
+    "printer-info",
+    "printer-state-message",
+    "printer-state-reasons",
+    "printer-state",
+    "queued-job-count",
+    "printer-is-accepting-jobs",
+    "job-sheets-supported",
+    "job-sheets-default",
+    "printer-type",
+    "auth-info-required",
+    "number-up-default",
+    "ipp-versions-supported",
+    "multiple-document-handling-supported",
+    "copies-supported",
+    "number-up-supported"
+  };
+
 typedef enum
   {
     GTK_PRINTER_STATE_LEVEL_NONE = 0,
@@ -1743,10 +1920,88 @@ typedef struct
   gboolean default_printer;
   gboolean got_printer_type;
   gboolean remote_printer;
+#ifdef HAVE_CUPS_API_1_6
+  gboolean avahi_printer;
+#endif
   gchar  **auth_info_required;
+  guchar   ipp_version_major;
+  guchar   ipp_version_minor;
+  gboolean supports_copies;
+  gboolean supports_collate;
+  gboolean supports_number_up;
 } PrinterSetupInfo;
 
 static void
+get_ipp_version (const char *ipp_version_string,
+                 guchar     *ipp_version_major,
+                 guchar     *ipp_version_minor)
+{
+  gchar **ipp_version_strv;
+  gchar  *endptr;
+
+  *ipp_version_major = 1;
+  *ipp_version_minor = 1;
+
+  if (ipp_version_string)
+    {
+      ipp_version_strv = g_strsplit (ipp_version_string, ".", 0);
+
+      if (ipp_version_strv)
+        {
+          if (g_strv_length (ipp_version_strv) == 2)
+            {
+              *ipp_version_major = (guchar) g_ascii_strtoull (ipp_version_strv[0], &endptr, 10);
+              if (endptr == ipp_version_strv[0])
+                *ipp_version_major = 1;
+
+              *ipp_version_minor = (guchar) g_ascii_strtoull (ipp_version_strv[1], &endptr, 10);
+              if (endptr == ipp_version_strv[1])
+                *ipp_version_minor = 1;
+            }
+
+          g_strfreev (ipp_version_strv);
+        }
+    }
+}
+
+static void
+get_server_ipp_version (guchar *ipp_version_major,
+                        guchar *ipp_version_minor)
+{
+  *ipp_version_major = 1;
+  *ipp_version_minor = 1;
+
+  if (IPP_VERSION && strlen (IPP_VERSION) == 2)
+    {
+      *ipp_version_major = (unsigned char) IPP_VERSION[0];
+      *ipp_version_minor = (unsigned char) IPP_VERSION[1];
+    }
+}
+
+static gint
+ipp_version_cmp (guchar ipp_version_major1,
+                 guchar ipp_version_minor1,
+                 guchar ipp_version_major2,
+                 guchar ipp_version_minor2)
+{
+  if (ipp_version_major1 == ipp_version_major2 &&
+      ipp_version_minor1 == ipp_version_minor2)
+    {
+      return 0;
+    }
+  else if (ipp_version_major1 < ipp_version_major2 ||
+           (ipp_version_major1 == ipp_version_major2 &&
+            ipp_version_minor1 < ipp_version_minor2))
+    {
+      return -1;
+    }
+  else
+    {
+      return 1;
+    }
+}
+
+static void
 cups_printer_handle_attribute (GtkPrintBackendCups *cups_backend,
                               ipp_attribute_t *attr,
                               PrinterSetupInfo *info)
@@ -1867,6 +2122,63 @@ cups_printer_handle_attribute (GtkPrintBackendCups *cups_backend,
            info->auth_info_required[i] = g_strdup (ippGetString (attr, i, NULL));
        }
     }
+  else if (g_strcmp0 (ippGetName (attr), "ipp-versions-supported") == 0)
+    {
+      guchar server_ipp_version_major;
+      guchar server_ipp_version_minor;
+      guchar ipp_version_major;
+      guchar ipp_version_minor;
+
+      get_server_ipp_version (&server_ipp_version_major,
+                              &server_ipp_version_minor);
+
+      for (i = 0; i < ippGetCount (attr); i++)
+        {
+          get_ipp_version (ippGetString (attr, i, NULL),
+                           &ipp_version_major,
+                           &ipp_version_minor);
+
+          if (ipp_version_cmp (ipp_version_major,
+                               ipp_version_minor,
+                               info->ipp_version_major,
+                               info->ipp_version_minor) > 0 &&
+              ipp_version_cmp (ipp_version_major,
+                               ipp_version_minor,
+                               server_ipp_version_major,
+                               server_ipp_version_minor) <= 0)
+            {
+              info->ipp_version_major = ipp_version_major;
+              info->ipp_version_minor = ipp_version_minor;
+            }
+        }
+    }
+  else if (g_strcmp0 (ippGetName (attr), "number-up-supported") == 0)
+    {
+      if (ippGetCount (attr) == 6)
+        {
+          info->supports_number_up = TRUE;
+        }
+    }
+  else if (g_strcmp0 (ippGetName (attr), "copies-supported") == 0)
+    {
+      int upper = 1;
+
+      ippGetRange (attr, 0, &upper);
+      if (upper > 1)
+        {
+          info->supports_copies = TRUE;
+        }
+    }
+  else if (g_strcmp0 (ippGetName (attr), "multiple-document-handling-supported") == 0)
+    {
+      for (i = 0; i < ippGetCount (attr); i++)
+        {
+          if (g_strcmp0 (ippGetString (attr, i, NULL), "separate-documents-collated-copies") == 0)
+            {
+              info->supports_collate = TRUE;
+            }
+        }
+    }
   else
     {
       GTK_NOTE (PRINTING,
@@ -1955,22 +2267,722 @@ cups_create_printer (GtkPrintBackendCups *cups_backend,
 
   cups_printer->hostname = g_strdup (hostname);
   cups_printer->port = port;
-         
+
   cups_printer->auth_info_required = g_strdupv (info->auth_info_required);
   g_strfreev (info->auth_info_required);
 
   printer = GTK_PRINTER (cups_printer);
-         
+
   if (cups_backend->default_printer != NULL &&
       strcmp (cups_backend->default_printer, gtk_printer_get_name (printer)) == 0)
     gtk_printer_set_is_default (printer, TRUE);
 
-         
+#ifdef HAVE_CUPS_API_1_6
+  cups_printer->avahi_browsed = info->avahi_printer;
+#endif
+
   gtk_print_backend_add_printer (backend, printer);
   return printer;
 }
 
 static void
+set_printer_icon_name_from_info (GtkPrinter       *printer,
+                                 PrinterSetupInfo *info)
+{
+  /* Set printer icon according to importance
+     (none, report, warning, error - report is omitted). */
+  if (info->reason_level == GTK_PRINTER_STATE_LEVEL_ERROR)
+    gtk_printer_set_icon_name (printer, "printer-error");
+  else if (info->reason_level == GTK_PRINTER_STATE_LEVEL_WARNING)
+    gtk_printer_set_icon_name (printer, "printer-warning");
+  else if (gtk_printer_is_paused (printer))
+    gtk_printer_set_icon_name (printer, "printer-paused");
+  else
+    gtk_printer_set_icon_name (printer, "printer");
+}
+
+static void
+set_info_state_message (PrinterSetupInfo *info)
+{
+  gint i;
+
+  if (info->state_msg && strlen (info->state_msg) == 0)
+    {
+      gchar *tmp_msg2 = NULL;
+      if (info->is_paused && !info->is_accepting_jobs)
+        /* Translators: this is a printer status. */
+        tmp_msg2 = g_strdup ( _("Paused; Rejecting Jobs"));
+      if (info->is_paused && info->is_accepting_jobs)
+        /* Translators: this is a printer status. */
+        tmp_msg2 = g_strdup ( _("Paused"));
+      if (!info->is_paused && !info->is_accepting_jobs)
+        /* Translators: this is a printer status. */
+        tmp_msg2 = g_strdup ( _("Rejecting Jobs"));
+
+      if (tmp_msg2 != NULL)
+        {
+          g_free (info->state_msg);
+          info->state_msg = tmp_msg2;
+        }
+    }
+
+  /* Set description of the reason and combine it with printer-state-message. */
+  if (info->reason_msg)
+    {
+      gchar *reason_msg_desc = NULL;
+      gboolean found = FALSE;
+
+      for (i = 0; i < G_N_ELEMENTS (printer_messages); i++)
+        {
+          if (strncmp (info->reason_msg, printer_messages[i],
+                       strlen (printer_messages[i])) == 0)
+            {
+              reason_msg_desc = g_strdup_printf (printer_strings[i],
+                                                 info->printer_name);
+              found = TRUE;
+              break;
+            }
+        }
+
+      if (!found)
+        info->reason_level = GTK_PRINTER_STATE_LEVEL_NONE;
+
+      if (info->reason_level >= GTK_PRINTER_STATE_LEVEL_WARNING)
+        {
+          if (info->state_msg == NULL || info->state_msg[0] == '\0')
+            {
+              g_free (info->state_msg);
+              info->state_msg = reason_msg_desc;
+              reason_msg_desc = NULL;
+            }
+          else
+            {
+              gchar *tmp_msg = NULL;
+              /* Translators: this string connects multiple printer states together. */
+              tmp_msg = g_strjoin ( _("; "), info->state_msg,
+                                   reason_msg_desc, NULL);
+              g_free (info->state_msg);
+              info->state_msg = tmp_msg;
+            }
+        }
+
+      g_free (reason_msg_desc);
+    }
+}
+
+static void
+set_default_printer (GtkPrintBackendCups *cups_backend,
+                     const gchar         *default_printer_name)
+{
+  cups_backend->default_printer = g_strdup (default_printer_name);
+  cups_backend->got_default_printer = TRUE;
+
+  if (cups_backend->default_printer != NULL)
+    {
+      GtkPrinter *default_printer = NULL;
+      default_printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (cups_backend),
+                                                        cups_backend->default_printer);
+      if (default_printer != NULL)
+        {
+          gtk_printer_set_is_default (default_printer, TRUE);
+          g_signal_emit_by_name (GTK_PRINT_BACKEND (cups_backend),
+                                 "printer-status-changed", default_printer);
+        }
+    }
+}
+
+#ifdef HAVE_CUPS_API_1_6
+typedef struct
+{
+  gchar *name;
+  gchar *type;
+  gchar *domain;
+  gchar *host;
+  gint   port;
+} AvahiService;
+
+void
+avahi_service_free (AvahiService *service)
+{
+  if (service)
+    {
+      g_free (service->name);
+      g_free (service->type);
+      g_free (service->domain);
+      g_free (service->host);
+      g_free (service);
+    }
+}
+
+static void
+cups_request_avahi_printer_info_cb (GtkPrintBackendCups *cups_backend,
+                                    GtkCupsResult       *result,
+                                    gpointer             user_data)
+{
+  PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo);
+  GtkPrintBackend  *backend = GTK_PRINT_BACKEND (cups_backend);
+  ipp_attribute_t  *attr;
+  AvahiService     *service = (AvahiService *) user_data;
+  GtkPrinter       *printer;
+  gboolean          list_has_changed = FALSE;
+  gboolean          status_changed = FALSE;
+  ipp_t            *response;
+
+  gdk_threads_enter ();
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: %s\n", G_STRFUNC));
+
+  if (gtk_cups_result_is_error (result))
+    {
+      GTK_NOTE (PRINTING,
+                g_warning ("CUPS Backend: Error getting printer info: %s %d %d",
+                           gtk_cups_result_get_error_string (result),
+                           gtk_cups_result_get_error_type (result),
+                           gtk_cups_result_get_error_code (result)));
+
+      goto done;
+    }
+
+  response = gtk_cups_result_get_response (result);
+  attr = ippFirstAttribute (response);
+  while (attr && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
+    attr = ippNextAttribute (response);
+
+  if (attr)
+    {
+      while (attr && ippGetGroupTag (attr) == IPP_TAG_PRINTER)
+        {
+          cups_printer_handle_attribute (cups_backend, attr, info);
+          attr = ippNextAttribute (response);
+        }
+
+      if (info->printer_name && info->printer_uri)
+        {
+          info->avahi_printer = TRUE;
+
+          if (info->got_printer_type &&
+              info->default_printer &&
+              cups_backend->avahi_default_printer == NULL)
+            cups_backend->avahi_default_printer = g_strdup (info->printer_name);
+
+          set_info_state_message (info);
+
+          printer = gtk_print_backend_find_printer (backend, info->printer_name);
+          if (!printer)
+            {
+              printer = cups_create_printer (cups_backend, info);
+              list_has_changed = TRUE;
+            }
+          else
+            {
+              g_object_ref (printer);
+            }
+
+          gtk_printer_set_is_paused (printer, info->is_paused);
+          gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
+
+          if (!gtk_printer_is_active (printer))
+            {
+              gtk_printer_set_is_active (printer, TRUE);
+              gtk_printer_set_is_new (printer, TRUE);
+              list_has_changed = TRUE;
+            }
+
+          GTK_PRINTER_CUPS (printer)->remote = info->remote_printer;
+          GTK_PRINTER_CUPS (printer)->avahi_name = g_strdup (service->name);
+          GTK_PRINTER_CUPS (printer)->avahi_type = g_strdup (service->type);
+          GTK_PRINTER_CUPS (printer)->avahi_domain = g_strdup (service->domain);
+          GTK_PRINTER_CUPS (printer)->hostname = g_strdup (service->host);
+          GTK_PRINTER_CUPS (printer)->port = service->port;
+          GTK_PRINTER_CUPS (printer)->state = info->state;
+          GTK_PRINTER_CUPS (printer)->ipp_version_major = info->ipp_version_major;
+          GTK_PRINTER_CUPS (printer)->ipp_version_minor = info->ipp_version_minor;
+          GTK_PRINTER_CUPS (printer)->supports_copies = info->supports_copies;
+          GTK_PRINTER_CUPS (printer)->supports_collate = info->supports_collate;
+          GTK_PRINTER_CUPS (printer)->supports_number_up = info->supports_number_up;
+          status_changed = gtk_printer_set_job_count (printer, info->job_count);
+          status_changed |= gtk_printer_set_location (printer, info->location);
+          status_changed |= gtk_printer_set_description (printer, info->description);
+          status_changed |= gtk_printer_set_state_message (printer, info->state_msg);
+          status_changed |= gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
+
+          set_printer_icon_name_from_info (printer, info);
+
+          if (gtk_printer_is_new (printer))
+            {
+              g_signal_emit_by_name (backend, "printer-added", printer);
+              gtk_printer_set_is_new (printer, FALSE);
+            }
+
+          if (status_changed)
+            g_signal_emit_by_name (GTK_PRINT_BACKEND (backend),
+                                   "printer-status-changed", printer);
+
+          /* The ref is held by GtkPrintBackend, in add_printer() */
+          g_object_unref (printer);
+        }
+    }
+
+done:
+  if (list_has_changed)
+    g_signal_emit_by_name (backend, "printer-list-changed");
+
+  if (!cups_backend->got_default_printer &&
+      gtk_print_backend_printer_list_is_done (backend) &&
+      cups_backend->avahi_default_printer != NULL)
+    {
+      set_default_printer (cups_backend, cups_backend->avahi_default_printer);
+    }
+
+  g_slice_free (PrinterSetupInfo, info);
+
+  gdk_threads_leave ();
+}
+
+static void
+cups_request_avahi_printer_info (const gchar         *printer_uri,
+                                 const gchar         *host,
+                                 gint                 port,
+                                 const gchar         *name,
+                                 const gchar         *type,
+                                 const gchar         *domain,
+                                 GtkPrintBackendCups *backend)
+{
+  GtkCupsRequest *request;
+  AvahiService   *service;
+  http_t         *http;
+
+  http = httpConnect (host, port);
+  if (http)
+    {
+      service = (AvahiService *) g_new0 (AvahiService, 1);
+      service->name = g_strdup (name);
+      service->type = g_strdup (type);
+      service->domain = g_strdup (domain);
+      service->host = g_strdup (host);
+      service->port = port;
+
+      request = gtk_cups_request_new_with_username (http,
+                                                    GTK_CUPS_POST,
+                                                    IPP_GET_PRINTER_ATTRIBUTES,
+                                                    NULL,
+                                                    NULL,
+                                                    NULL,
+                                                    backend->username);
+
+      gtk_cups_request_set_ipp_version (request, 1, 1);
+
+      gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
+                                       "printer-uri", NULL, printer_uri);
+
+      gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+                                        "requested-attributes", G_N_ELEMENTS (printer_attrs),
+                                        NULL, printer_attrs);
+
+      cups_request_execute (backend,
+                            request,
+                            (GtkPrintCupsResponseCallbackFunc) cups_request_avahi_printer_info_cb,
+                            service,
+                            (GDestroyNotify) avahi_service_free);
+    }
+}
+
+typedef struct
+{
+  gchar               *printer_uri;
+  gchar               *host;
+  gint                 port;
+  gchar               *name;
+  gchar               *type;
+  gchar               *domain;
+  GtkPrintBackendCups *backend;
+} AvahiConnectionTestData;
+
+static void
+avahi_connection_test_cb (GObject      *source_object,
+                          GAsyncResult *res,
+                          gpointer      user_data)
+{
+  AvahiConnectionTestData *data = (AvahiConnectionTestData *) user_data;
+  GSocketConnection       *connection;
+
+  connection = g_socket_client_connect_to_host_finish (G_SOCKET_CLIENT (source_object),
+                                                       res,
+                                                       NULL);
+  g_object_unref (source_object);
+
+  if (connection != NULL)
+    {
+      g_io_stream_close (G_IO_STREAM (connection), NULL, NULL);
+      g_object_unref (connection);
+
+      cups_request_avahi_printer_info (data->printer_uri,
+                                       data->host,
+                                       data->port,
+                                       data->name,
+                                       data->type,
+                                       data->domain,
+                                       data->backend);
+    }
+
+  g_free (data->printer_uri);
+  g_free (data->host);
+  g_free (data->name);
+  g_free (data->type);
+  g_free (data->domain);
+  g_free (data);
+}
+
+static void
+avahi_service_resolver_cb (GObject      *source_object,
+                           GAsyncResult *res,
+                           gpointer      user_data)
+{
+  AvahiConnectionTestData *data;
+  GtkPrintBackendCups     *backend;
+  const gchar             *name;
+  const gchar             *host;
+  const gchar             *type;
+  const gchar             *domain;
+  const gchar             *address;
+  const gchar             *protocol_string;
+  GVariant                *output;
+  GVariant                *txt;
+  GVariant                *child;
+  guint32                  flags;
+  guint16                  port;
+  GError                  *error = NULL;
+  gchar                   *suffix = NULL;
+  gchar                   *tmp;
+  gint                     interface;
+  gint                     protocol;
+  gint                     aprotocol;
+  gint                     i, j;
+
+  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+                                          res,
+                                          &error);
+  if (output)
+    {
+      backend = GTK_PRINT_BACKEND_CUPS (user_data);
+
+      g_variant_get (output, "(ii&s&s&s&si&sq aayu)",
+                     &interface,
+                     &protocol,
+                     &name,
+                     &type,
+                     &domain,
+                     &host,
+                     &aprotocol,
+                     &address,
+                     &port,
+                     &txt,
+                     &flags);
+
+      for (i = 0; i < g_variant_n_children (txt); i++)
+        {
+          child = g_variant_get_child_value (txt, i);
+
+          tmp = g_new0 (gchar, g_variant_n_children (child) + 1);
+          for (j = 0; j < g_variant_n_children (child); j++)
+            {
+              tmp[j] = g_variant_get_byte (g_variant_get_child_value (child, j));
+            }
+
+          if (g_str_has_prefix (tmp, "rp="))
+            {
+              suffix = g_strdup (tmp + 3);
+              g_free (tmp);
+              break;
+            }
+
+          g_free (tmp);
+        }
+
+      if (suffix)
+        {
+          if (g_strcmp0 (type, "_ipp._tcp") == 0)
+            protocol_string = "ipp";
+          else
+            protocol_string = "ipps";
+
+          data = g_new0 (AvahiConnectionTestData, 1);
+
+          if (aprotocol == AVAHI_PROTO_INET6)
+            data->printer_uri = g_strdup_printf ("%s://[%s]:%u/%s", protocol_string, address, port, suffix);
+          else
+            data->printer_uri = g_strdup_printf ("%s://%s:%u/%s", protocol_string, address, port, suffix);
+
+          data->host = g_strdup (address);
+          data->port = port;
+          data->name = g_strdup (name);
+          data->type = g_strdup (type);
+          data->domain = g_strdup (domain);
+          data->backend = backend;
+
+          /* It can happen that the address is not reachable */
+          g_socket_client_connect_to_host_async (g_socket_client_new (),
+                                                 address,
+                                                 port,
+                                                 backend->avahi_cancellable,
+                                                 avahi_connection_test_cb,
+                                                 data);
+          g_free (suffix);
+        }
+
+      g_variant_unref (output);
+    }
+  else
+    {
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        g_warning ("%s", error->message);
+      g_error_free (error);
+    }
+}
+
+static void
+avahi_service_browser_signal_handler (GDBusConnection *connection,
+                                      const gchar     *sender_name,
+                                      const gchar     *object_path,
+                                      const gchar     *interface_name,
+                                      const gchar     *signal_name,
+                                      GVariant        *parameters,
+                                      gpointer         user_data)
+{
+  GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
+  gchar               *name;
+  gchar               *type;
+  gchar               *domain;
+  guint                flags;
+  gint                 interface;
+  gint                 protocol;
+
+  if (g_strcmp0 (signal_name, "ItemNew") == 0)
+    {
+      g_variant_get (parameters, "(ii&s&s&su)",
+                     &interface,
+                     &protocol,
+                     &name,
+                     &type,
+                     &domain,
+                     &flags);
+
+      if (g_strcmp0 (type, "_ipp._tcp") == 0 ||
+          g_strcmp0 (type, "_ipps._tcp") == 0)
+        {
+          g_dbus_connection_call (backend->dbus_connection,
+                                  AVAHI_BUS,
+                                  "/",
+                                  AVAHI_SERVER_IFACE,
+                                  "ResolveService",
+                                  g_variant_new ("(iisssiu)",
+                                                 interface,
+                                                 protocol,
+                                                 name,
+                                                 type,
+                                                 domain,
+                                                 AVAHI_PROTO_UNSPEC,
+                                                 0),
+                                  G_VARIANT_TYPE ("(iissssisqaayu)"),
+                                  G_DBUS_CALL_FLAGS_NONE,
+                                  -1,
+                                  backend->avahi_cancellable,
+                                  avahi_service_resolver_cb,
+                                  user_data);
+        }
+    }
+  else if (g_strcmp0 (signal_name, "ItemRemove") == 0)
+    {
+      GtkPrinterCups *printer;
+      GList          *list;
+      GList          *iter;
+
+      g_variant_get (parameters, "(ii&s&s&su)",
+                     &interface,
+                     &protocol,
+                     &name,
+                     &type,
+                     &domain,
+                     &flags);
+
+      if (g_strcmp0 (type, "_ipp._tcp") == 0 ||
+          g_strcmp0 (type, "_ipps._tcp") == 0)
+        {
+          list = gtk_print_backend_get_printer_list (GTK_PRINT_BACKEND (backend));
+          for (iter = list; iter; iter = iter->next)
+            {
+              printer = GTK_PRINTER_CUPS (iter->data);
+              if (g_strcmp0 (printer->avahi_name, name) == 0 &&
+                  g_strcmp0 (printer->avahi_type, type) == 0 &&
+                  g_strcmp0 (printer->avahi_domain, domain) == 0)
+                {
+                  if (g_strcmp0 (gtk_printer_get_name (GTK_PRINTER (printer)),
+                                 backend->avahi_default_printer) == 0)
+                    g_clear_pointer (&backend->avahi_default_printer, g_free);
+
+                  g_signal_emit_by_name (backend, "printer-removed", printer);
+                  gtk_print_backend_remove_printer (GTK_PRINT_BACKEND (backend),
+                                                    GTK_PRINTER (printer));
+                  g_signal_emit_by_name (backend, "printer-list-changed");
+                  break;
+                }
+            }
+        }
+
+      g_list_free (list);
+    }
+}
+
+static void
+avahi_service_browser_new_cb (GObject      *source_object,
+                              GAsyncResult *res,
+                              gpointer      user_data)
+{
+  GtkPrintBackendCups *cups_backend;
+  GVariant            *output;
+  GError              *error = NULL;
+  gint                 i;
+
+  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+                                          res,
+                                          &error);
+  if (output)
+    {
+      cups_backend = GTK_PRINT_BACKEND_CUPS (user_data);
+      i = cups_backend->avahi_service_browser_paths[0] ? 1 : 0;
+
+      g_variant_get (output, "(o)", &cups_backend->avahi_service_browser_paths[i]);
+
+      cups_backend->avahi_service_browser_subscription_ids[i] =
+        g_dbus_connection_signal_subscribe (cups_backend->dbus_connection,
+                                            NULL,
+                                            AVAHI_SERVICE_BROWSER_IFACE,
+                                            NULL,
+                                            cups_backend->avahi_service_browser_paths[i],
+                                            NULL,
+                                            G_DBUS_SIGNAL_FLAGS_NONE,
+                                            avahi_service_browser_signal_handler,
+                                            user_data,
+                                            NULL);
+
+      /*
+       * The general subscription for all service browsers is not needed
+       * now because we are already subscribed to service browsers
+       * specific to _ipp._tcp and _ipps._tcp services.
+       */
+      if (cups_backend->avahi_service_browser_paths[0] &&
+          cups_backend->avahi_service_browser_paths[1] &&
+          cups_backend->avahi_service_browser_subscription_id > 0)
+        {
+          g_dbus_connection_signal_unsubscribe (cups_backend->dbus_connection,
+                                                cups_backend->avahi_service_browser_subscription_id);
+          cups_backend->avahi_service_browser_subscription_id = 0;
+        }
+
+      g_variant_unref (output);
+    }
+  else
+    {
+      /*
+       * The creation of ServiceBrowser fails with G_IO_ERROR_DBUS_ERROR
+       * if Avahi is disabled.
+       */
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR) &&
+          !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        g_warning ("%s", error->message);
+      g_error_free (error);
+    }
+}
+
+static void
+avahi_create_browsers (GObject      *source_object,
+                       GAsyncResult *res,
+                       gpointer      user_data)
+{
+  GDBusConnection     *dbus_connection;
+  GtkPrintBackendCups *cups_backend;
+  GError              *error = NULL;
+
+  dbus_connection = g_bus_get_finish (res, &error);
+  if (!dbus_connection)
+    {
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        g_warning ("Couldn't connect to D-Bus system bus, %s", error->message);
+
+      g_error_free (error);
+      return;
+    }
+
+  cups_backend = GTK_PRINT_BACKEND_CUPS (user_data);
+  cups_backend->dbus_connection = dbus_connection;
+
+  /*
+   * We need to subscribe to signals of service browser before
+   * we actually create it because it starts to emit them right
+   * after its creation.
+   */
+  cups_backend->avahi_service_browser_subscription_id =
+    g_dbus_connection_signal_subscribe  (cups_backend->dbus_connection,
+                                         NULL,
+                                         AVAHI_SERVICE_BROWSER_IFACE,
+                                         NULL,
+                                         NULL,
+                                         NULL,
+                                         G_DBUS_SIGNAL_FLAGS_NONE,
+                                         avahi_service_browser_signal_handler,
+                                         cups_backend,
+                                         NULL);
+
+  /*
+   * Create service browsers for _ipp._tcp and _ipps._tcp services.
+   */
+  g_dbus_connection_call (cups_backend->dbus_connection,
+                          AVAHI_BUS,
+                          "/",
+                          AVAHI_SERVER_IFACE,
+                          "ServiceBrowserNew",
+                          g_variant_new ("(iissu)",
+                                         AVAHI_IF_UNSPEC,
+                                         AVAHI_PROTO_UNSPEC,
+                                         "_ipp._tcp",
+                                         "",
+                                         0),
+                          G_VARIANT_TYPE ("(o)"),
+                          G_DBUS_CALL_FLAGS_NONE,
+                          -1,
+                          cups_backend->avahi_cancellable,
+                          avahi_service_browser_new_cb,
+                          cups_backend);
+
+  g_dbus_connection_call (cups_backend->dbus_connection,
+                          AVAHI_BUS,
+                          "/",
+                          AVAHI_SERVER_IFACE,
+                          "ServiceBrowserNew",
+                          g_variant_new ("(iissu)",
+                                         AVAHI_IF_UNSPEC,
+                                         AVAHI_PROTO_UNSPEC,
+                                         "_ipps._tcp",
+                                         "",
+                                         0),
+                          G_VARIANT_TYPE ("(o)"),
+                          G_DBUS_CALL_FLAGS_NONE,
+                          -1,
+                          cups_backend->avahi_cancellable,
+                          avahi_service_browser_new_cb,
+                          cups_backend);
+}
+
+static void
+avahi_request_printer_list (GtkPrintBackendCups *cups_backend)
+{
+  cups_backend->avahi_cancellable = g_cancellable_new ();
+  g_bus_get (G_BUS_TYPE_SYSTEM, cups_backend->avahi_cancellable, avahi_create_browsers, cups_backend);
+}
+#endif
+
+static void
 cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend,
                               GtkCupsResult       *result,
                               gpointer             user_data)
@@ -1981,6 +2993,7 @@ cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend,
   gboolean list_has_changed;
   GList *removed_printer_checklist;
   gchar *remote_default_printer = NULL;
+  GList *iter;
 
   GDK_THREADS_ENTER ();
 
@@ -2025,7 +3038,6 @@ cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend,
       GtkPrinter *printer;
       gboolean status_changed = FALSE;
       GList *node;
-      gint i;
       PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo);
 
       /* Skip leading attributes until we hit a printer...
@@ -2046,7 +3058,6 @@ cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend,
       GtkPrinter *printer;
       gboolean status_changed = FALSE;
       GList *node;
-      gint i;
       PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo);
 
       /* Skip leading attributes until we hit a printer...
@@ -2137,88 +3148,22 @@ cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend,
 #endif
 
       GTK_PRINTER_CUPS (printer)->state = info->state;
+      GTK_PRINTER_CUPS (printer)->ipp_version_major = info->ipp_version_major;
+      GTK_PRINTER_CUPS (printer)->ipp_version_minor = info->ipp_version_minor;
+      GTK_PRINTER_CUPS (printer)->supports_copies = info->supports_copies;
+      GTK_PRINTER_CUPS (printer)->supports_collate = info->supports_collate;
+      GTK_PRINTER_CUPS (printer)->supports_number_up = info->supports_number_up;
       status_changed = gtk_printer_set_job_count (printer, info->job_count);
       status_changed |= gtk_printer_set_location (printer, info->location);
       status_changed |= gtk_printer_set_description (printer,
                                                     info->description);
 
-      if (info->state_msg != NULL && strlen (info->state_msg) == 0)
-        {
-         gchar *tmp_msg2 = NULL;
-         if (info->is_paused && !info->is_accepting_jobs)
-           /* Translators: this is a printer status. */
-            tmp_msg2 = g_strdup ( N_("Paused ; Rejecting Jobs"));
-          if (info->is_paused && info->is_accepting_jobs)
-           /* Translators: this is a printer status. */
-            tmp_msg2 = g_strdup ( N_("Paused"));
-          if (!info->is_paused && !info->is_accepting_jobs)
-           /* Translators: this is a printer status. */
-            tmp_msg2 = g_strdup ( N_("Rejecting Jobs"));
-
-          if (tmp_msg2 != NULL)
-           {
-             g_free (info->state_msg);
-             info->state_msg = tmp_msg2;
-           }
-       }
-
-      /* Set description of the reason and combine it with printer-state-message. */
-      if ( (info->reason_msg != NULL))
-        {
-         gchar *reason_msg_desc = NULL;
-         gboolean found = FALSE;
-
-          for (i = 0; i < G_N_ELEMENTS (printer_messages); i++)
-            {
-              if (strncmp (info->reason_msg, printer_messages[i],
-                          strlen (printer_messages[i])) == 0)
-                {
-                  reason_msg_desc = g_strdup_printf (printer_strings[i],
-                                                    info->printer_name);
-                  found = TRUE;
-                  break;
-                }
-            }
-
-          if (!found)
-            info->reason_level = GTK_PRINTER_STATE_LEVEL_NONE;
-
-          if (info->reason_level >= GTK_PRINTER_STATE_LEVEL_WARNING)
-            {
-              if (strlen (info->state_msg) == 0)
-                {
-                  g_free (info->state_msg);
-                  info->state_msg = reason_msg_desc;
-                  reason_msg_desc = NULL;
-                }
-              else
-                {
-                 gchar *tmp_msg = NULL;
-                 tmp_msg = g_strjoin (" ; ", info->state_msg,
-                                      reason_msg_desc, NULL);
-                  g_free (info->state_msg);
-                  info->state_msg = tmp_msg;
-                }
-            }
-         if (reason_msg_desc != NULL)
-           g_free (reason_msg_desc);
-        }
+      set_info_state_message (info);
 
       status_changed |= gtk_printer_set_state_message (printer, info->state_msg);
       status_changed |= gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
 
-
-
-      /* Set printer icon according to importance
-         (none, report, warning, error - report is omitted). */
-      if (info->reason_level == 3)
-        gtk_printer_set_icon_name (printer, "gtk-print-error");
-      else if (info->reason_level == 2)
-        gtk_printer_set_icon_name (printer, "gtk-print-warning");
-      else if (gtk_printer_is_paused (printer))
-        gtk_printer_set_icon_name (printer, "gtk-print-paused");
-      else
-        gtk_printer_set_icon_name (printer, "gtk-print");
+      set_printer_icon_name_from_info (printer, info);
 
       if (status_changed)
         g_signal_emit_by_name (GTK_PRINT_BACKEND (backend),
@@ -2237,9 +3182,18 @@ cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend,
      as inactive if it is in the list, emitting a printer_removed signal */
   if (removed_printer_checklist != NULL)
     {
-      g_list_foreach (removed_printer_checklist, (GFunc) mark_printer_inactive, backend);
+      for (iter = removed_printer_checklist; iter; iter = iter->next)
+        {
+#ifdef HAVE_CUPS_API_1_6
+          if (!GTK_PRINTER_CUPS (iter->data)->avahi_browsed)
+#endif
+            {
+              mark_printer_inactive (GTK_PRINTER (iter->data), backend);
+              list_has_changed = TRUE;
+            }
+        }
+
       g_list_free (removed_printer_checklist);
-      list_has_changed = TRUE;
     }
   
 done:
@@ -2250,23 +3204,16 @@ done:
 
   if (!cups_backend->got_default_printer && remote_default_printer != NULL)
     {
-      cups_backend->default_printer = g_strdup (remote_default_printer);
-      cups_backend->got_default_printer = TRUE;
+      set_default_printer (cups_backend, remote_default_printer);
       g_free (remote_default_printer);
+    }
 
-      if (cups_backend->default_printer != NULL)
-        {
-          GtkPrinter *default_printer = NULL;
-          default_printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (cups_backend),
-                                                            cups_backend->default_printer);
-          if (default_printer != NULL)
-            {
-              gtk_printer_set_is_default (default_printer, TRUE);
-              g_signal_emit_by_name (GTK_PRINT_BACKEND (cups_backend),
-                                     "printer-status-changed", default_printer);
-            }
-        }
+#ifdef HAVE_CUPS_API_1_6
+  if (!cups_backend->got_default_printer && cups_backend->avahi_default_printer != NULL)
+    {
+      set_default_printer (cups_backend, cups_backend->avahi_default_printer);
     }
+#endif
 
   GDK_THREADS_LEAVE ();
 }
@@ -2292,23 +3239,6 @@ cups_request_printer_list (GtkPrintBackendCups *cups_backend)
 {
   GtkCupsConnectionState state;
   GtkCupsRequest *request;
-  static const char * const pattrs[] = /* Attributes we're interested in */
-    {
-      "printer-name",
-      "printer-uri-supported",
-      "member-uris",
-      "printer-location",
-      "printer-info",
-      "printer-state-message",
-      "printer-state-reasons",
-      "printer-state",
-      "queued-job-count",
-      "printer-is-accepting-jobs",
-      "job-sheets-supported",
-      "job-sheets-default",
-      "printer-type",
-      "auth-info-required"
-    };
 
   if (cups_backend->reading_ppds > 0 || cups_backend->list_printers_pending)
     return TRUE;
@@ -2345,8 +3275,8 @@ cups_request_printer_list (GtkPrintBackendCups *cups_backend)
                                                 cups_backend->username);
 
   gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
-                                   "requested-attributes", G_N_ELEMENTS (pattrs),
-                                   NULL, pattrs);
+                                   "requested-attributes", G_N_ELEMENTS (printer_attrs),
+                                   NULL, printer_attrs);
 
   cups_request_execute (cups_backend,
                         request,
@@ -2373,6 +3303,10 @@ cups_get_printer_list (GtkPrintBackend *backend)
         cups_backend->list_printers_poll = gdk_threads_add_timeout (50,
                                              (GSourceFunc) cups_request_printer_list,
                                              backend);
+
+#ifdef HAVE_CUPS_API_1_6
+      avahi_request_printer_list (cups_backend);
+#endif
     }
 }
 
@@ -2414,10 +3348,15 @@ cups_request_ppd_cb (GtkPrintBackendCups *print_backend,
     {
       gboolean success = FALSE;
 
-      /* if we get a 404 then it is just a raw printer without a ppd
-         and not an error */
-      if ((gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_HTTP) &&
-          (gtk_cups_result_get_error_status (result) == HTTP_NOT_FOUND))
+      /* If we get a 404 then it is just a raw printer without a ppd
+         and not an error. Standalone Avahi printers also don't have
+         PPD files. */
+      if (((gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_HTTP) &&
+           (gtk_cups_result_get_error_status (result) == HTTP_NOT_FOUND))
+#ifdef HAVE_CUPS_API_1_6
+           || GTK_PRINTER_CUPS (printer)->avahi_browsed
+#endif
+           )
         {
           gtk_printer_set_has_details (printer, TRUE);
           success = TRUE;
@@ -2550,6 +3489,10 @@ cups_request_ppd (GtkPrinter *printer)
                                                 resource,
                                                 GTK_PRINT_BACKEND_CUPS (print_backend)->username);
 
+  gtk_cups_request_set_ipp_version (request,
+                                    cups_printer->ipp_version_major,
+                                    cups_printer->ipp_version_minor);
+
   GTK_NOTE (PRINTING,
             g_print ("CUPS Backend: Requesting resource %s to be written to temp file %s\n", resource, 
ppd_filename));
 
@@ -4501,11 +5444,10 @@ cups_printer_get_settings_from_options (GtkPrinter          *printer,
   data.settings = settings;
   data.ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
  
+  gtk_printer_option_set_foreach (options, foreach_option_get_settings, &data);
   if (data.ppd_file != NULL)
     {
       GtkPrinterOption *cover_before, *cover_after;
-      
-      gtk_printer_option_set_foreach (options, foreach_option_get_settings, &data);
 
       cover_before = gtk_printer_option_set_lookup (options, "gtk-cover-before");
       cover_after = gtk_printer_option_set_lookup (options, "gtk-cover-after");
@@ -4548,7 +5490,9 @@ cups_printer_prepare_for_print (GtkPrinter       *printer,
   GtkPaperSize *paper_size;
   const char *ppd_paper_name;
   double scale;
+  GtkPrintCapabilities  capabilities;
 
+  capabilities = cups_printer_get_capabilities (printer);
   print_job->print_pages = gtk_print_settings_get_print_pages (settings);
   print_job->page_ranges = NULL;
   print_job->num_page_ranges = 0;
@@ -4558,18 +5502,39 @@ cups_printer_prepare_for_print (GtkPrinter       *printer,
       gtk_print_settings_get_page_ranges (settings,
                                          &print_job->num_page_ranges);
   
-  if (gtk_print_settings_get_collate (settings))
-    gtk_print_settings_set (settings, "cups-Collate", "True");
-  print_job->collate = FALSE;
+  if (capabilities & GTK_PRINT_CAPABILITY_COLLATE)
+    {
+      if (gtk_print_settings_get_collate (settings))
+        gtk_print_settings_set (settings, "cups-Collate", "True");
+      print_job->collate = FALSE;
+    }
+  else
+    {
+      print_job->collate = gtk_print_settings_get_collate (settings);
+    }
 
-  if (gtk_print_settings_get_reverse (settings))
-    gtk_print_settings_set (settings, "cups-OutputOrder", "Reverse");
-  print_job->reverse = FALSE;
+  if (capabilities & GTK_PRINT_CAPABILITY_REVERSE)
+    {
+      if (gtk_print_settings_get_reverse (settings))
+        gtk_print_settings_set (settings, "cups-OutputOrder", "Reverse");
+      print_job->reverse = FALSE;
+    }
+  else
+    {
+      print_job->reverse = gtk_print_settings_get_reverse (settings);
+    }
 
-  if (gtk_print_settings_get_n_copies (settings) > 1)
-    gtk_print_settings_set_int (settings, "cups-copies",
-                               gtk_print_settings_get_n_copies (settings));
-  print_job->num_copies = 1;
+  if (capabilities & GTK_PRINT_CAPABILITY_COPIES)
+    {
+      if (gtk_print_settings_get_n_copies (settings) > 1)
+        gtk_print_settings_set_int (settings, "cups-copies",
+                                    gtk_print_settings_get_n_copies (settings));
+      print_job->num_copies = 1;
+    }
+  else
+    {
+      print_job->num_copies = gtk_print_settings_get_n_copies (settings);
+    }
 
   scale = gtk_print_settings_get_scale (settings);
   print_job->scale = 1.0;
@@ -4631,6 +5596,12 @@ cups_printer_prepare_for_print (GtkPrinter       *printer,
       enum_value = g_enum_get_value (enum_class, layout);
       gtk_print_settings_set (settings, "cups-number-up-layout", enum_value->value_nick);
       g_type_class_unref (enum_class);
+
+      if (!(capabilities & GTK_PRINT_CAPABILITY_NUMBER_UP))
+        {
+          print_job->number_up = gtk_print_settings_get_number_up (settings);
+          print_job->number_up_layout = gtk_print_settings_get_number_up_layout (settings);
+        }
     }
 
   print_job->rotate_to_orientation = TRUE;
@@ -4750,12 +5721,31 @@ cups_printer_get_hard_margins (GtkPrinter *printer,
 static GtkPrintCapabilities
 cups_printer_get_capabilities (GtkPrinter *printer)
 {
-  return
-    GTK_PRINT_CAPABILITY_COPIES |
-    GTK_PRINT_CAPABILITY_COLLATE |
-    GTK_PRINT_CAPABILITY_REVERSE |
+  GtkPrintCapabilities  capabilities = 0;
+  GtkPrinterCups       *cups_printer = GTK_PRINTER_CUPS (printer);
+
+  if (gtk_printer_cups_get_ppd (cups_printer))
+    {
+      capabilities = GTK_PRINT_CAPABILITY_REVERSE;
+    }
+
+  if (cups_printer->supports_copies)
+    {
+      capabilities |= GTK_PRINT_CAPABILITY_COPIES;
+    }
+
+  if (cups_printer->supports_collate)
+    {
+      capabilities |= GTK_PRINT_CAPABILITY_COLLATE;
+    }
+
+  if (cups_printer->supports_number_up)
+    {
+      capabilities |= GTK_PRINT_CAPABILITY_NUMBER_UP;
 #if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 1 && CUPS_VERSION_PATCH >= 15) || (CUPS_VERSION_MAJOR 
== 1 && CUPS_VERSION_MINOR > 1) || CUPS_VERSION_MAJOR > 1
-    GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT |
+      capabilities |= GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT;
 #endif
-    GTK_PRINT_CAPABILITY_NUMBER_UP;
+    }
+
+  return capabilities;
 }
diff --git a/modules/printbackends/cups/gtkprintercups.c b/modules/printbackends/cups/gtkprintercups.c
index 8b3f85c..f957558 100644
--- a/modules/printbackends/cups/gtkprintercups.c
+++ b/modules/printbackends/cups/gtkprintercups.c
@@ -82,6 +82,17 @@ gtk_printer_cups_init (GtkPrinterCups *printer)
   printer->get_remote_ppd_attempts = 0;
   printer->remote_cups_connection_test = NULL;
   printer->auth_info_required = NULL;
+#ifdef HAVE_CUPS_API_1_6
+  printer->avahi_browsed = FALSE;
+  printer->avahi_name = NULL;
+  printer->avahi_type = NULL;
+  printer->avahi_domain = NULL;
+#endif
+  printer->ipp_version_major = 1;
+  printer->ipp_version_minor = 1;
+  printer->supports_copies = FALSE;
+  printer->supports_collate = FALSE;
+  printer->supports_number_up = FALSE;
 }
 
 static void
@@ -101,6 +112,12 @@ gtk_printer_cups_finalize (GObject *object)
   g_free (printer->default_cover_after);
   g_strfreev (printer->auth_info_required);
 
+#ifdef HAVE_CUPS_API_1_6
+  g_free (printer->avahi_name);
+  g_free (printer->avahi_type);
+  g_free (printer->avahi_domain);
+#endif
+
   if (printer->ppd_file)
     ppdClose (printer->ppd_file);
 
@@ -128,6 +145,7 @@ gtk_printer_cups_new (const char      *name,
 {
   GObject *result;
   gboolean accepts_pdf;
+  GtkPrinterCups *printer;
 
 #if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 2) || CUPS_VERSION_MAJOR > 1
   accepts_pdf = TRUE;
@@ -142,7 +160,16 @@ gtk_printer_cups_new (const char      *name,
                         "accepts-pdf", accepts_pdf,
                          NULL);
 
-  return (GtkPrinterCups *) result;
+  printer = GTK_PRINTER_CUPS (result);
+
+  /*
+   * IPP version 1.1 has to be supported
+   * by all implementations according to rfc 2911
+   */
+  printer->ipp_version_major = 1;
+  printer->ipp_version_minor = 1;
+
+  return printer;
 }
 
 ppd_file_t *
diff --git a/modules/printbackends/cups/gtkprintercups.h b/modules/printbackends/cups/gtkprintercups.h
index 6868232..d10bd7e 100644
--- a/modules/printbackends/cups/gtkprintercups.h
+++ b/modules/printbackends/cups/gtkprintercups.h
@@ -62,6 +62,17 @@ struct _GtkPrinterCups
   guint get_remote_ppd_poll;
   gint  get_remote_ppd_attempts;
   GtkCupsConnectionTest *remote_cups_connection_test;
+#ifdef HAVE_CUPS_API_1_6
+  gboolean  avahi_browsed;
+  gchar    *avahi_name;
+  gchar    *avahi_type;
+  gchar    *avahi_domain;
+#endif
+  guchar ipp_version_major;
+  guchar ipp_version_minor;
+  gboolean supports_copies;
+  gboolean supports_collate;
+  gboolean supports_number_up;
 };
 
 struct _GtkPrinterCupsClass


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]