[gtk+] Use secrets service for cups auth_info



commit 382d68ff8e5701a10aa8d4f879c46c63c21e075f
Author: Andre Heinecke <aheinecke intevation de>
Date:   Tue Aug 19 17:55:47 2014 +0200

    Use secrets service for cups auth_info
    
        When a printer requires auth_info (e.g. a printer connected
        over the samba protocol) it is now possible to save the
        credentials necessary for printing if a secrets service
        is available over dbus.
        The auth_info is then stored / loaded from the default
        collection of that secrets service.
        If no such service is available the user is not shown
        the option to remember the password and the behavior
        remains the same as before.
    
        https://bugzilla.gnome.org/show_bug.cgi?id=674264

 gtk/gtkmarshalers.list                           |    1 -
 gtk/gtkprintbackend.c                            |   46 +-
 gtk/gtkprintbackend.h                            |    9 +-
 modules/printbackends/cups/Makefile.am           |    6 +-
 modules/printbackends/cups/gtkcupssecretsutils.c | 1044 ++++++++++++++++++++++
 modules/printbackends/cups/gtkcupssecretsutils.h |   41 +
 modules/printbackends/cups/gtkprintbackendcups.c |  169 ++++-
 7 files changed, 1292 insertions(+), 24 deletions(-)
---
diff --git a/gtk/gtkmarshalers.list b/gtk/gtkmarshalers.list
index ab6983d..c6f12a3 100644
--- a/gtk/gtkmarshalers.list
+++ b/gtk/gtkmarshalers.list
@@ -124,7 +124,6 @@ VOID:UINT,STRING,UINT
 VOID:UINT,UINT
 VOID:VOID
 OBJECT:OBJECT,INT,INT
-VOID:POINTER,POINTER,POINTER,POINTER,STRING
 VOID:OBJECT,STRING,POINTER,POINTER
 INT:INT
 VOID:POINTER,STRING,INT
diff --git a/gtk/gtkprintbackend.c b/gtk/gtkprintbackend.c
index 3de625f..31a181b 100644
--- a/gtk/gtkprintbackend.c
+++ b/gtk/gtkprintbackend.c
@@ -47,6 +47,7 @@ struct _GtkPrintBackendPrivate
   GtkPrintBackendStatus status;
   char **auth_info_required;
   char **auth_info;
+  gboolean store_auth_info;
 };
 
 enum {
@@ -360,7 +361,8 @@ static void                 request_password                       (GtkPrintBack
                                                                     gpointer             auth_info_default,
                                                                     gpointer             auth_info_display,
                                                                     gpointer             auth_info_visible,
-                                                                    const gchar         *prompt);
+                                                                    const gchar         *prompt,
+                                                                    gboolean             
can_store_auth_info);
   
 static void
 gtk_print_backend_class_init (GtkPrintBackendClass *class)
@@ -437,9 +439,9 @@ gtk_print_backend_class_init (GtkPrintBackendClass *class)
                  G_TYPE_FROM_CLASS (class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkPrintBackendClass, request_password),
-                 NULL, NULL,
-                 _gtk_marshal_VOID__POINTER_POINTER_POINTER_POINTER_STRING,
-                 G_TYPE_NONE, 5, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER, 
G_TYPE_STRING);
+                 NULL, NULL, NULL,
+                 G_TYPE_NONE, 6, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER,
+                 G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_BOOLEAN);
 }
 
 static void
@@ -666,12 +668,24 @@ gtk_print_backend_print_stream (GtkPrintBackend        *backend,
 void 
 gtk_print_backend_set_password (GtkPrintBackend  *backend,
                                 gchar           **auth_info_required,
-                                gchar           **auth_info)
+                                gchar           **auth_info,
+                                gboolean          store_auth_info)
 {
   g_return_if_fail (GTK_IS_PRINT_BACKEND (backend));
 
   if (GTK_PRINT_BACKEND_GET_CLASS (backend)->set_password)
-    GTK_PRINT_BACKEND_GET_CLASS (backend)->set_password (backend, auth_info_required, auth_info);
+    GTK_PRINT_BACKEND_GET_CLASS (backend)->set_password (backend,
+                                                         auth_info_required,
+                                                         auth_info,
+                                                         store_auth_info);
+}
+
+static void
+store_auth_info_toggled (GtkCheckButton *chkbtn,
+                         gpointer        user_data)
+{
+  gboolean *data = (gboolean *) user_data;
+  *data = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (chkbtn));
 }
 
 static void
@@ -698,9 +712,9 @@ password_dialog_response (GtkWidget       *dialog,
   gint i;
 
   if (response_id == GTK_RESPONSE_OK)
-    gtk_print_backend_set_password (backend, priv->auth_info_required, priv->auth_info);
+    gtk_print_backend_set_password (backend, priv->auth_info_required, priv->auth_info, 
priv->store_auth_info);
   else
-    gtk_print_backend_set_password (backend, priv->auth_info_required, NULL);
+    gtk_print_backend_set_password (backend, priv->auth_info_required, NULL, FALSE);
 
   for (i = 0; i < g_strv_length (priv->auth_info_required); i++)
     if (priv->auth_info[i] != NULL)
@@ -725,10 +739,11 @@ request_password (GtkPrintBackend  *backend,
                   gpointer          auth_info_default,
                   gpointer          auth_info_display,
                   gpointer          auth_info_visible,
-                  const gchar      *prompt)
+                  const gchar      *prompt,
+                  gboolean          can_store_auth_info)
 {
   GtkPrintBackendPrivate *priv = backend->priv;
-  GtkWidget *dialog, *box, *main_box, *label, *icon, *vbox, *entry;
+  GtkWidget *dialog, *box, *main_box, *label, *icon, *vbox, *entry, *chkbtn;
   GtkWidget *focus = NULL;
   GtkWidget *content_area;
   gchar     *markup;
@@ -742,6 +757,7 @@ request_password (GtkPrintBackend  *backend,
   priv->auth_info_required = g_strdupv (ai_required);
   length = g_strv_length (ai_required);
   priv->auth_info = g_new0 (gchar *, length);
+  priv->store_auth_info = FALSE;
 
   dialog = gtk_dialog_new_with_buttons ( _("Authentication"), NULL, GTK_DIALOG_MODAL, 
                                          _("_Cancel"), GTK_RESPONSE_CANCEL,
@@ -812,6 +828,16 @@ request_password (GtkPrintBackend  *backend,
         }
     }
 
+  if (can_store_auth_info)
+    {
+      chkbtn = gtk_check_button_new_with_mnemonic (_("_Remember password"));
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (chkbtn), FALSE);
+      gtk_box_pack_start (GTK_BOX (vbox), chkbtn, FALSE, FALSE, 6);
+      g_signal_connect (chkbtn, "toggled",
+                        G_CALLBACK (store_auth_info_toggled),
+                        &(priv->store_auth_info));
+    }
+
   if (focus != NULL)
     {
       gtk_widget_grab_focus (focus);
diff --git a/gtk/gtkprintbackend.h b/gtk/gtkprintbackend.h
index 74bd291..f4524e8 100644
--- a/gtk/gtkprintbackend.h
+++ b/gtk/gtkprintbackend.h
@@ -124,12 +124,14 @@ struct _GtkPrintBackendClass
                                                               gpointer             auth_info_default,
                                                               gpointer             auth_info_display,
                                                               gpointer             auth_info_visible,
-                                                              const gchar         *prompt);
+                                                              const gchar         *prompt,
+                                                              gboolean             can_store_auth_info);
 
   /* not a signal */
   void                  (*set_password)                      (GtkPrintBackend     *backend,
                                                               gchar              **auth_info_required,
-                                                              gchar              **auth_info);
+                                                              gchar              **auth_info,
+                                                              gboolean             store_auth_info);
 
   /* Padding for future expansion */
   void (*_gtk_reserved1) (void);
@@ -162,7 +164,8 @@ void        gtk_print_backend_destroy              (GtkPrintBackend         *pri
 GDK_AVAILABLE_IN_ALL
 void        gtk_print_backend_set_password         (GtkPrintBackend         *backend, 
                                                     gchar                  **auth_info_required,
-                                                    gchar                  **auth_info);
+                                                    gchar                  **auth_info,
+                                                    gboolean                 can_store_auth_info);
 
 /* Backend-only functions for GtkPrintBackend */
 
diff --git a/modules/printbackends/cups/Makefile.am b/modules/printbackends/cups/Makefile.am
index e9d4c2f..33520be 100644
--- a/modules/printbackends/cups/Makefile.am
+++ b/modules/printbackends/cups/Makefile.am
@@ -29,12 +29,14 @@ backend_LTLIBRARIES = libprintbackend-cups.la
 libprintbackend_cups_la_SOURCES =      \
        gtkprintbackendcups.c           \
        gtkprintercups.c                \
-       gtkcupsutils.c
+       gtkcupsutils.c                  \
+       gtkcupssecretsutils.c
 
 noinst_HEADERS =                       \
        gtkprintbackendcups.h           \
        gtkprintercups.h                \
-       gtkcupsutils.h
+       gtkcupsutils.h                  \
+       gtkcupssecretsutils.h
 
 libprintbackend_cups_la_LDFLAGS =  -avoid-version -module $(no_undefined)
 libprintbackend_cups_la_LIBADD = $(LDADDS) $(CUPS_LIBS)
diff --git a/modules/printbackends/cups/gtkcupssecretsutils.c 
b/modules/printbackends/cups/gtkcupssecretsutils.c
new file mode 100644
index 0000000..895e4bb
--- /dev/null
+++ b/modules/printbackends/cups/gtkcupssecretsutils.c
@@ -0,0 +1,1044 @@
+/* gtkcupssecretsutils.h: Helper to use a secrets service for printer passwords
+ * Copyright (C) 2014, Intevation GmbH
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "gtkcupssecretsutils.h"
+
+#define SECRETS_BUS              "org.freedesktop.secrets"
+#define SECRETS_IFACE(interface) "org.freedesktop.Secret."interface
+#define SECRETS_PATH             "/org/freedesktop/secrets"
+#define SECRETS_TIMEOUT          5000
+
+typedef enum
+{
+  SECRETS_SERVICE_ACTION_QUERY,
+  SECRETS_SERVICE_ACTION_STORE
+} SecretsServiceAction;
+
+typedef struct
+{
+  GDBusConnection       *dbus_connection;
+  SecretsServiceAction   action;
+  gchar                **auth_info,
+                       **auth_info_labels,
+                       **auth_info_required,
+                        *printer_uri,
+                        *session_path,
+                        *collection_path;
+  GDBusProxy            *item_proxy;
+  guint                  prompt_subscription;
+} SecretsServiceData;
+
+/**
+ * create_attributes:
+ * @printer_uri: URI for the printer
+ * @additional_labels: Optional labels for additional attributes
+ * @additional_attrs: Optional additional attributes
+ *
+ * Creates a GVariant dictionary with key / value pairs that
+ * can be used to identify a secret item.
+ *
+ * Returns: A GVariant dictionary of string pairs or NULL on error.
+ */
+static GVariant *
+create_attributes (const gchar  *printer_uri,
+                   gchar       **additional_attrs,
+                   gchar       **additional_labels)
+{
+  GVariantBuilder *attr_builder = NULL;
+  GVariant        *ret = NULL;
+
+  if (printer_uri == NULL)
+    {
+      GTK_NOTE (PRINTING,
+                g_print ("create_attributes called with invalid parameters.\n"));
+      return NULL;
+    }
+
+  attr_builder = g_variant_builder_new (G_VARIANT_TYPE_DICTIONARY);
+  /* The printer uri is the main identifying part */
+  g_variant_builder_add (attr_builder, "{ss}", "uri", printer_uri);
+
+  if (additional_labels != NULL)
+    {
+      int i;
+      for (i = 0; additional_labels[i] != NULL; i++)
+        {
+          g_variant_builder_add (attr_builder, "{ss}",
+                                 additional_labels[i],
+                                 additional_attrs[i]);
+        }
+    }
+
+  ret = g_variant_builder_end (attr_builder);
+  g_variant_builder_unref (attr_builder);
+
+  return ret;
+}
+
+static void
+get_secret_cb (GObject      *source_object,
+               GAsyncResult *res,
+               gpointer      user_data)
+{
+  GTask              *task;
+  SecretsServiceData *task_data;
+  GError             *error = NULL;
+  GVariant           *output,
+                     *attributes;
+  gchar             **auth_info = NULL,
+                     *key = NULL,
+                     *value = NULL;
+  GVariantIter       *iter = NULL;
+  guint               i;
+  gint                pw_field = -1;
+
+  task = user_data;
+  task_data = g_task_get_task_data (task);
+
+  output = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object),
+                                     res,
+                                     &error);
+  if (output == NULL)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  attributes = g_dbus_proxy_get_cached_property (task_data->item_proxy,
+                                                 "Attributes");
+  if (attributes == NULL)
+    {
+      GTK_NOTE (PRINTING, g_print ("Failed to lookup attributes.\n"));
+      g_variant_unref (output);
+      g_task_return_pointer (task, NULL, NULL);
+      return;
+    }
+
+  /* Iterate over the attributes to fill the auth info */
+  g_variant_get (attributes, "a{ss}", &iter);
+
+  auth_info = g_new0 (gchar *,
+                      g_strv_length (task_data->auth_info_required) + 1);
+
+  while (g_variant_iter_loop (iter, "{ss}", &key, &value))
+    {
+      /* Match attributes with required auth info */
+      for (i = 0; task_data->auth_info_required[i] != NULL; i++)
+        {
+          if ((strcmp (key, "user") == 0 ||
+               strcmp (key, "username") == 0) &&
+              strcmp (task_data->auth_info_required[i],
+                      "username") == 0)
+            {
+              auth_info[i] = g_strdup (value);
+            }
+          else if (strcmp (key, "domain") == 0 &&
+                   strcmp (task_data->auth_info_required[i], "domain") == 0)
+            {
+              auth_info[i] = g_strdup (value);
+            }
+          else if ((strcmp (key, "hostname") == 0 ||
+                    strcmp (key, "server") == 0 ) &&
+                   strcmp (task_data->auth_info_required[i], "hostname") == 0)
+            {
+              auth_info[i] = g_strdup (value);
+            }
+          else if (strcmp (task_data->auth_info_required[i], "password") == 0)
+            {
+              pw_field = i;
+            }
+        }
+    }
+
+  if (pw_field == -1)
+    {
+      /* should not happen... */
+      GTK_NOTE (PRINTING, g_print ("No password required?.\n"));
+      g_variant_unref (output);
+      goto fail;
+    }
+  else
+    {
+      GVariant      *secret,
+                    *s_value;
+      gconstpointer  ba_passwd = NULL;
+      gsize          len = 0;
+
+      secret = g_variant_get_child_value (output, 0);
+      g_variant_unref (output);
+      if (secret == NULL || g_variant_n_children (secret) != 4)
+        {
+          GTK_NOTE (PRINTING, g_print ("Get secret response invalid.\n"));
+          if (secret != NULL)
+            g_variant_unref (secret);
+          goto fail;
+        }
+      s_value = g_variant_get_child_value (secret, 2);
+      ba_passwd = g_variant_get_fixed_array (s_value,
+                                             &len,
+                                             sizeof (guchar));
+
+      g_variant_unref (secret);
+
+      if (ba_passwd == NULL || strlen (ba_passwd) > len + 1)
+        {
+          /* No secret or the secret is not a zero terminated value */
+          GTK_NOTE (PRINTING, g_print ("Invalid secret.\n"));
+          g_variant_unref (s_value);
+          goto fail;
+        }
+
+      auth_info[pw_field] = g_strndup (ba_passwd, len);
+      g_variant_unref (s_value);
+    }
+
+  for (i = 0; task_data->auth_info_required[i] != NULL; i++)
+    {
+      if (auth_info[i] == NULL)
+        {
+          /* Error out if we did not find everything */
+          GTK_NOTE (PRINTING, g_print ("Failed to lookup required attribute: %s.\n",
+                                       task_data->auth_info_required[i]));
+          goto fail;
+        }
+    }
+
+  g_task_return_pointer (task, auth_info, NULL);
+  return;
+
+fail:
+  /* Error out */
+  GTK_NOTE (PRINTING, g_print ("Failed to lookup secret.\n"));
+  for (i = 0; i < g_strv_length (task_data->auth_info_required); i++)
+    {
+      /* Not all fields of auth_info are neccessarily written so we can not
+         use strfreev here */
+      g_free (auth_info[i]);
+    }
+  g_free (auth_info);
+  g_task_return_pointer (task, NULL, NULL);
+}
+
+static void
+create_item_cb (GObject      *source_object,
+                GAsyncResult *res,
+                gpointer      user_data)
+{
+  GTask    *task;
+  GError   *error = NULL;
+  GVariant *output;
+  gchar    *item = NULL;
+
+  task = user_data;
+
+  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+                                          res,
+                                          &error);
+  if (output == NULL)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  g_variant_get (output, "(&o&o)", &item, NULL);
+  if (item != NULL && strlen (item) > 1)
+    {
+      GTK_NOTE (PRINTING, g_print ("Successfully stored auth info.\n"));
+      g_task_return_pointer (task, NULL, NULL);
+      return;
+    }
+  g_variant_unref (output);
+}
+
+static void
+do_store_auth_info (GTask *task)
+{
+  GVariant            *attributes = NULL,
+                      *properties = NULL,
+                      *secret = NULL;
+  gchar              **additional_attrs = NULL,
+                     **additional_labels = NULL,
+                      *password = NULL;
+  SecretsServiceData  *task_data = g_task_get_task_data (task);
+  guint                i,
+                       length,
+                       additional_count = 0;
+  GVariantBuilder     *prop_builder = NULL;
+
+  length = g_strv_length (task_data->auth_info_labels);
+
+  additional_attrs = g_new0 (gchar *, length + 1);
+  additional_labels = g_new0 (gchar *, length + 1);
+  /* The labels user and server are chosen to be compatible with
+     the attributes used by system-config-printer */
+  for (i = 0; task_data->auth_info_labels[i] != NULL; i++)
+    {
+      if (g_strcmp0 (task_data->auth_info_labels[i], "username") == 0)
+        {
+          additional_attrs[additional_count] = task_data->auth_info[i];
+          additional_labels[additional_count++] = "user";
+        }
+      else if (g_strcmp0 (task_data->auth_info_labels[i], "hostname") == 0)
+        {
+          additional_attrs[additional_count] = task_data->auth_info[i];
+          additional_labels[additional_count++] = "server";
+        }
+      else if (g_strcmp0 (task_data->auth_info_labels[i], "password") == 0)
+        {
+          password = task_data->auth_info[i];
+        }
+    }
+
+  attributes = create_attributes (task_data->printer_uri,
+                                  additional_attrs,
+                                  additional_labels);
+  g_free (additional_labels);
+  g_free (additional_attrs);
+  if (attributes == NULL)
+    {
+      GTK_NOTE (PRINTING, g_print ("Failed to create attributes.\n"));
+      g_task_return_pointer (task, NULL, NULL);
+      return;
+    }
+
+  if (password == NULL)
+    {
+      GTK_NOTE (PRINTING, g_print ("No secret to store.\n"));
+      g_task_return_pointer (task, NULL, NULL);
+      return;
+    }
+
+  prop_builder = g_variant_builder_new (G_VARIANT_TYPE_DICTIONARY);
+
+  g_variant_builder_add (prop_builder, "{sv}", SECRETS_IFACE ("Item.Label"),
+                         g_variant_new_string (task_data->printer_uri));
+  g_variant_builder_add (prop_builder, "{sv}", SECRETS_IFACE ("Item.Attributes"),
+                         attributes);
+
+  properties = g_variant_builder_end (prop_builder);
+
+  g_variant_builder_unref (prop_builder);
+
+  secret = g_variant_new ("(oay ays)",
+                          task_data->session_path,
+                          NULL,
+                          g_variant_new_bytestring (password),
+                          "text/plain");
+
+  g_dbus_connection_call (task_data->dbus_connection,
+                          SECRETS_BUS,
+                          task_data->collection_path,
+                          SECRETS_IFACE ("Collection"),
+                          "CreateItem",
+                          g_variant_new ("(@a{sv}@(oayays)b)",
+                                         properties,
+                                         secret,
+                                         TRUE),
+                          G_VARIANT_TYPE ("(oo)"),
+                          G_DBUS_CALL_FLAGS_NONE,
+                          SECRETS_TIMEOUT,
+                          g_task_get_cancellable (task),
+                          create_item_cb,
+                          task);
+}
+
+static void
+prompt_completed_cb (GDBusConnection *connection,
+                     const gchar     *sender_name,
+                     const gchar     *object_path,
+                     const gchar     *interface_name,
+                     const gchar     *signal_name,
+                     GVariant        *parameters,
+                     gpointer         user_data)
+{
+  GTask              *task;
+  SecretsServiceData *task_data;
+  GVariant           *dismissed;
+  gboolean            is_dismissed = TRUE;
+
+  task = user_data;
+  task_data = g_task_get_task_data (task);
+
+  g_dbus_connection_signal_unsubscribe (task_data->dbus_connection,
+                                        task_data->prompt_subscription);
+  task_data->prompt_subscription = 0;
+
+  dismissed = g_variant_get_child_value (parameters, 0);
+
+  if (dismissed == NULL)
+    {
+      GTK_NOTE (PRINTING, g_print ("Invalid prompt signal.\n"));
+      g_task_return_pointer (task, NULL, NULL);
+      return;
+    }
+
+  g_variant_get (dismissed, "b", &is_dismissed);
+  g_variant_unref (dismissed);
+
+  if (is_dismissed)
+    {
+      GTK_NOTE (PRINTING, g_print ("Collection unlock dismissed.\n"));
+      g_task_return_pointer (task, NULL, NULL);
+      return;
+    }
+
+  /* Prompt successfull, proceed to get or store secret */
+  switch (task_data->action)
+    {
+      case SECRETS_SERVICE_ACTION_STORE:
+        do_store_auth_info (task);
+        break;
+
+      case SECRETS_SERVICE_ACTION_QUERY:
+        g_dbus_proxy_call (task_data->item_proxy,
+                           "GetSecret",
+                           g_variant_new ("(o)",
+                                          task_data->session_path),
+                           G_DBUS_CALL_FLAGS_NONE,
+                           SECRETS_TIMEOUT,
+                           g_task_get_cancellable (task),
+                           get_secret_cb,
+                           task);
+        break;
+    }
+}
+
+static void
+prompt_cb (GObject      *source_object,
+           GAsyncResult *res,
+           gpointer      user_data)
+{
+  GTask              *task;
+  SecretsServiceData *task_data;
+  GError             *error = NULL;
+  GVariant           *output;
+
+  task = user_data;
+  task_data = g_task_get_task_data (task);
+
+  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+                                          res,
+                                          &error);
+  if (output == NULL)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  g_variant_unref (output);
+
+  /* Connect to the prompt's completed signal */
+  task_data->prompt_subscription =
+    g_dbus_connection_signal_subscribe (task_data->dbus_connection,
+                                        NULL,
+                                        SECRETS_IFACE ("Prompt"),
+                                        "Completed",
+                                        NULL,
+                                        NULL,
+                                        G_DBUS_SIGNAL_FLAGS_NONE,
+                                        prompt_completed_cb,
+                                        task,
+                                        NULL);
+}
+
+static void
+unlock_collection_cb (GObject      *source_object,
+                      GAsyncResult *res,
+                      gpointer      user_data)
+{
+  GTask              *task;
+  SecretsServiceData *task_data;
+  GError             *error = NULL;
+  GVariant           *output;
+  const gchar        *prompt_path;
+
+  task = user_data;
+  task_data = g_task_get_task_data (task);
+
+  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+                                          res,
+                                          &error);
+  if (output == NULL)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  g_variant_get (output, "(@ao&o)", NULL, &prompt_path);
+
+  if (prompt_path != NULL && strlen (prompt_path) > 1)
+    {
+      g_dbus_connection_call (task_data->dbus_connection,
+                              SECRETS_BUS,
+                              prompt_path,
+                              SECRETS_IFACE ("Prompt"),
+                              "Prompt",
+                              g_variant_new ("(s)", "0"),
+                              G_VARIANT_TYPE ("()"),
+                              G_DBUS_CALL_FLAGS_NONE,
+                              SECRETS_TIMEOUT,
+                              g_task_get_cancellable (task),
+                              prompt_cb,
+                              task);
+    }
+  else
+    {
+      switch (task_data->action)
+        {
+          case SECRETS_SERVICE_ACTION_STORE:
+            do_store_auth_info (task);
+            break;
+
+          case SECRETS_SERVICE_ACTION_QUERY:
+            /* Prompt successfull proceed to get secret */
+            g_dbus_proxy_call (task_data->item_proxy,
+                               "GetSecret",
+                               g_variant_new ("(o)",
+                                              task_data->session_path),
+                               G_DBUS_CALL_FLAGS_NONE,
+                               SECRETS_TIMEOUT,
+                               g_task_get_cancellable (task),
+                               get_secret_cb,
+                               task);
+            break;
+        }
+    }
+  g_variant_unref (output);
+}
+
+static void
+unlock_read_alias_cb (GObject      *source_object,
+                      GAsyncResult *res,
+                      gpointer      user_data)
+{
+  GTask              *task;
+  SecretsServiceData *task_data;
+  GError             *error = NULL;
+  GVariant           *output,
+                     *subresult;
+  gsize               path_len = 0;
+  const gchar        *collection_path;
+
+  task = user_data;
+  task_data = g_task_get_task_data (task);
+
+  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+                                          res,
+                                          &error);
+  if (output == NULL)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  subresult = g_variant_get_child_value (output, 0);
+  g_variant_unref (output);
+
+  if (subresult == NULL)
+    {
+      GTK_NOTE (PRINTING, g_print ("Invalid ReadAlias response.\n"));
+      g_task_return_pointer (task, NULL, NULL);
+      return;
+    }
+
+  collection_path = g_variant_get_string (subresult, &path_len);
+
+  const gchar * const to_unlock[] =
+  {
+    collection_path, NULL
+  };
+
+  task_data->collection_path = g_strdup (collection_path);
+
+  g_dbus_connection_call (task_data->dbus_connection,
+                          SECRETS_BUS,
+                          SECRETS_PATH,
+                          SECRETS_IFACE ("Service"),
+                          "Unlock",
+                          g_variant_new ("(^ao)", to_unlock),
+                          G_VARIANT_TYPE ("(aoo)"),
+                          G_DBUS_CALL_FLAGS_NONE,
+                          SECRETS_TIMEOUT,
+                          g_task_get_cancellable (task),
+                          unlock_collection_cb,
+                          task);
+
+  g_variant_unref (subresult);
+}
+
+static void
+item_proxy_cb (GObject      *source_object,
+               GAsyncResult *res,
+               gpointer      user_data)
+{
+  GTask              *task;
+  SecretsServiceData *task_data;
+  GError             *error = NULL;
+  GDBusProxy         *item_proxy;
+  GVariant           *locked;
+  gboolean            is_locked;
+
+  task = user_data;
+  task_data = g_task_get_task_data (task);
+
+  item_proxy = g_dbus_proxy_new_finish (res,
+                                        &error);
+  if (item_proxy == NULL)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  task_data->item_proxy = item_proxy;
+
+  locked = g_dbus_proxy_get_cached_property (item_proxy, "Locked");
+
+  if (locked == NULL)
+    {
+      GTK_NOTE (PRINTING, g_print ("Failed to look up \"Locked\" property on item.\n"));
+      g_task_return_pointer (task, NULL, NULL);
+      return;
+    }
+
+  g_variant_get (locked, "b", &is_locked);
+  g_variant_unref (locked);
+
+  if (is_locked)
+    {
+      /* Go down the unlock -> lookup path */
+      g_dbus_connection_call (task_data->dbus_connection,
+                              SECRETS_BUS,
+                              SECRETS_PATH,
+                              SECRETS_IFACE ("Service"),
+                              "ReadAlias",
+                              g_variant_new ("(s)", "default"),
+                              G_VARIANT_TYPE ("(o)"),
+                              G_DBUS_CALL_FLAGS_NONE,
+                              SECRETS_TIMEOUT,
+                              g_task_get_cancellable (task),
+                              unlock_read_alias_cb,
+                              task);
+      return;
+    }
+
+  /* Unlocked proceed to get or store secret */
+  switch (task_data->action)
+    {
+      case SECRETS_SERVICE_ACTION_STORE:
+        do_store_auth_info (task);
+        break;
+
+      case SECRETS_SERVICE_ACTION_QUERY:
+        g_dbus_proxy_call (item_proxy,
+                           "GetSecret",
+                           g_variant_new ("(o)",
+                                          task_data->session_path),
+                           G_DBUS_CALL_FLAGS_NONE,
+                           SECRETS_TIMEOUT,
+                           g_task_get_cancellable (task),
+                           get_secret_cb,
+                           task);
+        break;
+    }
+}
+
+static void
+search_items_cb (GObject      *source_object,
+                 GAsyncResult *res,
+                 gpointer      user_data)
+{
+  GTask              *task;
+  SecretsServiceData *task_data;
+  GError             *error = NULL;
+  GVariant           *output;
+  gsize               array_cnt,
+                      i;
+  gboolean            found_item = FALSE;
+
+  task = user_data;
+  task_data = g_task_get_task_data (task);
+
+  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+                                          res,
+                                          &error);
+  if (output == NULL)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  array_cnt = g_variant_n_children (output);
+
+  for (i = 0; i < array_cnt; i++)
+    {
+      GVariant * const   item_paths = g_variant_get_child_value (output, i);
+      const gchar      **items = NULL;
+
+      if (item_paths == NULL)
+        {
+          GTK_NOTE (PRINTING,
+                    g_print ("SearchItems returned invalid result.\n"));
+          continue;
+        }
+
+      items = g_variant_get_objv (item_paths, NULL);
+
+      if (*items == NULL)
+        {
+          g_variant_unref (item_paths);
+          g_free ((gpointer) items);
+          continue;
+        }
+
+      /* Access the first found item. */
+      found_item = TRUE;
+      g_dbus_proxy_new (task_data->dbus_connection,
+                        G_DBUS_PROXY_FLAGS_NONE,
+                        NULL,
+                        SECRETS_BUS,
+                        *items,
+                        SECRETS_IFACE ("Item"),
+                        g_task_get_cancellable (task),
+                        item_proxy_cb,
+                        task);
+      g_free ((gpointer) items);
+      g_variant_unref (item_paths);
+      break;
+    }
+  g_variant_unref (output);
+
+  if (!found_item)
+    {
+      GTK_NOTE (PRINTING, g_print ("No match found in secrets service.\n"));
+      g_task_return_pointer (task, NULL, NULL);
+      return;
+    }
+}
+
+static void
+open_session_cb (GObject      *source_object,
+                 GAsyncResult *res,
+                 gpointer      user_data)
+{
+  GTask              *task;
+  GVariant           *output,
+                     *session_variant;
+  SecretsServiceData *task_data;
+  GError             *error = NULL;
+
+  task = user_data;
+  task_data = g_task_get_task_data (task);
+
+  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+                                          res,
+                                          &error);
+  if (output == NULL)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  session_variant = g_variant_get_child_value (output, 1);
+
+  if (session_variant == NULL)
+    {
+      GTK_NOTE (PRINTING, g_print ("Invalid session path response.\n"));
+      g_variant_unref (output);
+      g_task_return_pointer (task, NULL, NULL);
+      return;
+    }
+
+  task_data->session_path = g_variant_dup_string (session_variant, NULL);
+
+  if (task_data->session_path == NULL)
+    {
+      GTK_NOTE (PRINTING, g_print ("Invalid session path string value.\n"));
+      g_variant_unref (session_variant);
+      g_variant_unref (output);
+      g_task_return_pointer (task, NULL, NULL);
+      return;
+    }
+
+  g_variant_unref (session_variant);
+  g_variant_unref (output);
+
+  switch (task_data->action)
+    {
+      case SECRETS_SERVICE_ACTION_QUERY:
+        {
+          /* Search for the secret item */
+          GVariant *secrets_attrs;
+
+          secrets_attrs = create_attributes (task_data->printer_uri, NULL, NULL);
+          if (secrets_attrs == NULL)
+            {
+              GTK_NOTE (PRINTING, g_print ("Failed to create attributes.\n"));
+              g_task_return_pointer (task, NULL, NULL);
+              return;
+            }
+
+          g_dbus_connection_call (task_data->dbus_connection,
+                                  SECRETS_BUS,
+                                  SECRETS_PATH,
+                                  SECRETS_IFACE ("Service"),
+                                  "SearchItems",
+                                  g_variant_new ("(@a{ss})", secrets_attrs),
+                                  G_VARIANT_TYPE ("(aoao)"),
+                                  G_DBUS_CALL_FLAGS_NONE,
+                                  SECRETS_TIMEOUT,
+                                  g_task_get_cancellable (task),
+                                  search_items_cb,
+                                  task);
+          break;
+        }
+
+      case SECRETS_SERVICE_ACTION_STORE:
+        {
+          /* Look up / unlock the default collection for storing */
+          g_dbus_connection_call (task_data->dbus_connection,
+                                  SECRETS_BUS,
+                                  SECRETS_PATH,
+                                  SECRETS_IFACE ("Service"),
+                                  "ReadAlias",
+                                  g_variant_new ("(s)", "default"),
+                                  G_VARIANT_TYPE ("(o)"),
+                                  G_DBUS_CALL_FLAGS_NONE,
+                                  SECRETS_TIMEOUT,
+                                  g_task_get_cancellable (task),
+                                  unlock_read_alias_cb,
+                                  task);
+          break;
+        }
+    }
+}
+
+static void
+get_connection_cb (GObject      *source_object,
+                   GAsyncResult *res,
+                   gpointer      user_data)
+{
+  GTask              *task;
+  SecretsServiceData *task_data;
+  GError             *error = NULL;
+
+  task = user_data;
+  task_data = g_task_get_task_data (task);
+
+  task_data->dbus_connection = g_bus_get_finish (res, &error);
+  if (task_data->dbus_connection == NULL)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  /* Now open a session */
+  g_dbus_connection_call (task_data->dbus_connection,
+                          SECRETS_BUS,
+                          SECRETS_PATH,
+                          SECRETS_IFACE ("Service"),
+                          "OpenSession",
+                          g_variant_new ("(sv)", "plain",
+                                         g_variant_new_string ("")),
+                          G_VARIANT_TYPE ("(vo)"),
+                          G_DBUS_CALL_FLAGS_NONE,
+                          SECRETS_TIMEOUT,
+                          g_task_get_cancellable (task),
+                          open_session_cb,
+                          task);
+}
+
+/**
+ * gtk_cups_secrets_service_watch:
+ * @appeared: The callback to call when the service interface appears
+ * @vanished: The callback to call when the service interface vanishes
+ * @user_data: A reference to the watching printbackend
+ *
+ * Registers a watch for the secrets service interface.
+ *
+ * Returns: The watcher id
+ */
+guint
+gtk_cups_secrets_service_watch (GBusNameAppearedCallback appeared,
+                                GBusNameVanishedCallback vanished,
+                                gpointer                 user_data)
+{
+  return g_bus_watch_name (G_BUS_TYPE_SESSION,
+                           SECRETS_BUS,
+                           G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
+                           appeared,
+                           vanished,
+                           user_data,
+                           NULL);
+}
+
+void
+cleanup_task_data (gpointer data)
+{
+  gint                i;
+  SecretsServiceData *task_data = data;
+
+  g_free (task_data->collection_path);
+  g_strfreev (task_data->auth_info_labels);
+  g_strfreev (task_data->auth_info_required);
+  g_free (task_data->printer_uri);
+
+  if (task_data->auth_info != NULL)
+    {
+      for (i = 0; task_data->auth_info[i] != NULL; i++)
+        {
+          memset (task_data->auth_info[i], 0, strlen (task_data->auth_info[i]));
+          g_clear_pointer (&task_data->auth_info[i], g_free);
+        }
+      g_clear_pointer (&task_data->auth_info, g_free);
+    }
+
+  if (task_data->prompt_subscription != 0)
+    {
+      g_dbus_connection_signal_unsubscribe (task_data->dbus_connection,
+                                            task_data->prompt_subscription);
+      task_data->prompt_subscription = 0;
+    }
+
+  if (task_data->session_path != NULL)
+    {
+      g_dbus_connection_call (task_data->dbus_connection,
+                              SECRETS_BUS,
+                              task_data->session_path,
+                              SECRETS_IFACE ("Session"),
+                              "Close",
+                              NULL,
+                              G_VARIANT_TYPE ("()"),
+                              G_DBUS_CALL_FLAGS_NONE,
+                              SECRETS_TIMEOUT,
+                              NULL,
+                              NULL,
+                              NULL);
+    }
+
+  g_clear_object (&task_data->dbus_connection);
+  g_clear_pointer (&task_data->session_path, g_free);
+  g_clear_object (&task_data->item_proxy);
+}
+
+/**
+ * gtk_cups_secrets_service_query_task:
+ * @source_object: Source object for this task
+ * @cancellable: Cancellable to cancel this task
+ * @callback: Callback to call once the query is finished
+ * @user_data: The user_data passed to the callback
+ * @printer_uri: URI of the printer
+ * @auth_info_required: Info required for authentication
+ *
+ * Checks if a secrets service as described by the secrets-service standard
+ * is available and if so it tries to find the authentication info in the
+ * default collection of the service.
+ *
+ * This is the entry point to a chain of async calls to open a session,
+ * search the secret, unlock the collection (if necessary) and finally
+ * to lookup the secret.
+ *
+ * See: http://standards.freedesktop.org/secret-service/ for documentation
+ * of the used API.
+ */
+void
+gtk_cups_secrets_service_query_task (gpointer              source_object,
+                                     GCancellable         *cancellable,
+                                     GAsyncReadyCallback   callback,
+                                     gpointer              user_data,
+                                     const gchar          *printer_uri,
+                                     gchar               **auth_info_required)
+{
+  GTask              *task;
+  SecretsServiceData *task_data;
+
+  task_data = g_new0 (SecretsServiceData, 1);
+  task_data->action = SECRETS_SERVICE_ACTION_QUERY;
+  task_data->printer_uri = g_strdup (printer_uri);
+  task_data->auth_info_required = g_strdupv (auth_info_required);
+
+  task = g_task_new (source_object, cancellable, callback, user_data);
+
+  g_task_set_task_data (task, task_data, cleanup_task_data);
+
+  g_bus_get (G_BUS_TYPE_SESSION, cancellable,
+             get_connection_cb, task);
+}
+
+static void
+store_done_cb (GObject      *source_object,
+               GAsyncResult *res,
+               gpointer      user_data)
+{
+  GTask  *task = (GTask *) res;
+  GError *error = NULL;
+
+  g_task_propagate_pointer (task, &error);
+
+  if (error != NULL)
+    {
+      GTK_NOTE (PRINTING,
+                g_print ("Failed to store auth info: %s\n", error->message));
+      g_error_free (error);
+    }
+
+  g_object_unref (task);
+  GTK_NOTE (PRINTING,
+            g_print ("gtk_cups_secrets_service_store finished.\n"));
+}
+
+/**
+ * gtk_cups_secrets_service_store:
+ * @auth_info: Auth info that should be stored
+ * @auth_info_labels: The keys to use for the auth info
+ * @printer_uri: URI of the printer
+ *
+ * Tries to store the auth_info in a secrets service.
+ */
+void
+gtk_cups_secrets_service_store (gchar       **auth_info,
+                                gchar       **auth_info_labels,
+                                const gchar  *printer_uri)
+{
+  GTask              *task;
+  SecretsServiceData *task_data;
+
+  if (auth_info == NULL || auth_info_labels == NULL || printer_uri == NULL)
+    {
+      GTK_NOTE (PRINTING,
+                g_print ("Invalid call to gtk_cups_secrets_service_store.\n"));
+      return;
+    }
+
+  task_data = g_new0 (SecretsServiceData, 1);
+  task_data->action = SECRETS_SERVICE_ACTION_STORE;
+  task_data->printer_uri = g_strdup (printer_uri);
+  task_data->auth_info = g_strdupv (auth_info);
+  task_data->auth_info_labels = g_strdupv (auth_info_labels);
+
+  task = g_task_new (NULL, NULL, store_done_cb, NULL);
+
+  g_task_set_task_data (task, task_data, cleanup_task_data);
+
+  g_bus_get (G_BUS_TYPE_SESSION, NULL,
+             get_connection_cb, task);
+}
diff --git a/modules/printbackends/cups/gtkcupssecretsutils.h 
b/modules/printbackends/cups/gtkcupssecretsutils.h
new file mode 100644
index 0000000..1a0424a
--- /dev/null
+++ b/modules/printbackends/cups/gtkcupssecretsutils.h
@@ -0,0 +1,41 @@
+/* gtkcupssecretsutils.h: Helper to use a secrets service for printer passwords
+ * Copyright (C) 2014 Intevation GmbH
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __GTK_SECRETS_UTILS_H__
+#define __GTK_SECRETS_UTILS_H__
+
+#include <glib.h>
+
+#include "gtkcupsutils.h"
+
+G_BEGIN_DECLS
+
+void  gtk_cups_secrets_service_query_task (gpointer                   source_object,
+                                           GCancellable              *cancellable,
+                                           GAsyncReadyCallback        callback,
+                                           gpointer                   user_data,
+                                           const gchar               *printer_uri,
+                                           gchar                    **auth_info_required);
+guint gtk_cups_secrets_service_watch      (GBusNameAppearedCallback   appeared,
+                                           GBusNameVanishedCallback   vanished,
+                                           gpointer                   user_data);
+void  gtk_cups_secrets_service_store      (gchar                    **auth_info,
+                                           gchar                    **auth_info_labels,
+                                           const gchar               *printer_uri);
+
+G_END_DECLS
+
+#endif /* __GTK_SECRETS_UTILS_H__ */
diff --git a/modules/printbackends/cups/gtkprintbackendcups.c 
b/modules/printbackends/cups/gtkprintbackendcups.c
index 69abd49..06c1e68 100644
--- a/modules/printbackends/cups/gtkprintbackendcups.c
+++ b/modules/printbackends/cups/gtkprintbackendcups.c
@@ -54,6 +54,7 @@
 #include "gtkprintercups.h"
 
 #include "gtkcupsutils.h"
+#include "gtkcupssecretsutils.h"
 
 #ifdef HAVE_COLORD
 #include <colord.h>
@@ -153,6 +154,9 @@ struct _GtkPrintBackendCups
   gchar           *avahi_service_browser_paths[2];
   GCancellable    *avahi_cancellable;
 #endif
+  gboolean      secrets_service_available;
+  guint         secrets_service_watch_id;
+  GCancellable *secrets_service_cancellable;
 };
 
 static GObjectClass *backend_parent_class;
@@ -213,16 +217,26 @@ static cairo_surface_t *    cups_printer_create_cairo_surface      (GtkPrinter
 
 static void                 gtk_print_backend_cups_set_password    (GtkPrintBackend                   
*backend,
                                                                     gchar                            
**auth_info_required,
-                                                                    gchar                            
**auth_info);
+                                                                    gchar                            
**auth_info,
+                                                                    gboolean                           
store_auth_info);
 
 void                        overwrite_and_free                      (gpointer                          data);
 static gboolean             is_address_local                        (const gchar                      
*address);
 static gboolean             request_auth_info                       (gpointer                          data);
+static void                 lookup_auth_info                        (gpointer                          data);
 
 #ifdef HAVE_CUPS_API_1_6
 static void                 avahi_request_printer_list              (GtkPrintBackendCups              
*cups_backend);
 #endif
 
+static void                 secrets_service_appeared_cb             (GDBusConnection *connection,
+                                                                     const gchar *name,
+                                                                     const gchar *name_owner,
+                                                                     gpointer user_data);
+static void                 secrets_service_vanished_cb             (GDBusConnection *connection,
+                                                                     const gchar *name,
+                                                                     gpointer user_data);
+
 static void
 gtk_print_backend_cups_register_type (GTypeModule *module)
 {
@@ -780,6 +794,13 @@ gtk_print_backend_cups_init (GtkPrintBackendCups *backend_cups)
 #endif
 
   cups_get_local_default_printer (backend_cups);
+
+  backend_cups->secrets_service_available = FALSE;
+  backend_cups->secrets_service_cancellable = g_cancellable_new ();
+  backend_cups->secrets_service_watch_id =
+    gtk_cups_secrets_service_watch (secrets_service_appeared_cb,
+                                    secrets_service_vanished_cb,
+                                    backend_cups);
 }
 
 static void
@@ -815,6 +836,12 @@ gtk_print_backend_cups_finalize (GObject *object)
   g_clear_object (&backend_cups->dbus_connection);
 #endif
 
+  g_clear_object (&backend_cups->secrets_service_cancellable);
+  if (backend_cups->secrets_service_watch_id != 0)
+    {
+      g_bus_unwatch_name (backend_cups->secrets_service_watch_id);
+    }
+
   backend_parent_class->finalize (object);
 }
 
@@ -895,7 +922,8 @@ is_address_local (const gchar *address)
 static void
 gtk_print_backend_cups_set_password (GtkPrintBackend  *backend,
                                      gchar           **auth_info_required,
-                                     gchar           **auth_info)
+                                     gchar           **auth_info,
+                                     gboolean          store_auth_info)
 {
   GtkPrintBackendCups *cups_backend = GTK_PRINT_BACKEND_CUPS (backend);
   GList *l;
@@ -924,7 +952,7 @@ gtk_print_backend_cups_set_password (GtkPrintBackend  *backend,
       gchar *key = g_strconcat (username, "@", hostname, NULL);
       g_hash_table_insert (cups_backend->auth, key, g_strdup (password));
       GTK_NOTE (PRINTING,
-                g_print ("CUPS backend: storing password for %s\n", key));
+                g_print ("CUPS backend: caching password for %s\n", key));
     }
 
   g_free (cups_backend->username);
@@ -947,6 +975,17 @@ gtk_print_backend_cups_set_password (GtkPrintBackend  *backend,
               for (i = 0; i < length; i++)
                 dispatch->request->auth_info[i] = g_strdup (auth_info[i]);
             }
+          /* Save the password if the user requested it */
+          if (password != NULL && store_auth_info)
+            {
+              const gchar *printer_uri =
+                  gtk_cups_request_ipp_get_string (dispatch->request,
+                                                   IPP_TAG_URI,
+                                                   "printer-uri");
+
+              gtk_cups_secrets_service_store (auth_info, auth_info_required,
+                                              printer_uri);
+            }
           dispatch->backend->authentication_lock = FALSE;
           dispatch->request->need_auth_info = FALSE;
         }
@@ -1074,7 +1113,9 @@ request_password (gpointer data)
       g_free (printer_name);
 
       g_signal_emit_by_name (dispatch->backend, "request-password",
-                             auth_info_required, auth_info_default, auth_info_display, auth_info_visible, 
prompt);
+                             auth_info_required, auth_info_default,
+                             auth_info_display, auth_info_visible, prompt,
+                             FALSE); /* Cups password is only cached not stored. */
 
       g_free (prompt);
     }
@@ -1178,6 +1219,98 @@ check_auth_info (gpointer user_data)
   return G_SOURCE_CONTINUE;
 }
 
+static void
+lookup_auth_info_cb (GObject      *source_object,
+                     GAsyncResult *res,
+                     gpointer      user_data)
+{
+  GTask                      *task;
+  GtkPrintCupsDispatchWatch  *dispatch;
+  gchar                     **auth_info;
+  GError                     *error = NULL;
+  gint                        i;
+
+  task = (GTask *) res;
+  dispatch = user_data;
+  auth_info = g_task_propagate_pointer (task, &error);
+
+  if (auth_info == NULL)
+    {
+      if (error != NULL)
+        {
+          GTK_NOTE (PRINTING,
+                    g_print ("Failed to look up auth info: %s\n", error->message));
+          g_error_free (error);
+        }
+      else
+        {
+          /* Error note should have been shown by the function causing this */
+          GTK_NOTE (PRINTING, g_print ("Failed to look up auth info.\n"));
+        }
+      dispatch->backend->authentication_lock = FALSE;
+      g_object_unref (task);
+      request_auth_info (dispatch);
+      return;
+    }
+
+  gtk_print_backend_cups_set_password (GTK_PRINT_BACKEND (dispatch->backend),
+                                       dispatch->request->auth_info_required, auth_info,
+                                       FALSE);
+  for (i = 0; auth_info[i] != NULL; i++)
+    {
+      overwrite_and_free (auth_info[i]);
+      auth_info[i] = NULL;
+    }
+  g_clear_pointer (auth_info, g_free);
+
+  g_object_unref (task);
+}
+
+static void
+lookup_auth_info (gpointer user_data)
+{
+  GtkPrintCupsDispatchWatch  *dispatch;
+  gsize                       length,
+                              i;
+  gboolean                    need_secret_auth_info = FALSE;
+  const gchar                *printer_uri;
+
+  dispatch = user_data;
+
+  if (dispatch->backend->authentication_lock)
+    return;
+
+  length = g_strv_length (dispatch->request->auth_info_required);
+
+  for (i = 0; i < length; i++)
+    {
+      if (g_strcmp0 (dispatch->request->auth_info_required[i], "password") == 0)
+        {
+          need_secret_auth_info = TRUE;
+          break;
+        }
+    }
+
+  g_idle_add (check_auth_info, user_data);
+
+  if (dispatch->backend->secrets_service_available && need_secret_auth_info)
+    {
+      dispatch->backend->authentication_lock = TRUE;
+      printer_uri = gtk_cups_request_ipp_get_string (dispatch->request,
+                                                     IPP_TAG_URI,
+                                                     "printer-uri");
+      gtk_cups_secrets_service_query_task (dispatch->backend,
+                                           dispatch->backend->secrets_service_cancellable,
+                                           lookup_auth_info_cb,
+                                           dispatch,
+                                           printer_uri,
+                                           dispatch->request->auth_info_required);
+      return;
+    }
+
+  request_auth_info (user_data);
+}
+
 static gboolean
 request_auth_info (gpointer user_data)
 {
@@ -1254,7 +1387,8 @@ request_auth_info (gpointer user_data)
                          auth_info_default,
                          auth_info_display,
                          auth_info_visible,
-                         prompt);
+                         prompt,
+                         dispatch->backend->secrets_service_available);
 
   for (i = 0; i < length; i++)
     {
@@ -1267,8 +1401,6 @@ request_auth_info (gpointer user_data)
   g_free (printer_name);
   g_free (prompt);
 
-  g_idle_add (check_auth_info, user_data);
-
   return FALSE;
 }
 
@@ -1469,7 +1601,7 @@ cups_request_execute (GtkPrintBackendCups              *print_backend,
     {
       dispatch->callback = callback;
       dispatch->callback_data = user_data;
-      request_auth_info (dispatch);
+      lookup_auth_info (dispatch);
     }
   else
     {
@@ -5924,3 +6056,24 @@ cups_printer_get_capabilities (GtkPrinter *printer)
 
   return capabilities;
 }
+
+static void
+secrets_service_appeared_cb (GDBusConnection *connection,
+                             const gchar     *name,
+                             const gchar     *name_owner,
+                             gpointer         user_data)
+{
+  GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
+
+  backend->secrets_service_available = TRUE;
+}
+
+static void
+secrets_service_vanished_cb (GDBusConnection *connection,
+                             const gchar     *name,
+                             gpointer         user_data)
+{
+  GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
+
+  backend->secrets_service_available = FALSE;
+}


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