[gnome-keyring/gnome-3-0] secrets: Don't prompt multiple times for simultaneous unlocks



commit 26c03d71fb085ee644550c27238b4c8dfbb22fc2
Author: Stef Walter <stefw collabora co uk>
Date:   Mon May 23 14:51:26 2011 +0200

    secrets: Don't prompt multiple times for simultaneous unlocks
    
     * Serialize unlock requests for keyrings.
     * This has the effect of not prompting multiple times when simultaneous
       unlock requests come in for the same keyring.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=649572

 daemon/dbus/gkd-secret-unlock.c |   72 +++++++++++++++++++++++++++++++++------
 1 files changed, 61 insertions(+), 11 deletions(-)
---
diff --git a/daemon/dbus/gkd-secret-unlock.c b/daemon/dbus/gkd-secret-unlock.c
index 35f672a..ea0728b 100644
--- a/daemon/dbus/gkd-secret-unlock.c
+++ b/daemon/dbus/gkd-secret-unlock.c
@@ -45,6 +45,13 @@
 
 #include <string.h>
 
+/*
+ * We try to serialize unlock requests, so the user doesn't get prompted
+ * multiple times for the same thing. There are two queues:
+ *  - self->queued: A queue of object paths per unlock requests.
+ *  - unlock_prompt_queue: A queue of unlock requests ready to prompt.
+ */
+
 enum {
 	PROP_0,
 	PROP_CALLER,
@@ -74,6 +81,7 @@ G_DEFINE_TYPE_WITH_CODE (GkdSecretUnlock, gkd_secret_unlock, G_TYPE_OBJECT,
                          G_IMPLEMENT_INTERFACE (GKD_SECRET_TYPE_DISPATCH, gkd_secret_dispatch_iface));
 
 static guint unique_prompt_number = 0;
+static GQueue unlock_prompt_queue = G_QUEUE_INIT;
 
 /* -----------------------------------------------------------------------------
  * INTERNAL
@@ -118,6 +126,7 @@ common_unlock_attributes (GckAttributes *attrs, GckObject *collection)
 static gboolean
 mark_as_complete (GkdSecretUnlock *self, gboolean dismissed)
 {
+	GkdSecretUnlock *other;
 	DBusMessage *signal;
 	DBusMessageIter iter;
 	dbus_bool_t bval;
@@ -151,6 +160,14 @@ mark_as_complete (GkdSecretUnlock *self, gboolean dismissed)
 
 	gkd_secret_service_send (self->service, signal);
 	dbus_message_unref (signal);
+
+	/* Fire off the next item in the unlock prompt queue */
+	other = g_queue_pop_head (&unlock_prompt_queue);
+	if (other != NULL) {
+		perform_next_unlock (other);
+		g_object_unref (other);
+	}
+
 	return TRUE;
 }
 
@@ -158,9 +175,18 @@ static void
 on_unlock_complete (GObject *object, GAsyncResult *res, gpointer user_data)
 {
 	GkdSecretUnlock *self = GKD_SECRET_UNLOCK (user_data);
+	GkdSecretUnlock *other;
 	GckObject *cred;
 	GError *error = NULL;
 
+	/* We should be at the front of the unlock queue, pop ourselves */
+	other = g_queue_pop_head (&unlock_prompt_queue);
+	if (other == self)
+		g_object_unref (other);
+	else
+		g_warning ("unlock prompt queue is out of sync with prompts");
+
+	/* Now process the results */
 	cred = gck_session_create_object_finish (GCK_SESSION (object), res, &error);
 
 	/* Successfully authentication */
@@ -200,9 +226,11 @@ perform_next_unlock (GkdSecretUnlock *self)
 	GckAttributes *template;
 	GckSession *session;
 	gboolean locked;
+	gboolean proceed;
 	gchar *objpath;
 
-	while (!self->current) {
+	for (;;) {
+		g_assert (!self->current);
 		objpath = g_queue_pop_head (self->queued);
 
 		/* Nothing more to prompt for? */
@@ -229,18 +257,37 @@ perform_next_unlock (GkdSecretUnlock *self)
 			continue;
 		}
 
-		/* The various unlock options */
-		template = gck_attributes_new ();
-		common_unlock_attributes (template, collection);
-		gck_attributes_add_data (template, CKA_VALUE, NULL, 0);
-
-		session = gkd_secret_service_get_pkcs11_session (self->service, self->caller);
-		gck_session_create_object_async (session, template, self->cancellable, on_unlock_complete,
-		                                 g_object_ref (self));
-		gck_attributes_unref (template);
+		/* Add ourselves to the unlock prompt queue */
+		proceed = g_queue_is_empty (&unlock_prompt_queue);
+		g_queue_push_tail (&unlock_prompt_queue, g_object_ref (self));
+
+		/*
+		 * Proceed with this unlock request. The on_unlock_complete callback
+		 * pops us back off the unlock prompt queue
+		 */
+		if (proceed) {
+			template = gck_attributes_new ();
+			common_unlock_attributes (template, collection);
+			gck_attributes_add_data (template, CKA_VALUE, NULL, 0);
+
+			session = gkd_secret_service_get_pkcs11_session (self->service, self->caller);
+			gck_session_create_object_async (session, template, self->cancellable, on_unlock_complete,
+							 g_object_ref (self));
+			gck_attributes_unref (template);
+			self->current = objpath;
+			break;
+		}
 
 		g_object_unref (collection);
-		self->current = objpath;
+
+		/*
+		 * Already have one unlock request going on. Just wait around
+		 * and this function will be called again later.
+		 */
+		if (!proceed) {
+			g_queue_push_head (self->queued, objpath);
+			break;
+		}
 	}
 }
 
@@ -374,6 +421,9 @@ gkd_secret_unlock_finalize (GObject *obj)
 {
 	GkdSecretUnlock *self = GKD_SECRET_UNLOCK (obj);
 
+	if (g_queue_find (&unlock_prompt_queue, self))
+		g_warning ("unlock queue is not in sync with prompting");
+
 	if (self->queued) {
 		while (!g_queue_is_empty (self->queued))
 			g_free (g_queue_pop_head (self->queued));



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