From 963e5dd0c80dbb01f795d29fd8ee00d3fcb18a3c Mon Sep 17 00:00:00 2001
From: Marek Kasik <mkasik@redhat.com>
Date: Thu, 20 Aug 2020 13:40:04 +0200
Subject: [PATCH] printing: Use original names for remote CUPS printers

CUPS uses resource paths in the form of "printers/printer_name"
or "classes/class_name" so it is enough to remove the "printers/"
or "classes/" prefix and use the string behind it as a name.

There was recently introduced a wrong check for the prefix.
This commit fixes it in the way it was originally intended.
---
 modules/printbackends/cups/gtkprintbackendcups.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/modules/printbackends/cups/gtkprintbackendcups.c b/modules/printbackends/cups/gtkprintbackendcups.c
index 610d688797..d95d632e0b 100644
--- a/modules/printbackends/cups/gtkprintbackendcups.c
+++ b/modules/printbackends/cups/gtkprintbackendcups.c
@@ -3194,8 +3194,8 @@ avahi_service_resolver_cb (GObject      *source_object,
       if (data->resource_path != NULL)
         {
           if (data->got_printer_type &&
-              (g_strcmp0 (data->resource_path, "printers/") == 0 ||
-               g_strcmp0 (data->resource_path, "classes/") == 0))
+              (g_str_has_prefix (data->resource_path, "printers/") ||
+               g_str_has_prefix (data->resource_path, "classes/")))
             {
               /* This is a CUPS printer advertised via Avahi */
               printer_name = g_strrstr (data->resource_path, "/");
-- 
2.26.2

From d07bfbec1d2b9d196eb661e7b2a6dc24fba9ad47 Mon Sep 17 00:00:00 2001
From: Marek Kasik <mkasik@redhat.com>
Date: Fri, 5 Feb 2021 17:53:08 +0100
Subject: [PATCH] printing: Create temporary queues for Avahi printers

This change extends set of Avahi advertised printers which
works with Gtk's CUPS print backend.

It creates a temporary queue (local printer) for each
Avahi printer in CUPS instead of accessing them directly
(via CUPS library).

This makes some printers work which did not work before and
also gives users more options to change in the print dialog.

This also changes naming of printers to be in accordance with CUPS.
It uses '_' instead of '-' and has hostname appended for CUPS remote
printers.
---
 .../printbackends/cups/gtkprintbackendcups.c  | 296 ++++++++++++++----
 modules/printbackends/cups/gtkprintercups.h   |   4 +-
 2 files changed, 243 insertions(+), 57 deletions(-)

diff --git a/modules/printbackends/cups/gtkprintbackendcups.c b/modules/printbackends/cups/gtkprintbackendcups.c
index d95d632e0b..b1ee0584d0 100644
--- a/modules/printbackends/cups/gtkprintbackendcups.c
+++ b/modules/printbackends/cups/gtkprintbackendcups.c
@@ -154,6 +154,9 @@ struct _GtkPrintBackendCups
   gboolean      secrets_service_available;
   guint         secrets_service_watch_id;
   GCancellable *secrets_service_cancellable;
+
+  GList *temporary_queues_in_construction;
+  GList *temporary_queues_removed;
 };
 
 static GObjectClass *backend_parent_class;
@@ -238,6 +241,11 @@ static void                 secrets_service_vanished_cb             (GDBusConnec
                                                                      const gchar *name,
                                                                      gpointer user_data);
 
+static void                 create_temporary_queue                  (GtkPrintBackendCups *backend,
+                                                                     const gchar         *printer_name,
+                                                                     const gchar         *printer_uri,
+                                                                     const gchar         *device_uri);
+
 static void
 gtk_print_backend_cups_register_type (GTypeModule *module)
 {
@@ -897,6 +905,9 @@ gtk_print_backend_cups_init (GtkPrintBackendCups *backend_cups)
     gtk_cups_secrets_service_watch (secrets_service_appeared_cb,
                                     secrets_service_vanished_cb,
                                     backend_cups);
+
+  backend_cups->temporary_queues_in_construction = NULL;
+  backend_cups->temporary_queues_removed = NULL;
 }
 
 static void
@@ -933,6 +944,12 @@ gtk_print_backend_cups_finalize (GObject *object)
       g_bus_unwatch_name (backend_cups->secrets_service_watch_id);
     }
 
+  g_list_free_full (backend_cups->temporary_queues_in_construction, g_free);
+  backend_cups->temporary_queues_in_construction = NULL;
+
+  g_list_free_full (backend_cups->temporary_queues_removed, g_free);
+  backend_cups->temporary_queues_removed = NULL;
+
   backend_parent_class->finalize (object);
 }
 
@@ -1861,8 +1878,28 @@ static void
 mark_printer_inactive (GtkPrinter      *printer,
                        GtkPrintBackend *backend)
 {
-  gtk_printer_set_is_active (printer, FALSE);
-  g_signal_emit_by_name (backend, "printer-removed", printer);
+  GtkPrinterCups *cups_printer = GTK_PRINTER_CUPS (printer);
+  GList          *iter;
+
+  if (cups_printer->is_temporary)
+    {
+      /* Do not recreate printers which disappeared from Avahi. */
+      iter = g_list_find_custom (GTK_PRINT_BACKEND_CUPS (backend)->temporary_queues_removed,
+                                 gtk_printer_get_name (printer), (GCompareFunc) g_strcmp0);
+      if (iter == NULL)
+        {
+          /* Recreate temporary queue since they are created for 60 seconds only. */
+          create_temporary_queue (GTK_PRINT_BACKEND_CUPS (backend),
+                                  gtk_printer_get_name (printer),
+                                  cups_printer->printer_uri,
+                                  cups_printer->temporary_queue_device_uri);
+        }
+    }
+  else
+    {
+      gtk_printer_set_is_active (printer, FALSE);
+      g_signal_emit_by_name (backend, "printer-removed", printer);
+    }
 }
 
 static gint
@@ -1913,7 +1950,8 @@ static const char * const printer_attrs[] =
     "multiple-document-handling-supported",
     "copies-supported",
     "number-up-supported",
-    "device-uri"
+    "device-uri",
+    "printer-is-temporary"
   };
 
 /* Attributes we're interested in for printers without PPD */
@@ -2010,6 +2048,7 @@ typedef struct
   gchar    *output_bin_default;
   GList    *output_bin_supported;
   gchar    *original_device_uri;
+  gboolean  is_temporary;
 } PrinterSetupInfo;
 
 static void
@@ -2395,6 +2434,13 @@ cups_printer_handle_attribute (GtkPrintBackendCups *cups_backend,
     {
       info->original_device_uri = g_strdup (ippGetString (attr, 0, NULL));
     }
+  else if (strcmp (ippGetName (attr), "printer-is-temporary") == 0)
+    {
+      if (ippGetBoolean (attr, 0) == 1)
+        info->is_temporary = TRUE;
+      else
+        info->is_temporary = FALSE;
+    }
   else
     {
       GTK_NOTE (PRINTING,
@@ -2430,12 +2476,7 @@ cups_create_printer (GtkPrintBackendCups *cups_backend,
   cups_printer = gtk_printer_cups_new (info->printer_name, backend, NULL);
 #endif
 
-  if (info->avahi_printer)
-    {
-      cups_printer->device_uri = g_strdup_printf ("/%s",
-                                                  info->avahi_resource_path);
-    }
-  else
+  if (!info->avahi_printer)
     {
       cups_printer->device_uri = g_strdup_printf ("/printers/%s",
                                                   info->printer_name);
@@ -2799,6 +2840,8 @@ cups_request_printer_info_cb (GtkPrintBackendCups *cups_backend,
           GTK_PRINTER_CUPS (printer)->output_bin_default = info->output_bin_default;
           GTK_PRINTER_CUPS (printer)->output_bin_supported = info->output_bin_supported;
 
+          GTK_PRINTER_CUPS (printer)->is_temporary = info->is_temporary;
+
           gtk_printer_set_has_details (printer, TRUE);
           g_signal_emit_by_name (printer, "details-acquired", TRUE);
 
@@ -2866,8 +2909,10 @@ cups_request_printer_info (GtkPrinterCups *printer)
 typedef struct
 {
   gchar               *printer_uri;
+  gchar               *device_uri;
   gchar               *location;
-  gchar               *host;
+  gchar               *address;
+  gchar               *hostname;
   gint                 port;
   gchar               *printer_name;
   gchar               *name;
@@ -2924,6 +2969,88 @@ find_printer_by_uuid (GtkPrintBackendCups *backend,
   return result;
 }
 
+static void
+cups_create_local_printer_cb (GtkPrintBackendCups *print_backend,
+                              GtkCupsResult       *result,
+                              gpointer             user_data)
+{
+  ipp_attribute_t *attr;
+  gchar           *printer_name = NULL;
+  ipp_t           *response;
+  GList           *iter;
+
+  response = gtk_cups_result_get_response (result);
+
+  if (ippGetStatusCode (response) <= IPP_OK_CONFLICT)
+    {
+      if ((attr = ippFindAttribute (response, "printer-uri-supported", IPP_TAG_URI)) != NULL)
+        {
+          printer_name = g_strdup (g_strrstr (ippGetString (attr, 0, NULL), "/") + 1);
+        }
+
+      GTK_NOTE (PRINTING,
+                g_print ("CUPS Backend: Created local printer %s\n", printer_name));
+    }
+  else
+    {
+      GTK_NOTE (PRINTING,
+                g_print ("CUPS Backend: Creating of local printer failed: %d\n", ippGetStatusCode (response)));
+    }
+
+  iter = g_list_find_custom (print_backend->temporary_queues_in_construction, printer_name, (GCompareFunc) g_strcmp0);
+  if (iter != NULL)
+    {
+      g_free (iter->data);
+      print_backend->temporary_queues_in_construction = g_list_delete_link (print_backend->temporary_queues_in_construction, iter);
+    }
+
+  g_free (printer_name);
+}
+
+/*
+ *  Create CUPS temporary queue.
+ */
+static void
+create_temporary_queue (GtkPrintBackendCups *backend,
+                        const gchar         *printer_name,
+                        const gchar         *printer_uri,
+                        const gchar         *device_uri)
+{
+  GtkCupsRequest *request;
+  GList          *iter;
+
+  /* There can be several queues with the same name (ipp and ipps versions of the same printer) */
+  iter = g_list_find_custom (backend->temporary_queues_in_construction, printer_name, (GCompareFunc) g_strcmp0);
+  if (iter != NULL)
+    return;
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: Creating local printer %s\n", printer_name));
+
+  backend->temporary_queues_in_construction = g_list_prepend (backend->temporary_queues_in_construction, g_strdup (printer_name));
+
+  request = gtk_cups_request_new_with_username (NULL,
+                                                GTK_CUPS_POST,
+                                                IPP_OP_CUPS_CREATE_LOCAL_PRINTER,
+                                                NULL,
+                                                NULL,
+                                                NULL,
+                                                NULL);
+
+  gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
+                                   "printer-uri", NULL, printer_uri);
+  gtk_cups_request_ipp_add_string (request, IPP_TAG_PRINTER, IPP_TAG_NAME,
+                                   "printer-name", NULL, printer_name);
+  gtk_cups_request_ipp_add_string (request, IPP_TAG_PRINTER, IPP_TAG_URI,
+                                   "device-uri", NULL, device_uri);
+
+  cups_request_execute (backend,
+                        request,
+                        (GtkPrintCupsResponseCallbackFunc) cups_create_local_printer_cb,
+                        NULL,
+                        NULL);
+}
+
 /*
  *  Create new GtkPrinter from informations included in TXT records.
  */
@@ -2933,6 +3060,15 @@ create_cups_printer_from_avahi_data (AvahiConnectionTestData *data)
   PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo);
   GtkPrinter       *printer;
 
+  printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (data->backend), data->printer_name);
+  if (printer != NULL)
+    {
+      /* A printer with this name is already present in this backend. It is probably the same printer
+       * on another protocol (IPv4 vs IPv6).
+       */
+      return;
+    }
+
   info->avahi_printer = TRUE;
   info->printer_name = data->printer_name;
   info->printer_uri = data->printer_uri;
@@ -2997,8 +3133,9 @@ create_cups_printer_from_avahi_data (AvahiConnectionTestData *data)
       GTK_PRINTER_CUPS (printer)->avahi_type = g_strdup (data->type);
       GTK_PRINTER_CUPS (printer)->avahi_domain = g_strdup (data->domain);
       GTK_PRINTER_CUPS (printer)->printer_uri = g_strdup (data->printer_uri);
+      GTK_PRINTER_CUPS (printer)->temporary_queue_device_uri = g_strdup (data->device_uri);
       g_free (GTK_PRINTER_CUPS (printer)->hostname);
-      GTK_PRINTER_CUPS (printer)->hostname = g_strdup (data->host);
+      GTK_PRINTER_CUPS (printer)->hostname = g_strdup (data->hostname);
       GTK_PRINTER_CUPS (printer)->port = data->port;
       gtk_printer_set_location (printer, data->location);
       gtk_printer_set_state_message (printer, info->state_msg);
@@ -3031,10 +3168,11 @@ avahi_connection_test_cb (GObject      *source_object,
 {
   AvahiConnectionTestData *data = (AvahiConnectionTestData *) user_data;
   GSocketConnection       *connection;
+  GError                  *error = NULL;
 
   connection = g_socket_client_connect_to_host_finish (G_SOCKET_CLIENT (source_object),
                                                        res,
-                                                       NULL);
+                                                       &error);
   g_object_unref (source_object);
 
   if (connection != NULL)
@@ -3044,15 +3182,25 @@ avahi_connection_test_cb (GObject      *source_object,
 
       create_cups_printer_from_avahi_data (data);
     }
+  else
+    {
+      GTK_NOTE (PRINTING,
+                g_warning ("CUPS Backend: Can not connect to %s: %s\n",
+                           data->address,
+                           error->message));
+      g_error_free (error);
+    }
 
   g_free (data->printer_uri);
   g_free (data->location);
-  g_free (data->host);
+  g_free (data->address);
+  g_free (data->hostname);
   g_free (data->printer_name);
   g_free (data->name);
   g_free (data->resource_path);
   g_free (data->type);
   g_free (data->domain);
+  g_free (data->device_uri);
   g_free (data);
 }
 
@@ -3091,17 +3239,17 @@ avahi_service_resolver_cb (GObject      *source_object,
   AvahiConnectionTestData *data;
   GtkPrintBackendCups     *backend;
   const gchar             *name;
-  const gchar             *host;
+  const gchar             *hostname;
   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;
+  GList                   *iter;
   gchar                   *tmp;
   gchar                   *printer_name;
   gchar                  **printer_name_strv;
@@ -3128,7 +3276,7 @@ avahi_service_resolver_cb (GObject      *source_object,
                      &name,
                      &type,
                      &domain,
-                     &host,
+                     &hostname,
                      &aprotocol,
                      &address,
                      &port,
@@ -3193,51 +3341,50 @@ avahi_service_resolver_cb (GObject      *source_object,
 
       if (data->resource_path != NULL)
         {
-          if (data->got_printer_type &&
-              (g_str_has_prefix (data->resource_path, "printers/") ||
-               g_str_has_prefix (data->resource_path, "classes/")))
+          /*
+           * Create name of temporary queue from the name of the discovered service.
+           * This emulates the way how CUPS creates the name.
+           */
+          printer_name = g_strdup_printf ("%s", name);
+          g_strcanon (printer_name, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", '_');
+
+          printer_name_strv = g_strsplit_set (printer_name, "_", -1);
+          printer_name_compressed_strv = g_new0 (gchar *, g_strv_length (printer_name_strv) + 1);
+          for (i = 0, j = 0; printer_name_strv[i] != NULL; i++)
             {
-              /* This is a CUPS printer advertised via Avahi */
-              printer_name = g_strrstr (data->resource_path, "/");
-              if (printer_name != NULL && printer_name[0] != '\0')
-                data->printer_name = g_strdup (printer_name + 1);
-              else
-                data->printer_name = g_strdup (data->resource_path);
-            }
-          else
-            {
-              printer_name = g_strdup (name);
-              g_strcanon (printer_name, PRINTER_NAME_ALLOWED_CHARACTERS, '-');
-
-              printer_name_strv = g_strsplit_set (printer_name, "-", -1);
-              printer_name_compressed_strv = g_new0 (gchar *, g_strv_length (printer_name_strv) + 1);
-              for (i = 0, j = 0; printer_name_strv[i] != NULL; i++)
+              if (printer_name_strv[i][0] != '\0')
                 {
-                  if (printer_name_strv[i][0] != '\0')
-                    {
-                      printer_name_compressed_strv[j] = printer_name_strv[i];
-                      j++;
-                    }
+                  printer_name_compressed_strv[j] = printer_name_strv[i];
+                  j++;
                 }
+            }
+
+          data->printer_name = g_strjoinv ("_", printer_name_compressed_strv);
 
-              data->printer_name = g_strjoinv ("-", printer_name_compressed_strv);
+          g_strfreev (printer_name_strv);
+          g_free (printer_name_compressed_strv);
+          g_free (printer_name);
 
-              g_strfreev (printer_name_strv);
-              g_free (printer_name_compressed_strv);
-              g_free (printer_name);
+          iter = g_list_find_custom (backend->temporary_queues_removed, data->printer_name, (GCompareFunc) g_strcmp0);
+          if (iter != NULL)
+            {
+              g_free (iter->data);
+              backend->temporary_queues_removed = g_list_delete_link (backend->temporary_queues_removed, iter);
             }
 
           if (g_strcmp0 (type, "_ipp._tcp") == 0)
-            protocol_string = "ipp";
-          else
-            protocol_string = "ipps";
-
-          if (aprotocol == AVAHI_PROTO_INET6)
-            data->printer_uri = g_strdup_printf ("%s://[%s]:%u/%s", protocol_string, address, port, data->resource_path);
+            {
+              data->printer_uri = g_strdup_printf ("ipp://localhost/printers/%s", data->printer_name);
+              data->device_uri = g_strdup_printf ("ipp://%s:%d/%s", hostname, port, data->resource_path);
+            }
           else
-            data->printer_uri = g_strdup_printf ("%s://%s:%u/%s", protocol_string, address, port, data->resource_path);
+            {
+              data->printer_uri = g_strdup_printf ("ipps://localhost/printers/%s", data->printer_name);
+              data->device_uri = g_strdup_printf ("ipps://%s:%d/%s", hostname, port, data->resource_path);
+            }
 
-          data->host = g_strdup (address);
+          data->address = g_strdup (address);
+          data->hostname = g_strdup (hostname);
           data->port = port;
 
           data->name = g_strdup (name);
@@ -3351,6 +3498,9 @@ avahi_service_browser_signal_handler (GDBusConnection *connection,
                                  backend->avahi_default_printer) == 0)
                     g_clear_pointer (&backend->avahi_default_printer, g_free);
 
+                  backend->temporary_queues_removed = g_list_prepend (backend->temporary_queues_removed,
+                    g_strdup (gtk_printer_get_name (GTK_PRINTER (printer))));
+
                   g_signal_emit_by_name (backend, "printer-removed", printer);
                   gtk_print_backend_remove_printer (GTK_PRINT_BACKEND (backend),
                                                     GTK_PRINTER (printer));
@@ -3463,7 +3613,6 @@ avahi_create_browsers (GObject      *source_object,
                                          avahi_service_browser_signal_handler,
                                          cups_backend,
                                          NULL);
-
   /*
    * Create service browsers for _ipp._tcp and _ipps._tcp services.
    */
@@ -3591,6 +3740,12 @@ cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend,
             continue;
         }
 
+      /* Do not show printer for queue which was removed from Avahi. */
+      iter = g_list_find_custom (GTK_PRINT_BACKEND_CUPS (backend)->temporary_queues_removed,
+                                 info->printer_name, (GCompareFunc) g_strcmp0);
+      if (iter != NULL)
+        continue;
+
       if (info->got_printer_type)
         {
           if (info->default_printer && !cups_backend->got_default_printer)
@@ -3626,7 +3781,24 @@ cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend,
 	  printer = cups_create_printer (cups_backend, info);
 	  list_has_changed = TRUE;
 	}
-
+      else if (GTK_PRINTER_CUPS (printer)->avahi_browsed && info->is_temporary)
+        {
+          /*
+           * A temporary queue was created for a printer found via Avahi.
+           * We modify the placeholder GtkPrinter to point to the temporary queue
+           * instead of removing the placeholder GtkPrinter and creating new GtkPrinter.
+           */
+
+          g_object_ref (printer);
+
+          GTK_PRINTER_CUPS (printer)->avahi_browsed = FALSE;
+          GTK_PRINTER_CUPS (printer)->is_temporary = TRUE;
+          g_free (GTK_PRINTER_CUPS (printer)->device_uri);
+          GTK_PRINTER_CUPS (printer)->device_uri = g_strdup_printf ("/printers/%s",
+                                                                    info->printer_name);
+          gtk_printer_set_has_details (printer, FALSE);
+          cups_printer_request_details (printer);
+        }
       else
 	g_object_ref (printer);
 
@@ -3657,6 +3829,7 @@ cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend,
       GTK_PRINTER_CUPS (printer)->supports_number_up = info->supports_number_up;
       GTK_PRINTER_CUPS (printer)->number_of_covers = info->number_of_covers;
       GTK_PRINTER_CUPS (printer)->covers = g_strdupv (info->covers);
+      GTK_PRINTER_CUPS (printer)->is_temporary = info->is_temporary;
       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,
@@ -3979,7 +4152,10 @@ cups_request_ppd (GtkPrinter *printer)
     }
   else
     {
-      hostname = cups_printer->hostname;
+      if (cups_printer->is_temporary)
+        hostname = cupsServer ();
+      else
+        hostname = cups_printer->hostname;
       port = cups_printer->port;
       resource = g_strdup_printf ("/printers/%s.ppd",
                                   gtk_printer_cups_get_ppd_name (GTK_PRINTER_CUPS (printer)));
@@ -4352,8 +4528,16 @@ cups_printer_request_details (GtkPrinter *printer)
   GtkPrinterCups *cups_printer;
 
   cups_printer = GTK_PRINTER_CUPS (printer);
-  if (!cups_printer->reading_ppd &&
-      gtk_printer_cups_get_ppd (cups_printer) == NULL)
+
+  if (cups_printer->avahi_browsed)
+    {
+      create_temporary_queue (GTK_PRINT_BACKEND_CUPS (gtk_printer_get_backend (printer)),
+                              gtk_printer_get_name (printer),
+                              cups_printer->printer_uri,
+                              cups_printer->temporary_queue_device_uri);
+    }
+  else if (!cups_printer->reading_ppd &&
+           gtk_printer_cups_get_ppd (cups_printer) == NULL)
     {
       if (cups_printer->remote && !cups_printer->avahi_browsed)
         {
diff --git a/modules/printbackends/cups/gtkprintercups.h b/modules/printbackends/cups/gtkprintercups.h
index bdc30ca4f3..c8ad83fcb9 100644
--- a/modules/printbackends/cups/gtkprintercups.h
+++ b/modules/printbackends/cups/gtkprintercups.h
@@ -56,7 +56,9 @@ struct _GtkPrinterCups
   gchar *original_hostname;
   gchar *original_resource;
   gint original_port;
-  gboolean request_original_uri; /* Request PPD from original hostname */
+  gboolean request_original_uri;     /* Request PPD from original hostname */
+  gboolean is_temporary;             /* This printer is temporary queue */
+  gchar *temporary_queue_device_uri; /* Device uri of temporary queue for this printer */
 
   ipp_pstate_t state;
   gboolean reading_ppd;
-- 
2.26.2

From de3e97309f91fc631a1a4b5ab48cd69dd1421ed5 Mon Sep 17 00:00:00 2001
From: Marek Kasik <mkasik@redhat.com>
Date: Fri, 5 Feb 2021 18:00:02 +0100
Subject: [PATCH] printing: Show all Avahi advertised printers

This commit unsubscribes CUPS backend from a DBus
signal in idle when listening for new items on Avahi.

Since GDBus emits gathered signals in idle while
checking whether the signal has been unsubscribed
it could happen that a signal was not processed
because it was removed from hash table of
subscribed signals.
This caused the situation where printers advertised
on Avahi were not listed in CUPS backend sometimes.

We need those signals since this happens when switching
from a general subscription which listens to signals
for all Avahi services to a specific one which listens
to just _ipp._tcp and _ipps._tcp (chicken and egg problem).
---
 .../printbackends/cups/gtkprintbackendcups.c  | 27 ++++++++++++++++---
 1 file changed, 24 insertions(+), 3 deletions(-)

diff --git a/modules/printbackends/cups/gtkprintbackendcups.c b/modules/printbackends/cups/gtkprintbackendcups.c
index b1ee0584d0..6b8c9e02ad 100644
--- a/modules/printbackends/cups/gtkprintbackendcups.c
+++ b/modules/printbackends/cups/gtkprintbackendcups.c
@@ -150,6 +150,7 @@ struct _GtkPrintBackendCups
   guint avahi_service_browser_subscription_ids[2];
   char *avahi_service_browser_paths[2];
   GCancellable *avahi_cancellable;
+  guint unsubscribe_general_subscription_id;
 
   gboolean      secrets_service_available;
   guint         secrets_service_watch_id;
@@ -1009,6 +1010,12 @@ gtk_print_backend_cups_dispose (GObject *object)
       backend_cups->avahi_service_browser_subscription_id = 0;
     }
 
+  if (backend_cups->unsubscribe_general_subscription_id > 0)
+    {
+      g_source_remove (backend_cups->unsubscribe_general_subscription_id);
+      backend_cups->unsubscribe_general_subscription_id = 0;
+    }
+
   backend_parent_class->dispose (object);
 }
 
@@ -3514,6 +3521,19 @@ avahi_service_browser_signal_handler (GDBusConnection *connection,
     }
 }
 
+static gboolean
+unsubscribe_general_subscription_cb (gpointer user_data)
+{
+  GtkPrintBackendCups *cups_backend = user_data;
+
+  g_dbus_connection_signal_unsubscribe (cups_backend->dbus_connection,
+                                        cups_backend->avahi_service_browser_subscription_id);
+  cups_backend->avahi_service_browser_subscription_id = 0;
+  cups_backend->unsubscribe_general_subscription_id = 0;
+
+  return G_SOURCE_REMOVE;
+}
+
 static void
 avahi_service_browser_new_cb (GObject      *source_object,
                               GAsyncResult *res,
@@ -3555,9 +3575,10 @@ avahi_service_browser_new_cb (GObject      *source_object,
           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;
+          /* We need to unsubscribe in idle since signals in queue destined for emit
+           * are emitted in idle and check whether the subscriber is still subscribed.
+           */
+          cups_backend->unsubscribe_general_subscription_id = g_idle_add (unsubscribe_general_subscription_cb, cups_backend);
         }
 
       g_variant_unref (output);
-- 
2.26.2

