[gnome-keyring/dbus-api] Implement basics of prompting for operations.
- From: Stefan Walter <stefw src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [gnome-keyring/dbus-api] Implement basics of prompting for operations.
- Date: Thu, 5 Nov 2009 05:54:14 +0000 (UTC)
commit 15a7df11c798c881875596c3432afa340f6443f9
Author: Stef Walter <stef memberwebs com>
Date: Sun Nov 1 15:55:02 2009 +0000
Implement basics of prompting for operations.
* Implement Service.Unlock method.
* Hook into prompting code.
* Implementing what was recently posted on the mailing list,
about prompt objects, and returning them from operations that
need to prompt.
* Buildable, but not yet runnable.
configure.in | 1 +
daemon/.gitignore | 6 +-
daemon/Makefile.am | 2 +
daemon/dbus/Makefile.am | 4 +-
daemon/dbus/gkd-secrets-objects.c | 31 +
daemon/dbus/gkd-secrets-objects.h | 4 +
daemon/dbus/gkd-secrets-prompt.c | 387 +++++++++++++
daemon/dbus/gkd-secrets-prompt.h | 70 +++
daemon/dbus/gkd-secrets-service.c | 116 +++-
daemon/dbus/gkd-secrets-service.h | 6 +
daemon/dbus/gkd-secrets-types.h | 6 +-
daemon/dbus/gkd-secrets-unlock.c | 351 ++++++++++++
daemon/dbus/gkd-secrets-unlock.h | 56 ++
daemon/prompt/Makefile.am | 29 +
daemon/prompt/gkd-prompt-marshal.list | 1 +
daemon/prompt/gkd-prompt.c | 975 +++++++++++++++++++++++++++++++++
daemon/prompt/gkd-prompt.h | 102 ++++
17 files changed, 2119 insertions(+), 28 deletions(-)
---
diff --git a/configure.in b/configure.in
index e1f95fd..34373b7 100644
--- a/configure.in
+++ b/configure.in
@@ -532,6 +532,7 @@ daemon/dbus/Makefile
daemon/keyrings/Makefile
daemon/keyrings/tests/Makefile
daemon/pkcs11/Makefile
+daemon/prompt/Makefile
daemon/ui/Makefile
daemon/util/Makefile
daemon/util/tests/Makefile
diff --git a/daemon/.gitignore b/daemon/.gitignore
index a7e6dc6..f68cc1c 100644
--- a/daemon/.gitignore
+++ b/daemon/.gitignore
@@ -1,5 +1,5 @@
-/.libs
-/.deps
+.libs
+.deps
Makefile
Makefile.in
/gnome-keyring-ask
@@ -7,3 +7,5 @@ Makefile.in
/org.gnome.keyring.service
/gnome-keyring-daemon.desktop
/gnome-keyring-daemon.desktop.in
+*-marshal.[ch]
+
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index b3759a3..2c8201f 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -1,6 +1,7 @@
SUBDIRS = \
util \
ui \
+ prompt \
keyrings \
pkcs11 \
dbus \
@@ -33,6 +34,7 @@ gnome_keyring_daemon_LDADD = \
$(top_builddir)/daemon/dbus/libgkr-dbus.la \
$(top_builddir)/daemon/keyrings/libgkr-keyrings.la \
$(top_builddir)/daemon/ui/libgkr-ui.la \
+ $(top_builddir)/daemon/prompt/libgkd-prompt.la \
$(top_builddir)/daemon/util/libgkr-daemon-util.la \
$(top_builddir)/library/libgnome-keyring-common.la \
$(top_builddir)/pkcs11/plex-layer/libgck-plex-layer.la \
diff --git a/daemon/dbus/Makefile.am b/daemon/dbus/Makefile.am
index 36f2d96..818ad16 100644
--- a/daemon/dbus/Makefile.am
+++ b/daemon/dbus/Makefile.am
@@ -19,9 +19,11 @@ libgkr_dbus_la_SOURCES = \
gkd-dbus-session.c \
gkd-dbus-util.c gkd-dbus-util.h \
gkd-secrets-objects.c gkd-secrets-objects.h \
+ gkd-secrets-prompt.c gkd-secrets-prompt.h \
gkd-secrets-service.c gkd-secrets-service.h \
gkd-secrets-session.c gkd-secrets-session.h \
- gkd-secrets-types.h
+ gkd-secrets-types.h \
+ gkd-secrets-unlock.c gkd-secrets-unlock.h
libgkr_dbus_la_LIBADD = \
$(GLIB_LIBS) \
diff --git a/daemon/dbus/gkd-secrets-objects.c b/daemon/dbus/gkd-secrets-objects.c
index 165b2ea..c9c8b5d 100644
--- a/daemon/dbus/gkd-secrets-objects.c
+++ b/daemon/dbus/gkd-secrets-objects.c
@@ -1395,3 +1395,34 @@ gkd_secrets_objects_handle_search_items (GkdSecretsObjects *self, DBusMessage *m
return reply;
}
+
+GP11Object*
+gkd_secrets_objects_lookup_collection (GkdSecretsObjects *self, const gchar *caller,
+ const gchar *objpath)
+{
+ GP11Session *session;
+ GP11Object *coll;
+ gchar *coll_id;
+ gchar *item_id;
+
+ g_return_val_if_fail (GKD_SECRETS_IS_OBJECTS (self), NULL);
+ g_return_val_if_fail (objpath, NULL);
+ g_return_val_if_fail (caller, NULL);
+
+ /* The session we're using to access the object */
+ session = gkd_secrets_service_get_pkcs11_session (self->service, caller);
+ g_return_val_if_fail (session, NULL);
+
+ /* Figure out which collection or item we're talking about */
+ if (!parse_collection_and_item_from_path (objpath, &coll_id, &item_id))
+ return NULL;
+
+ g_return_val_if_fail (coll_id, NULL);
+ coll = collection_for_identifier (session, coll_id);
+
+ g_free (coll_id);
+ g_free (item_id);
+
+ return coll;
+
+}
diff --git a/daemon/dbus/gkd-secrets-objects.h b/daemon/dbus/gkd-secrets-objects.h
index 2fcf4ab..2ec1453 100644
--- a/daemon/dbus/gkd-secrets-objects.h
+++ b/daemon/dbus/gkd-secrets-objects.h
@@ -61,4 +61,8 @@ void gkd_secrets_objects_append_item_paths (GkdSecretsObje
GP11Slot* gkd_secrets_objects_get_pkcs11_slot (GkdSecretsObjects *self);
+GP11Object* gkd_secrets_objects_lookup_collection (GkdSecretsObjects *self,
+ const gchar *caller,
+ const gchar *objpath);
+
#endif /* __GKD_SECRETS_OBJECTS_H__ */
diff --git a/daemon/dbus/gkd-secrets-prompt.c b/daemon/dbus/gkd-secrets-prompt.c
new file mode 100644
index 0000000..ec4246b
--- /dev/null
+++ b/daemon/dbus/gkd-secrets-prompt.c
@@ -0,0 +1,387 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * This program 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.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gkd-secrets-service.h"
+#include "gkd-secrets-prompt.h"
+#include "gkd-secrets-objects.h"
+#include "gkd-secrets-types.h"
+#include "gkd-dbus-util.h"
+
+#include "prompt/gkd-prompt.h"
+
+#include <string.h>
+
+enum {
+ PROP_0,
+ PROP_CALLER,
+ PROP_OBJECT_PATH,
+ PROP_SERVICE
+};
+
+struct _GkdSecretsPromptPrivate {
+ GkdPrompt parent;
+ gchar *object_path;
+ GkdSecretsService *service;
+ gboolean prompted;
+ gboolean completed;
+ gchar *caller;
+ gchar *window_id;
+ GList *objects;
+};
+
+G_DEFINE_TYPE (GkdSecretsPrompt, gkd_secrets_prompt, GKD_TYPE_PROMPT);
+
+static guint unique_prompt_number = 0;
+
+/* -----------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+static GkdPrompt*
+on_prompt_attention (gpointer user_data)
+{
+ GkdSecretsPrompt *self = user_data;
+
+ /* Check with the derived class */
+ g_return_val_if_fail (GKD_SECRETS_PROMPT_GET_CLASS (self)->prompt_ready, NULL);
+ GKD_SECRETS_PROMPT_GET_CLASS (self)->prompt_ready (self);
+
+ if (self->pv->completed)
+ return NULL;
+ return g_object_ref (self);
+}
+
+static void
+emit_completed (GkdSecretsPrompt *self, gboolean dismissed)
+{
+ DBusMessage *signal;
+ DBusMessageIter iter;
+ dbus_bool_t bval;
+
+ signal = dbus_message_new_signal (self->pv->object_path, SECRETS_PROMPT_INTERFACE,
+ "Completed");
+ dbus_message_set_destination (signal, self->pv->caller);
+ dbus_message_iter_init_append (signal, &iter);
+
+ g_return_if_fail (GKD_SECRETS_PROMPT_GET_CLASS (self)->encode_result);
+ GKD_SECRETS_PROMPT_GET_CLASS (self)->encode_result (self, &iter);
+
+ bval = dismissed;
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &bval);
+
+ gkd_secrets_service_send (self->pv->service, signal);
+ dbus_message_unref (signal);
+}
+
+/* -----------------------------------------------------------------------------
+ * DBUS
+ */
+
+static DBusMessage*
+prompt_method_prompt (GkdSecretsPrompt *self, DBusMessage *message)
+{
+ DBusMessage *reply;
+ const char *window_id;
+
+ /* Act as if this object no longer exists */
+ if (self->pv->completed)
+ return NULL;
+
+ if (!dbus_message_get_args (message, NULL, DBUS_TYPE_STRING,
+ &window_id, DBUS_TYPE_INVALID))
+ return NULL;
+
+ /* Prompt can only be called once */
+ if (self->pv->prompted)
+ return dbus_message_new_error (message, SECRETS_ERROR_ALREADY_EXISTS,
+ "This prompt has already been shown.");
+
+ gkd_prompt_set_window_id (GKD_PROMPT (self), window_id);
+ gkd_prompt_request_attention_async (window_id, on_prompt_attention,
+ g_object_ref (self), g_object_unref);
+ self->pv->prompted = TRUE;
+
+ reply = dbus_message_new_method_return (message);
+ dbus_message_append_args (reply, DBUS_TYPE_INVALID);
+ return reply;
+}
+
+static DBusMessage*
+prompt_method_dismiss (GkdSecretsPrompt *self, DBusMessage *message)
+{
+ DBusMessage *reply;
+
+ /* Act as if this object no longer exists */
+ if (self->pv->completed)
+ return NULL;
+
+ if (!dbus_message_get_args (message, NULL, DBUS_TYPE_INVALID))
+ return NULL;
+
+ gkd_secrets_prompt_dismiss (self);
+
+ reply = dbus_message_new_method_return (message);
+ dbus_message_append_args (reply, DBUS_TYPE_INVALID);
+ return reply;
+}
+
+/* -----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+static gboolean
+gkd_secrets_prompt_responded (GkdPrompt *base)
+{
+ GkdSecretsPrompt *self = GKD_SECRETS_PROMPT (base);
+ gint res;
+
+ res = gkd_prompt_get_response (GKD_PROMPT (self));
+ if (res == GKD_RESPONSE_NO || res == GKD_RESPONSE_FAILURE) {
+ gkd_secrets_prompt_dismiss (self);
+ return TRUE;
+ }
+
+ /* Check with the prompt ready guys */
+ g_return_val_if_fail (GKD_SECRETS_PROMPT_GET_CLASS (self)->prompt_ready, TRUE);
+ GKD_SECRETS_PROMPT_GET_CLASS (self)->prompt_ready (self);
+ return self->pv->completed;
+}
+
+static void
+gkd_secrets_prompt_ready (GkdSecretsPrompt *self)
+{
+ /* Default implementation, unused */
+ g_return_if_reached ();
+}
+
+static void
+gkd_secrets_prompt_encode_result (GkdSecretsPrompt *self, DBusMessageIter *iter)
+{
+ /* Default implementation, unused */
+ g_return_if_reached ();
+}
+
+static GObject*
+gkd_secrets_prompt_constructor (GType type, guint n_props, GObjectConstructParam *props)
+{
+ GkdSecretsPrompt *self = GKD_SECRETS_PROMPT (G_OBJECT_CLASS (gkd_secrets_prompt_parent_class)->constructor(type, n_props, props));
+
+ g_return_val_if_fail (self, NULL);
+ g_return_val_if_fail (self->pv->caller, NULL);
+ g_return_val_if_fail (self->pv->service, NULL);
+
+ /* Setup the path for the object */
+ self->pv->object_path = g_strdup_printf (SECRETS_PROMPT_PREFIX "/p%d", ++unique_prompt_number);
+
+ return G_OBJECT (self);
+}
+
+static void
+gkd_secrets_prompt_init (GkdSecretsPrompt *self)
+{
+ self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GKD_SECRETS_TYPE_PROMPT, GkdSecretsPromptPrivate);
+}
+
+static void
+gkd_secrets_prompt_dispose (GObject *obj)
+{
+ GkdSecretsPrompt *self = GKD_SECRETS_PROMPT (obj);
+
+ g_free (self->pv->object_path);
+ self->pv->object_path = NULL;
+
+ if (self->pv->service) {
+ g_object_remove_weak_pointer (G_OBJECT (self->pv->service),
+ (gpointer*)&(self->pv->service));
+ self->pv->service = NULL;
+ }
+
+ G_OBJECT_CLASS (gkd_secrets_prompt_parent_class)->dispose (obj);
+}
+
+static void
+gkd_secrets_prompt_finalize (GObject *obj)
+{
+ GkdSecretsPrompt *self = GKD_SECRETS_PROMPT (obj);
+
+ g_assert (!self->pv->object_path);
+ g_assert (!self->pv->service);
+
+ g_free (self->pv->caller);
+ self->pv->caller = NULL;
+
+ G_OBJECT_CLASS (gkd_secrets_prompt_parent_class)->finalize (obj);
+}
+
+static void
+gkd_secrets_prompt_set_property (GObject *obj, guint prop_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ GkdSecretsPrompt *self = GKD_SECRETS_PROMPT (obj);
+
+ switch (prop_id) {
+ case PROP_CALLER:
+ g_return_if_fail (!self->pv->caller);
+ self->pv->caller = g_value_dup_string (value);
+ break;
+ case PROP_SERVICE:
+ g_return_if_fail (!self->pv->service);
+ self->pv->service = g_value_get_object (value);
+ g_return_if_fail (self->pv->service);
+ g_object_add_weak_pointer (G_OBJECT (self->pv->service),
+ (gpointer*)&(self->pv->service));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gkd_secrets_prompt_get_property (GObject *obj, guint prop_id, GValue *value,
+ GParamSpec *pspec)
+{
+ GkdSecretsPrompt *self = GKD_SECRETS_PROMPT (obj);
+
+ switch (prop_id) {
+ case PROP_CALLER:
+ g_value_set_string (value, gkd_secrets_prompt_get_caller (self));
+ break;
+ case PROP_OBJECT_PATH:
+ g_value_set_boxed (value, gkd_secrets_prompt_get_object_path (self));
+ break;
+ case PROP_SERVICE:
+ g_value_set_object (value, self->pv->service);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gkd_secrets_prompt_class_init (GkdSecretsPromptClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GkdPromptClass *prompt_class = GKD_PROMPT_CLASS (klass);
+
+ gobject_class->constructor = gkd_secrets_prompt_constructor;
+ gobject_class->dispose = gkd_secrets_prompt_dispose;
+ gobject_class->finalize = gkd_secrets_prompt_finalize;
+ gobject_class->set_property = gkd_secrets_prompt_set_property;
+ gobject_class->get_property = gkd_secrets_prompt_get_property;
+
+ prompt_class->responded = gkd_secrets_prompt_responded;
+
+ klass->encode_result = gkd_secrets_prompt_encode_result;
+ klass->prompt_ready = gkd_secrets_prompt_ready;
+
+ g_type_class_add_private (klass, sizeof (GkdSecretsPromptPrivate));
+
+ g_object_class_install_property (gobject_class, PROP_CALLER,
+ g_param_spec_string ("caller", "Caller", "DBus caller name",
+ NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY ));
+
+ g_object_class_install_property (gobject_class, PROP_OBJECT_PATH,
+ g_param_spec_string ("object-path", "Object Path", "DBus Object Path",
+ NULL, G_PARAM_READABLE));
+
+ g_object_class_install_property (gobject_class, PROP_SERVICE,
+ g_param_spec_object ("service", "Service", "Service which owns this prompt",
+ GKD_SECRETS_TYPE_SERVICE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+DBusMessage*
+gkd_secrets_prompt_dispatch (GkdSecretsPrompt *self, DBusMessage *message)
+{
+ DBusMessage *reply = NULL;
+ const gchar *caller;
+
+ g_return_val_if_fail (message, NULL);
+ g_return_val_if_fail (GKD_SECRETS_IS_PROMPT (self), NULL);
+
+ /* This should already have been caught elsewhere */
+ caller = dbus_message_get_sender (message);
+ if (!caller || !g_str_equal (caller, self->pv->caller))
+ g_return_val_if_reached (NULL);
+
+ /* org.freedesktop.Secrets.Prompt.Prompt() */
+ else if (dbus_message_is_method_call (message, SECRETS_PROMPT_INTERFACE, "Prompt"))
+ reply = prompt_method_prompt (self, message);
+
+ /* org.freedesktop.Secrets.Prompt.Negotiate() */
+ else if (dbus_message_is_method_call (message, SECRETS_PROMPT_INTERFACE, "Dismiss"))
+ reply = prompt_method_dismiss (self, message);
+
+ return reply;
+}
+
+const gchar*
+gkd_secrets_prompt_get_caller (GkdSecretsPrompt *self)
+{
+ g_return_val_if_fail (GKD_SECRETS_IS_PROMPT (self), NULL);
+ return self->pv->caller;
+}
+
+const gchar*
+gkd_secrets_prompt_get_object_path (GkdSecretsPrompt *self)
+{
+ g_return_val_if_fail (GKD_SECRETS_IS_PROMPT (self), NULL);
+ return self->pv->object_path;
+}
+
+void
+gkd_secrets_prompt_complete (GkdSecretsPrompt *self)
+{
+ g_return_if_fail (GKD_SECRETS_IS_PROMPT (self));
+ g_return_if_fail (!self->pv->completed);
+ self->pv->completed = TRUE;
+ emit_completed (self, TRUE);
+}
+
+void
+gkd_secrets_prompt_dismiss (GkdSecretsPrompt *self)
+{
+ g_return_if_fail (GKD_SECRETS_IS_PROMPT (self));
+ g_return_if_fail (!self->pv->completed);
+ self->pv->completed = TRUE;
+ emit_completed (self, FALSE);
+}
+
+GP11Object*
+gkd_secrets_prompt_lookup_collection (GkdSecretsPrompt *self, const gchar *objpath)
+{
+ GkdSecretsObjects *objects;
+
+ g_return_val_if_fail (GKD_SECRETS_IS_PROMPT (self), NULL);
+ g_return_val_if_fail (self->pv->service, NULL);
+
+ objects = gkd_secrets_service_get_objects (self->pv->service);
+ g_return_val_if_fail (objects, NULL);
+
+ return gkd_secrets_objects_lookup_collection (objects, self->pv->caller, objpath);
+}
diff --git a/daemon/dbus/gkd-secrets-prompt.h b/daemon/dbus/gkd-secrets-prompt.h
new file mode 100644
index 0000000..9c57931
--- /dev/null
+++ b/daemon/dbus/gkd-secrets-prompt.h
@@ -0,0 +1,70 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2009 Stefan Walter
+ *
+ * This program 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.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GKD_SECRETS_PROMPT_H__
+#define __GKD_SECRETS_PROMPT_H__
+
+#include <glib-object.h>
+
+#include "gkd-secrets-types.h"
+
+#include "prompt/gkd-prompt.h"
+
+#define GKD_SECRETS_TYPE_PROMPT (gkd_secrets_prompt_get_type ())
+#define GKD_SECRETS_PROMPT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GKD_SECRETS_TYPE_PROMPT, GkdSecretsPrompt))
+#define GKD_SECRETS_PROMPT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GKD_SECRETS_TYPE_PROMPT, GkdSecretsPromptClass))
+#define GKD_SECRETS_IS_PROMPT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GKD_SECRETS_TYPE_PROMPT))
+#define GKD_SECRETS_IS_PROMPT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GKD_SECRETS_TYPE_PROMPT))
+#define GKD_SECRETS_PROMPT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GKD_SECRETS_TYPE_PROMPT, GkdSecretsPromptClass))
+
+typedef struct _GkdSecretsPromptClass GkdSecretsPromptClass;
+typedef struct _GkdSecretsPromptPrivate GkdSecretsPromptPrivate;
+
+struct _GkdSecretsPrompt {
+ GkdPrompt parent;
+ GkdSecretsPromptPrivate *pv;
+};
+
+struct _GkdSecretsPromptClass {
+ GObjectClass parent_class;
+
+ /* virtual methods */
+ void (*prompt_ready) (GkdSecretsPrompt *self);
+ void (*encode_result) (GkdSecretsPrompt *self, DBusMessageIter *iter);
+};
+
+GType gkd_secrets_prompt_get_type (void);
+
+DBusMessage* gkd_secrets_prompt_dispatch (GkdSecretsPrompt *self,
+ DBusMessage *message);
+
+const gchar* gkd_secrets_prompt_get_caller (GkdSecretsPrompt *self);
+
+const gchar* gkd_secrets_prompt_get_object_path (GkdSecretsPrompt *self);
+
+GP11Object* gkd_secrets_prompt_lookup_collection (GkdSecretsPrompt *self,
+ const gchar *objpath);
+
+void gkd_secrets_prompt_complete (GkdSecretsPrompt *self);
+
+void gkd_secrets_prompt_dismiss (GkdSecretsPrompt *self);
+
+#endif /* __GKD_SECRETS_PROMPT_H__ */
diff --git a/daemon/dbus/gkd-secrets-service.c b/daemon/dbus/gkd-secrets-service.c
index b27d0e3..68871fa 100644
--- a/daemon/dbus/gkd-secrets-service.c
+++ b/daemon/dbus/gkd-secrets-service.c
@@ -23,9 +23,11 @@
#include "gkd-dbus-util.h"
#include "gkd-secrets-objects.h"
+#include "gkd-secrets-prompt.h"
#include "gkd-secrets-service.h"
#include "gkd-secrets-session.h"
#include "gkd-secrets-types.h"
+#include "gkd-secrets-unlock.h"
#include "egg/egg-unix-credentials.h"
@@ -67,7 +69,8 @@ typedef struct _ServiceClient {
pid_t caller_pid;
CK_G_APPLICATION app;
GP11Session *pkcs11_session;
- GList *sessions;
+ GHashTable *sessions;
+ GHashTable *prompts;
} ServiceClient;
/* Forward declaration */
@@ -113,17 +116,17 @@ object_path_has_prefix (const gchar *path, const gchar *prefix)
}
static void
-dispose_session (GkdSecretsSession *session)
+dispose_and_unref (gpointer object)
{
- g_object_run_dispose (G_OBJECT (session));
- g_object_unref (session);
+ g_return_if_fail (G_IS_OBJECT (object));
+ g_object_run_dispose (G_OBJECT (object));
+ g_object_unref (object);
}
static void
free_client (gpointer data)
{
ServiceClient *client = data;
- GList *l;
if (!client)
return;
@@ -140,9 +143,9 @@ free_client (gpointer data)
g_object_unref (client->pkcs11_session);
}
- /* The secrets API sessions client has open */
- for (l = client->sessions; l; l = g_list_next (l))
- dispose_session (l->data);
+ /* The sessions and prompts the client has open */
+ g_hash_table_destroy (client->sessions);
+ g_hash_table_destroy (client->prompts);
g_free (client);
}
@@ -206,6 +209,8 @@ on_get_connection_unix_process_id (DBusPendingCall *pending, gpointer user_data)
if (caller_pid != 0)
client->caller_exec = egg_unix_credentials_executable (caller_pid);
client->app.applicationData = client;
+ client->sessions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, dispose_and_unref);
+ client->prompts = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, dispose_and_unref);
g_hash_table_replace (self->clients, client->caller_peer, client);
}
@@ -256,7 +261,6 @@ initialize_service_client (GkdSecretsService *self, DBusMessage *message)
dbus_pending_call_unref (pending);
}
-
/* -----------------------------------------------------------------------------
* DBUS
*/
@@ -355,16 +359,62 @@ service_method_open_session (GkdSecretsService *self, DBusMessage *message)
/* Take ownership of the session */
client = g_hash_table_lookup (self->clients, caller);
g_return_val_if_fail (client, NULL);
- client->sessions = g_list_prepend (client->sessions, session);
+ path = gkd_secrets_session_get_object_path (session);
+ g_return_val_if_fail (!g_hash_table_lookup (client->sessions, path), NULL);
+ g_hash_table_replace (client->sessions, (gpointer)path, session);
/* Return the response */
- path = gkd_secrets_session_get_object_path (session);
reply = dbus_message_new_method_return (message);
dbus_message_append_args (reply, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
return reply;
}
static DBusMessage*
+service_method_unlock (GkdSecretsService *self, DBusMessage *message)
+{
+ char **objpaths, **o;
+ GkdSecretsUnlock *unlock;
+ ServiceClient *client;
+ DBusMessage *reply;
+ const char *caller;
+ const gchar *path;
+
+ if (!dbus_message_get_args (message, NULL,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objpaths,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ caller = dbus_message_get_sender (message);
+ unlock = gkd_secrets_unlock_new (self, caller);
+ for (o = objpaths; o && *o; ++o)
+ gkd_secrets_unlock_queue (unlock, *o);
+ dbus_free_string_array (objpaths);
+
+ /* So do we need to prompt? */
+ if (gkd_secrets_unlock_have_queued (unlock)) {
+ client = g_hash_table_lookup (self->clients, caller);
+ g_return_val_if_fail (client, NULL);
+ path = gkd_secrets_prompt_get_object_path (GKD_SECRETS_PROMPT (unlock));
+ g_hash_table_replace (client->sessions, (gpointer)path, g_object_ref (unlock));
+
+ /* No need to prompt */
+ } else {
+ path = "";
+ }
+
+ reply = dbus_message_new_method_return (message);
+ dbus_message_append_args (reply,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, gkd_secrets_unlock_get_results (unlock),
+ DBUS_TYPE_OBJECT_PATH, path,
+ DBUS_TYPE_INVALID);
+
+ gkd_secrets_unlock_reset_results (unlock);
+ g_object_unref (unlock);
+
+ return reply;
+}
+
+static DBusMessage*
service_message_handler (GkdSecretsService *self, DBusMessage *message)
{
DBusMessage *reply = NULL;
@@ -388,9 +438,9 @@ service_message_handler (GkdSecretsService *self, DBusMessage *message)
if (dbus_message_is_method_call (message, SECRETS_SERVICE_INTERFACE, "SearchItems"))
return gkd_secrets_objects_handle_search_items (self->objects, message, NULL);
- /* org.freedesktop.Secrets.Service.BeginAuthenticate() */
- if (dbus_message_is_method_call (message, SECRETS_SERVICE_INTERFACE, "BeginAuthenticate"))
- g_return_val_if_reached (NULL); /* TODO: Need to implement */
+ /* org.freedesktop.Secrets.Service.Unlock() */
+ if (dbus_message_is_method_call (message, SECRETS_SERVICE_INTERFACE, "Unlock"))
+ reply = service_method_unlock (self, message);
/* org.freedesktop.Secrets.Service.CompleteAuthenticate() */
if (dbus_message_is_method_call (message, SECRETS_SERVICE_INTERFACE, "CompleteAuthenticate"))
@@ -421,7 +471,7 @@ service_dispatch_message (GkdSecretsService *self, DBusMessage *message)
const gchar *caller;
ServiceClient *client;
const gchar *path;
- GList *l;
+ gpointer object;
g_assert (GKD_SECRETS_IS_SERVICE (self));
g_assert (message);
@@ -447,12 +497,15 @@ service_dispatch_message (GkdSecretsService *self, DBusMessage *message)
/* Dispatched to a session, find a session in this client */
if (object_path_has_prefix (path, SECRETS_SESSION_PREFIX)) {
- for (l = client->sessions; l; l = g_list_next (l)) {
- if (g_str_equal (path, gkd_secrets_session_get_object_path (l->data))) {
- reply = gkd_secrets_session_dispatch (l->data, message);
- break;
- }
- }
+ object = g_hash_table_lookup (client->sessions, path);
+ if (object != NULL)
+ reply = gkd_secrets_session_dispatch (object, message);
+
+ /* Dispatched to a prompt, find a prompt in this client */
+ } else if (object_path_has_prefix (path, SECRETS_PROMPT_PREFIX)) {
+ object = g_hash_table_lookup (client->prompts, path);
+ if (object != NULL)
+ reply = gkd_secrets_prompt_dispatch (object, message);
/* Dispatched to a collection, off it goes */
} else if (object_path_has_prefix (path, SECRETS_COLLECTION_PREFIX)) {
@@ -607,7 +660,7 @@ gkd_secrets_service_dispose (GObject *obj)
self->match_rule = NULL;
}
- /* Closes all the sessions */
+ /* Closes all the clients */
g_hash_table_remove_all (self->clients);
/* Hide all the objects */
@@ -707,6 +760,20 @@ gkd_secrets_service_class_init (GkdSecretsServiceClass *klass)
* PUBLIC
*/
+void
+gkd_secrets_service_send (GkdSecretsService *self, DBusMessage *message)
+{
+ g_return_if_fail (GKD_SECRETS_IS_SERVICE (self));
+ dbus_connection_send (self->connection, message, NULL);
+}
+
+GkdSecretsObjects*
+gkd_secrets_service_get_objects (GkdSecretsService *self)
+{
+ g_return_val_if_fail (GKD_SECRETS_IS_SERVICE (self), NULL);
+ return self->objects;
+}
+
DBusConnection*
gkd_secrets_service_get_connection (GkdSecretsService *self)
{
@@ -757,6 +824,7 @@ gkd_secrets_service_close_session (GkdSecretsService *self, GkdSecretsSession *s
{
ServiceClient *client;
const gchar *caller;
+ const gchar *path;
g_return_if_fail (GKD_SECRETS_IS_SERVICE (self));
g_return_if_fail (GKD_SECRETS_IS_SESSION (session));
@@ -765,8 +833,8 @@ gkd_secrets_service_close_session (GkdSecretsService *self, GkdSecretsSession *s
client = g_hash_table_lookup (self->clients, caller);
g_return_if_fail (client);
- client->sessions = g_list_remove (client->sessions, session);
- dispose_session (session);
+ path = gkd_secrets_session_get_object_path (session);
+ g_hash_table_remove (client->sessions, path);
}
#if 0
diff --git a/daemon/dbus/gkd-secrets-service.h b/daemon/dbus/gkd-secrets-service.h
index 063b6d5..f63dfc9 100644
--- a/daemon/dbus/gkd-secrets-service.h
+++ b/daemon/dbus/gkd-secrets-service.h
@@ -57,12 +57,18 @@ GP11Slot* gkd_secrets_service_get_pkcs11_slot (GkdSecretsSe
GP11Session* gkd_secrets_service_get_pkcs11_session (GkdSecretsService *self,
const gchar *caller);
+GkdSecretsObjects* gkd_secrets_service_get_objects (GkdSecretsService *self);
+
#if 0
void gkd_secrets_service_refresh (GkdSecretsService *self);
#endif
void gkd_secrets_service_close_session (GkdSecretsService *self,
GkdSecretsSession *sess);
+
+void gkd_secrets_service_send (GkdSecretsService *self,
+ DBusMessage *message);
+
#if 0
GkdSecretsCollection* gkd_secrets_service_get_default_collection (GkdSecretsService *self);
#endif
diff --git a/daemon/dbus/gkd-secrets-types.h b/daemon/dbus/gkd-secrets-types.h
index 081b89d..39183d7 100644
--- a/daemon/dbus/gkd-secrets-types.h
+++ b/daemon/dbus/gkd-secrets-types.h
@@ -25,9 +25,10 @@
#define BUS_INTERFACE "org.freedesktop.DBus"
#define PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties"
-#define SECRETS_SERVICE_INTERFACE "org.freedesktop.Secrets.Service"
#define SECRETS_COLLECTION_INTERFACE "org.freedesktop.Secrets.Collection"
#define SECRETS_ITEM_INTERFACE "org.freedesktop.Secrets.Item"
+#define SECRETS_PROMPT_INTERFACE "org.freedesktop.Secrets.Prompt"
+#define SECRETS_SERVICE_INTERFACE "org.freedesktop.Secrets.Service"
#define SECRETS_SERVICE_PATH "/org/freedesktop/secrets"
#define SECRETS_SERVICE "org.freedesktop.secrets"
@@ -35,6 +36,7 @@
#define SECRETS_INTERFACE_PREFIX "org.freedesktop.Secrets"
#define SECRETS_COLLECTION_PREFIX "/org/freedesktop/secrets/collection"
#define SECRETS_SESSION_PREFIX "/org/freedesktop/secrets/session"
+#define SECRETS_PROMPT_PREFIX "/org/freedesktop/secrets/prompt"
#define SECRETS_ERROR_ALREADY_EXISTS "org.freedesktop.Secrets.Error.AlreadyExists"
#define SECRETS_ERROR_IS_LOCKED "org.freedesktop.Secrets.Error.IsLocked"
@@ -44,7 +46,9 @@
typedef struct _GkdSecretsCollection GkdSecretsCollection;
typedef struct _GkdSecretsItem GkdSecretsItem;
typedef struct _GkdSecretsObjects GkdSecretsObjects;
+typedef struct _GkdSecretsPrompt GkdSecretsPrompt;
typedef struct _GkdSecretsService GkdSecretsService;
typedef struct _GkdSecretsSession GkdSecretsSession;
+typedef struct _GkdSecretsUnlock GkdSecretsUnlock;
#endif /* __GKD_SECRETS_TYPES_H__ */
diff --git a/daemon/dbus/gkd-secrets-unlock.c b/daemon/dbus/gkd-secrets-unlock.c
new file mode 100644
index 0000000..ce7df4c
--- /dev/null
+++ b/daemon/dbus/gkd-secrets-unlock.c
@@ -0,0 +1,351 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * This program 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.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gkd-secrets-service.h"
+#include "gkd-secrets-prompt.h"
+#include "gkd-secrets-types.h"
+#include "gkd-secrets-unlock.h"
+
+#include "egg/egg-secure-memory.h"
+
+#include "pkcs11/pkcs11i.h"
+
+#include <glib/gi18n.h>
+
+#include <gp11/gp11.h>
+
+#include <string.h>
+
+struct _GkdSecretsUnlock {
+ GkdSecretsPrompt parent;
+ GQueue *queued;
+ gchar *current;
+ GArray *results;
+};
+
+G_DEFINE_TYPE (GkdSecretsUnlock, gkd_secrets_unlock, GKD_SECRETS_TYPE_UNLOCK);
+
+/* -----------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+static void
+prepare_unlock_prompt (GkdSecretsUnlock *self, GP11Object *coll)
+{
+ GError *error = NULL;
+ GkdPrompt *prompt;
+ gpointer data;
+ gsize n_data;
+ gchar *label;
+ gchar *text;
+
+ g_assert (GKD_SECRETS_IS_UNLOCK (self));
+ g_assert (coll);
+
+ prompt = GKD_PROMPT (self);
+
+ data = gp11_object_get_data (coll, CKA_LABEL, &n_data, &error);
+ if (!data) {
+ g_warning ("couldn't get label for collection: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ if (!data || !n_data)
+ label = g_strdup (_("Unnamed"));
+ else
+ label = g_strndup (data, n_data);
+ g_free (data);
+
+ gkd_prompt_reset (prompt);
+
+ gkd_prompt_set_title (prompt, _("Unlock Keyring"));
+
+ text = g_markup_printf_escaped (_("Enter password for keyring '%s' to unlock"), label);
+ gkd_prompt_set_primary_text (prompt, text);
+ g_free (text);
+
+ text = g_markup_printf_escaped (_("An application wants access to the keyring '%s', but it is locked"), label);
+ gkd_prompt_set_secondary_text (prompt, text);
+ g_free (text);
+
+ gkd_prompt_hide_widget (prompt, "name_area");
+ gkd_prompt_hide_widget (prompt, "original_area");
+ gkd_prompt_hide_widget (prompt, "confirm_area");
+ gkd_prompt_hide_widget (prompt, "details_area");
+
+ g_free (label);
+}
+
+static void
+set_warning_wrong (GkdSecretsUnlock *self)
+{
+ g_assert (GKD_SECRETS_IS_UNLOCK (self));
+ gkd_prompt_set_warning (GKD_PROMPT (self), _("The unlock password was incorrect"));
+}
+
+static gboolean
+authenticate_collection (GkdSecretsUnlock *self, GP11Object *coll, gboolean *locked)
+{
+ GError *error = NULL;
+ GP11Attributes *attrs;
+ GP11Session *session;
+ GP11Object *auth;
+ gchar *password;
+ gsize n_password;
+
+ g_assert (GKD_SECRETS_IS_UNLOCK (self));
+ g_assert (locked);
+ g_assert (coll);
+
+ attrs = gp11_object_get (coll, &error, CKA_G_LOCKED, GP11_INVALID);
+ if (!attrs) {
+ if (error->code != CKR_OBJECT_HANDLE_INVALID)
+ g_warning ("couldn't check locked status of collection: %s",
+ error->message);
+ g_clear_error (&error);
+ return FALSE;
+ }
+
+ if (!gp11_attributes_find_boolean (attrs, CKA_G_LOCKED, locked))
+ g_return_val_if_reached (FALSE);
+ if (!locked)
+ return TRUE;
+
+ session = gp11_object_get_session (coll);
+ g_return_val_if_fail (session, FALSE);
+
+ if (!gkd_prompt_has_response (GKD_PROMPT (self)))
+ return TRUE; /* Bail out early, just checking locked status */
+
+ password = gkd_prompt_get_password (GKD_PROMPT (self), "password");
+ n_password = password ? strlen (password) : 0;
+ auth = gp11_session_create_object (session, &error,
+ CKA_CLASS, GP11_ULONG, CKO_GNOME_AUTHENTICATOR,
+ CKA_GNOME_OBJECT, GP11_ULONG, gp11_object_get_handle (coll),
+ CKA_GNOME_TRANSIENT, GP11_BOOLEAN, TRUE,
+ CKA_TOKEN, GP11_BOOLEAN, FALSE,
+ CKA_VALUE, n_password, password,
+ GP11_INVALID);
+ egg_secure_strfree (password);
+ g_object_unref (session);
+
+ if (auth) {
+ g_object_unref (auth);
+ *locked = FALSE;
+ return TRUE; /* Operation succeeded, and unlocked */
+
+ } else {
+ if (error->code == (gint)CKR_PIN_INCORRECT) {
+ g_clear_error (&error);
+ *locked = TRUE;
+ return TRUE; /* Operation succeded, although not unlocked*/
+
+ } else {
+ g_warning ("couldn't create authenticator for collection: %s",
+ error->message);
+ g_clear_error (&error);
+ return FALSE; /* Operation failed */
+ }
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+static void
+gkd_secrets_unlock_prompt_ready (GkdSecretsPrompt *base)
+{
+ GkdSecretsUnlock *self = GKD_SECRETS_UNLOCK (base);
+ GP11Object *coll;
+ gboolean locked;
+ gchar *objpath;
+
+ /* Already prompted for an item */
+ if (self->current) {
+ coll = gkd_secrets_prompt_lookup_collection (base, self->current);
+
+ /* If the object or collection is gone, no need to unlock */
+ if (coll == NULL) {
+ g_free (self->current);
+ self->current = NULL;
+
+ } else {
+ /* Try to unlock the collection */
+ if (!authenticate_collection (self, coll, &locked)) {
+ g_free (self->current);
+ self->current = NULL;
+
+ /* Collection still locked, prompt again */
+ } else if (locked) {
+ prepare_unlock_prompt (self, coll);
+ set_warning_wrong (self);
+
+ /* Collection not locked, done with this one */
+ } else {
+ g_array_append_val (self->results, self->current);
+ self->current = NULL;
+ }
+
+ g_object_unref (coll);
+ }
+ }
+
+ /* Queue the next item? */
+ while (!self->current) {
+ objpath = g_queue_pop_head (self->queued);
+
+ /* Nothing more to prompt for? */
+ if (!objpath) {
+ gkd_secrets_prompt_complete (base);
+ break;
+ }
+
+ /* Find the collection, make sure it's still around */
+ coll = gkd_secrets_prompt_lookup_collection (base, objpath);
+ if (coll == NULL) {
+ g_free (objpath);
+ continue;
+ }
+
+ /* Make sure this collection still needs unlocking */
+ if (!authenticate_collection (self, coll, &locked)) {
+ g_object_unref (coll);
+ g_free (objpath);
+ continue;
+ } else if (!locked) {
+ g_array_append_val (self->results, self->current);
+ g_object_unref (coll);
+ continue;
+ }
+
+ prepare_unlock_prompt (self, coll);
+ g_object_unref (coll);
+ self->current = objpath;
+ }
+}
+
+static void
+gkd_secrets_unlock_encode_result (GkdSecretsPrompt *base, DBusMessageIter *iter)
+{
+ GkdSecretsUnlock *self = GKD_SECRETS_UNLOCK (base);
+ DBusMessageIter variant;
+ DBusMessageIter array;
+ gint i;
+
+ dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT, "ao", &variant);
+ dbus_message_iter_open_container (&variant, DBUS_TYPE_ARRAY, "o", &array);
+
+ for (i = 0; i < self->results->len; ++i)
+ dbus_message_iter_append_basic (&array, DBUS_TYPE_OBJECT_PATH,
+ g_array_index (self->results, gchar*, i));
+
+ dbus_message_iter_close_container (&variant, &array);
+ dbus_message_iter_close_container (iter, &variant);
+}
+
+static void
+gkd_secrets_unlock_init (GkdSecretsUnlock *self)
+{
+ self->queued = g_queue_new ();
+ self->results = g_array_new (TRUE, TRUE, sizeof (gchar*));
+}
+
+static void
+gkd_secrets_unlock_finalize (GObject *obj)
+{
+ GkdSecretsUnlock *self = GKD_SECRETS_UNLOCK (obj);
+
+ if (self->queued) {
+ while (!g_queue_is_empty (self->queued))
+ g_free (g_queue_pop_head (self->queued));
+ g_queue_free (self->queued);
+ self->queued = NULL;
+ }
+
+ if (self->results) {
+ gkd_secrets_unlock_reset_results (self);
+ g_array_free (self->results, TRUE);
+ self->results = NULL;
+ }
+
+ g_free (self->current);
+ self->current = NULL;
+
+ G_OBJECT_CLASS (gkd_secrets_unlock_parent_class)->finalize (obj);
+}
+
+static void
+gkd_secrets_unlock_class_init (GkdSecretsUnlockClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GkdSecretsPromptClass *prompt_class = GKD_SECRETS_PROMPT_CLASS (klass);
+
+ gobject_class->finalize = gkd_secrets_unlock_finalize;
+ prompt_class->prompt_ready = gkd_secrets_unlock_prompt_ready;
+ prompt_class->encode_result = gkd_secrets_unlock_encode_result;
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+GkdSecretsUnlock*
+gkd_secrets_unlock_new (GkdSecretsService *service, const gchar *caller)
+{
+ return g_object_new (GKD_SECRETS_TYPE_UNLOCK, "service", service, "caller", caller, NULL);
+}
+
+void
+gkd_secrets_unlock_queue (GkdSecretsUnlock *self, const gchar *objpath)
+{
+ g_return_if_fail (GKD_SECRETS_IS_UNLOCK (self));
+ g_return_if_fail (objpath);
+ g_queue_push_tail (self->queued, g_strdup (objpath));
+}
+
+gboolean
+gkd_secrets_unlock_have_queued (GkdSecretsUnlock *self)
+{
+ g_return_val_if_fail (GKD_SECRETS_IS_UNLOCK (self), FALSE);
+ return !g_queue_is_empty (self->queued) && !self->current;
+}
+
+gchar**
+gkd_secrets_unlock_get_results (GkdSecretsUnlock *self)
+{
+ g_return_val_if_fail (GKD_SECRETS_IS_UNLOCK (self), NULL);
+ return (gchar**)self->results->data;
+}
+
+void
+gkd_secrets_unlock_reset_results (GkdSecretsUnlock *self)
+{
+ gint i;
+
+ g_return_if_fail (GKD_SECRETS_IS_UNLOCK (self));
+
+ for (i = 0; i < self->results->len; ++i)
+ g_free (g_array_index (self->results, gchar*, i));
+ g_array_set_size (self->results, 0);
+}
diff --git a/daemon/dbus/gkd-secrets-unlock.h b/daemon/dbus/gkd-secrets-unlock.h
new file mode 100644
index 0000000..0dcdff5
--- /dev/null
+++ b/daemon/dbus/gkd-secrets-unlock.h
@@ -0,0 +1,56 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2009 Stefan Walter
+ *
+ * This program 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.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GKD_SECRETS_UNLOCK_H__
+#define __GKD_SECRETS_UNLOCK_H__
+
+#include <glib-object.h>
+
+#include "gkd-secrets-types.h"
+
+#define GKD_SECRETS_TYPE_UNLOCK (gkd_secrets_unlock_get_type ())
+#define GKD_SECRETS_UNLOCK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GKD_SECRETS_TYPE_UNLOCK, GkdSecretsUnlock))
+#define GKD_SECRETS_UNLOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GKD_SECRETS_TYPE_UNLOCK, GkdSecretsUnlockClass))
+#define GKD_SECRETS_IS_UNLOCK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GKD_SECRETS_TYPE_UNLOCK))
+#define GKD_SECRETS_IS_UNLOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GKD_SECRETS_TYPE_UNLOCK))
+#define GKD_SECRETS_UNLOCK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GKD_SECRETS_TYPE_UNLOCK, GkdSecretsUnlockClass))
+
+typedef struct _GkdSecretsUnlockClass GkdSecretsUnlockClass;
+
+struct _GkdSecretsUnlockClass {
+ GObjectClass parent_class;
+};
+
+GType gkd_secrets_unlock_get_type (void);
+
+GkdSecretsUnlock* gkd_secrets_unlock_new (GkdSecretsService *service,
+ const gchar *caller);
+
+void gkd_secrets_unlock_queue (GkdSecretsUnlock *self,
+ const gchar *objpath);
+
+gboolean gkd_secrets_unlock_have_queued (GkdSecretsUnlock *self);
+
+gchar** gkd_secrets_unlock_get_results (GkdSecretsUnlock *self);
+
+void gkd_secrets_unlock_reset_results (GkdSecretsUnlock *self);
+
+#endif /* __GKD_SECRETS_UNLOCK_H__ */
diff --git a/daemon/prompt/Makefile.am b/daemon/prompt/Makefile.am
new file mode 100644
index 0000000..57036aa
--- /dev/null
+++ b/daemon/prompt/Makefile.am
@@ -0,0 +1,29 @@
+
+INCLUDES = \
+ -I$(top_srcdir) \
+ -I$(top_builddir) \
+ -DLIBEXECDIR=\""$(libexecdir)"\" \
+ $(GOBJECT_CFLAGS) \
+ $(GLIB_CFLAGS)
+
+noinst_LTLIBRARIES = libgkd-prompt.la
+
+BUILT_SOURCES = \
+ gkd-prompt-marshal.c gkd-prompt-marshal.h
+
+libgkd_prompt_la_SOURCES = \
+ gkd-prompt.c gkd-prompt.h \
+ $(BUILT_SOURCES)
+
+libgkd_prompt_la_LIBADD = \
+ $(GLIB_LIBS) \
+ $(GOBJECT_LIBS)
+
+gkd-prompt-marshal.h: gkd-prompt-marshal.list $(GLIB_GENMARSHAL)
+ $(GLIB_GENMARSHAL) $< --header --prefix=gkd_prompt_marshal > $@
+
+gkd-prompt-marshal.c: gkd-prompt-marshal.list $(GLIB_GENMARSHAL)
+ echo "#include \"gkd-prompt-marshal.h\"" > $@ && \
+ $(GLIB_GENMARSHAL) $< --body --prefix=gkd_prompt_marshal >> $@
+
+EXTRA_DIST = gkd-prompt-marshal.list
diff --git a/daemon/prompt/gkd-prompt-marshal.list b/daemon/prompt/gkd-prompt-marshal.list
new file mode 100644
index 0000000..6ff7797
--- /dev/null
+++ b/daemon/prompt/gkd-prompt-marshal.list
@@ -0,0 +1 @@
+BOOLEAN:VOID
diff --git a/daemon/prompt/gkd-prompt.c b/daemon/prompt/gkd-prompt.c
new file mode 100644
index 0000000..89b0cc9
--- /dev/null
+++ b/daemon/prompt/gkd-prompt.c
@@ -0,0 +1,975 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * This program 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.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gkd-prompt.h"
+#include "gkd-prompt-marshal.h"
+
+#include "egg/egg-cleanup.h"
+#include "egg/egg-dh.h"
+#include "egg/egg-hex.h"
+#include "egg/egg-secure-memory.h"
+#include "egg/egg-spawn.h"
+
+#include <gcrypt.h>
+
+enum {
+ RESPONDED,
+ COMPLETED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+struct _GkdPromptPrivate {
+ GKeyFile *input;
+ GKeyFile *output;
+ gchar *executable;
+ gboolean completed;
+ gboolean failure;
+
+ /* Transport crypto */
+ gcry_mpi_t secret;
+ gcry_mpi_t prime;
+ guchar *key;
+ gsize n_key;
+
+ /* Information about child */
+ GPid pid;
+
+ /* Input and output */
+ gchar *in_data;
+ gsize in_offset;
+ gsize in_length;
+ GString *out_data;
+ GString *err_data;
+ guint io_tag;
+};
+
+G_DEFINE_TYPE (GkdPrompt, gkd_prompt, G_TYPE_OBJECT);
+
+/* -----------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+static void
+kill_process (GkdPrompt *self)
+{
+ if (self->pv->pid)
+ kill (self->pv->pid, SIGTERM);
+}
+
+static void
+mark_completed (GkdPrompt *self)
+{
+ g_assert (!self->pv->completed);
+ self->pv->completed = TRUE;
+ g_signal_emit (self, signals[COMPLETED], 0);
+}
+
+static gboolean
+on_standard_input (int fd, gpointer user_data)
+{
+ GkdPrompt *self = GKD_PROMPT (self);
+ gssize ret;
+
+ g_return_val_if_fail (GKD_IS_PROMPT (self), FALSE);
+
+ if (self->pv->in_offset >= self->pv->in_length)
+ return FALSE;
+
+ g_assert (self->pv->in_data);
+ ret = egg_spawn_write_input (fd, self->pv->in_data + self->pv->in_offset,
+ self->pv->in_length - self->pv->in_offset);
+
+ if (ret <= 0) {
+ g_warning ("couldn't write all input to prompt process");
+ self->pv->failure = TRUE;
+ return FALSE;
+ }
+
+ self->pv->in_offset += ret;
+ return TRUE;
+}
+
+static gboolean
+on_standard_output (int fd, gpointer user_data)
+{
+ GkdPrompt *self = GKD_PROMPT (self);
+ gchar buffer[1024];
+ gssize ret;
+
+ g_return_val_if_fail (GKD_IS_PROMPT (self), FALSE);
+
+ ret = egg_spawn_read_output (fd, buffer, sizeof (buffer));
+ if (ret < 0) {
+ g_warning ("couldn't read output data from prompt process");
+ self->pv->failure = TRUE;
+ return FALSE;
+ }
+
+ if (!self->pv->out_data)
+ self->pv->out_data = g_string_new_len (buffer, ret);
+ else
+ g_string_append_len (self->pv->out_data, buffer, ret);
+
+ return (ret > 0);
+}
+
+static gboolean
+on_standard_error (int fd, gpointer user_data)
+{
+ GkdPrompt *self = GKD_PROMPT (self);
+ gchar buffer[1024];
+ gssize ret;
+ gchar *ptr;
+
+ g_return_val_if_fail (GKD_IS_PROMPT (self), FALSE);
+
+ ret = egg_spawn_read_output (fd, buffer, sizeof (buffer));
+ if (ret < 0) {
+ g_warning ("couldn't read error data from prompt process");
+ self->pv->failure = TRUE;
+ return FALSE;
+ }
+
+ if (!self->pv->err_data)
+ self->pv->err_data = g_string_new_len (buffer, ret);
+ else
+ g_string_append_len (self->pv->err_data, buffer, ret);
+
+ /* Print all stderr lines as messages */
+ while ((ptr = strchr (self->pv->err_data->str, '\n')) != NULL) {
+ *ptr = '\0';
+ g_message ("%s", self->pv->err_data->str);
+ g_string_erase (self->pv->err_data, 0,
+ ptr - self->pv->err_data->str);
+ }
+
+ return ret > 0;
+}
+
+static void
+on_io_completed (gpointer user_data)
+{
+ GkdPrompt *self = GKD_PROMPT (self);
+ GError *error = NULL;
+
+ g_return_if_fail (GKD_IS_PROMPT (self));
+
+ g_assert (!self->pv->output);
+ g_assert (self->pv->io_tag != 0);
+ g_assert (!self->pv->completed);
+
+ /* Should be the last call we receive */
+ self->pv->io_tag = 0;
+
+ /* Print out any remaining errors */
+ if (self->pv->err_data && self->pv->err_data->len)
+ g_message ("%s", self->pv->err_data->str);
+
+ /* Parse the output data properly */
+ if (!self->pv->failure) {
+ self->pv->output = g_key_file_new ();
+ if (!g_key_file_load_from_data (self->pv->output, self->pv->out_data->str,
+ self->pv->out_data->len, G_KEY_FILE_NONE, &error)) {
+ g_key_file_free (self->pv->output);
+ g_warning ("couldn't parse output from prompt: %s",
+ error && error->message ? error->message : "");
+ g_clear_error (&error);
+ self->pv->failure = TRUE;
+ } else {
+ g_signal_emit (self, signals[RESPONDED], 0);
+ }
+ }
+}
+
+static void
+on_child_exited (GPid pid, gint status, gpointer user_data)
+{
+ GkdPrompt *self = GKD_PROMPT (self);
+ gint code;
+
+ if (pid == self->pv->pid) {
+ self->pv->pid = 0;
+ if (!self->pv->failure) {
+ if (WIFEXITED (status)) {
+ code = WEXITSTATUS (status);
+ if (code != 0) {
+ g_warning ("prompt process exited with failure code: %d", code);
+ self->pv->failure = TRUE;
+ }
+ } else if (WIFSIGNALED (status)) {
+ code = WTERMSIG (status);
+ g_warning ("prompt process was killed with signal: %d", code);
+ self->pv->failure = TRUE;
+ }
+ }
+ }
+
+ g_spawn_close_pid (pid);
+}
+
+static gboolean
+encode_input_mpi (GkdPrompt *self, const gchar *section,
+ const gchar *field, gcry_mpi_t mpi)
+{
+ gcry_error_t gcry;
+ guchar *data;
+ gsize n_data;
+
+ g_assert (self->pv->input);
+
+ /* Get the size */
+ gcry = gcry_mpi_print (GCRYMPI_FMT_HEX, NULL, 0, &n_data, mpi);
+ g_return_val_if_fail (gcry == 0, FALSE);
+
+ data = g_malloc0 (n_data + 1);
+
+ /* Write into buffer */
+ gcry = gcry_mpi_print (GCRYMPI_FMT_HEX, data, n_data, &n_data, mpi);
+ g_return_val_if_fail (gcry == 0, FALSE);
+
+ g_key_file_set_value (self->pv->input, section, field, (gchar*)data);
+ g_free (data);
+
+ return TRUE;
+}
+
+static void
+prepare_transport_crypto (GkdPrompt *self)
+{
+ gcry_mpi_t pub, base;
+
+ g_assert (!self->pv->prime);
+ g_assert (!self->pv->secret);
+
+ /* Figure out our prime, base, public and secret bits */
+ if (!egg_dh_default_params (&self->pv->prime, &base) ||
+ !egg_dh_gen_secret (self->pv->prime, base, &pub, &self->pv->secret))
+ g_return_if_reached ();
+
+ /* Send over the prime, base, and public bits */
+ if (!encode_input_mpi (self, "transport", "prime", self->pv->prime) ||
+ !encode_input_mpi (self, "transport", "base", base) ||
+ !encode_input_mpi (self, "transport", "public", pub))
+ g_return_if_reached ();
+
+ gcry_mpi_release (base);
+ gcry_mpi_release (pub);
+}
+
+static gboolean
+decode_output_mpi (GkdPrompt *self, const gchar *section,
+ const gchar *field, gcry_mpi_t *mpi)
+{
+ gcry_error_t gcry;
+ gchar *data;
+
+ g_assert (self->pv->output);
+
+ data = g_key_file_get_value (self->pv->output, section, field, NULL);
+ if (!data)
+ return FALSE;
+
+ gcry = gcry_mpi_scan (mpi, GCRYMPI_FMT_HEX, data, 0, NULL);
+ g_free (data);
+
+ return (gcry == 0);
+}
+
+static guchar*
+decode_output_hex (GkdPrompt *self, const gchar *section,
+ const gchar *field, gsize *n_result)
+{
+ guchar *result;
+ gchar *data;
+
+ g_assert (self->pv->output);
+
+ data = g_key_file_get_value (self->pv->output, section, field, NULL);
+ if (!data)
+ return NULL;
+
+ result = egg_hex_decode (data, -1, n_result);
+ g_free (data);
+ return result;
+}
+
+static gboolean
+receive_transport_crypto (GkdPrompt *self)
+{
+ gcry_mpi_t key, peer;
+ gcry_error_t gcry;
+ guchar *buffer;
+ gsize n_buffer;
+ gboolean ret;
+
+ g_assert (self->pv->output);
+
+ if (!decode_output_mpi (self, "transport", "public", &peer))
+ return FALSE;
+
+ ret = egg_dh_gen_key (peer, self->pv->secret, self->pv->prime, &key);
+ gcry_mpi_release (peer);
+ if (!ret)
+ return FALSE;
+
+ /* Write the key out to raw data */
+ gcry = gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, &n_buffer, peer);
+ g_return_val_if_fail (gcry == 0, FALSE);
+ buffer = egg_secure_alloc (n_buffer);
+ gcry = gcry_mpi_print (GCRYMPI_FMT_USG, buffer, n_buffer, &n_buffer, peer);
+ g_return_val_if_fail (gcry == 0, FALSE);
+
+ /* Allocate memory for hashed key */
+ egg_secure_free (self->pv->key);
+ g_assert (16 == gcry_md_get_algo_dlen (GCRY_MD_MD5));
+ self->pv->key = egg_secure_alloc (16);
+ self->pv->n_key = 16;
+
+ /* Use that as the input to derive a key for 128-bit AES */
+ gcry_md_hash_buffer (GCRY_MD_MD5, self->pv->key, buffer, n_buffer);
+
+ egg_secure_free (buffer);
+ return TRUE;
+}
+
+static gchar*
+decrypt_transport_crypto (GkdPrompt *self, guchar *data, gsize n_data,
+ guchar *iv, gsize n_iv)
+{
+ gcry_cipher_hd_t cih;
+ gcry_error_t gcry;
+ gchar *result;
+ gsize pos;
+
+ g_assert (self->pv->key);
+ g_assert (self->pv->n_key == 16);
+
+ if (n_iv != 16) {
+ g_warning ("prompt response has iv of wrong length");
+ return NULL;
+ }
+
+ if (n_data % 16 != 0) {
+ g_warning ("prompt response encrypted password of wrong length");
+ return NULL;
+ }
+
+ gcry = gcry_cipher_open (&cih, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC, 0);
+ if (gcry) {
+ g_warning ("couldn't create aes cipher context: %s", gcry_strerror (gcry));
+ return NULL;
+ }
+
+ /* 16 = 128 bits */
+ gcry = gcry_cipher_setkey (cih, self->pv->key, 16);
+ g_return_val_if_fail (gcry == 0, NULL);
+
+ /* 16 = 128 bits */
+ gcry = gcry_cipher_setiv (cih, iv, 16);
+ g_return_val_if_fail (gcry == 0, NULL);
+
+ /* Allocate memory for the result */
+ result = egg_secure_alloc (n_data);
+
+ for (pos = 0; pos < n_data; pos += 16) {
+ gcry = gcry_cipher_decrypt (cih, result + pos, 16, data + pos, 16);
+ g_return_val_if_fail (gcry == 0, NULL);
+ }
+
+ gcry_cipher_close (cih);
+
+ if (!g_utf8_validate (result, n_data, NULL)) {
+ egg_secure_free (result);
+ return NULL;
+ }
+
+ return result;
+}
+
+
+static gboolean
+prepare_input_data (GkdPrompt *self)
+{
+ GError *error = NULL;
+
+ g_assert (self->pv->input);
+
+ prepare_transport_crypto (self);
+
+ self->pv->in_data = g_key_file_to_data (self->pv->input, &self->pv->in_length, &error);
+ if (!self->pv->in_data) {
+ g_warning ("couldn't encode data for prompt: %s",
+ error && error->message ? error->message : "");
+ g_clear_error (&error);
+ self->pv->failure = TRUE;
+ mark_completed (self);
+ return FALSE;
+ }
+
+ /* No further modifications to input are possible */
+ g_key_file_free (self->pv->input);
+ self->pv->input = NULL;
+
+ return TRUE;
+}
+
+static void
+display_async_prompt (GkdPrompt *self)
+{
+ EggSpawnCallbacks callbacks;
+ GError *error = NULL;
+ gchar **names, **envp;
+ int i, n;
+
+ char *argv[] = {
+ self->pv->executable,
+ NULL,
+ };
+
+ g_assert (!self->pv->pid);
+
+ /* Fires completed event when fails */
+ if (!prepare_input_data (self))
+ return;
+
+ /* Any environment we have */
+ names = g_listenv ();
+ for (n = 0; names && names[n]; ++n);
+ envp = g_new (char*, n + 2);
+ for (i = 0; i < n; i++)
+ envp[i] = g_strdup_printf ("%s=%s", names[i], g_getenv (names[i]));
+ envp[i++] = NULL;
+ g_strfreev (names);
+
+ memset (&callbacks, 0, sizeof (callbacks));
+ callbacks.standard_input = on_standard_input;
+ callbacks.standard_output = on_standard_output;
+ callbacks.standard_error = on_standard_error;
+ callbacks.completed = on_io_completed;
+ callbacks.finalize_func = g_object_unref;
+
+ self->pv->io_tag = egg_spawn_async_with_callbacks (NULL, argv, envp, G_SPAWN_DO_NOT_REAP_CHILD,
+ &self->pv->pid, &callbacks, g_object_ref (self),
+ NULL, &error);
+ if (!self->pv->io_tag) {
+ g_warning ("couldn't spawn prompt tool: %s",
+ error && error->message ? error->message : "");
+ g_clear_error (&error);
+ self->pv->pid = 0;
+ self->pv->failure = TRUE;
+ mark_completed (self);
+ return;
+ }
+
+ g_child_watch_add_full (G_PRIORITY_DEFAULT, self->pv->pid, on_child_exited,
+ g_object_ref (self), g_object_unref);
+}
+
+static void
+clear_prompt_data (GkdPrompt *self)
+{
+ if (self->pv->input)
+ g_key_file_free (self->pv->input);
+ self->pv->input = NULL;
+
+ if (self->pv->output)
+ g_key_file_free (self->pv->output);
+ self->pv->output = NULL;
+
+ self->pv->failure = FALSE;
+
+ g_free (self->pv->in_data);
+ self->pv->in_data = NULL;
+ self->pv->in_length = 0;
+ self->pv->in_offset = 0;
+
+ if (self->pv->out_data)
+ g_string_free (self->pv->out_data, TRUE);
+ self->pv->out_data = NULL;
+
+ if (self->pv->err_data)
+ g_string_free (self->pv->err_data, TRUE);
+ self->pv->err_data = NULL;
+
+ if (self->pv->io_tag)
+ g_source_remove (self->pv->io_tag);
+ self->pv->io_tag = 0;
+
+ if (self->pv->prime)
+ gcry_mpi_release (self->pv->prime);
+ self->pv->prime = NULL;
+
+ if (self->pv->secret)
+ gcry_mpi_release (self->pv->secret);
+ self->pv->secret = NULL;
+
+ if (self->pv->key) {
+ egg_secure_clear (self->pv->key, self->pv->n_key);
+ egg_secure_free (self->pv->key);
+ self->pv->key = NULL;
+ self->pv->n_key = 0;
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+static GObject*
+gkd_prompt_constructor (GType type, guint n_props, GObjectConstructParam *props)
+{
+ GkdPrompt *self = GKD_PROMPT (G_OBJECT_CLASS (gkd_prompt_parent_class)->constructor(type, n_props, props));
+ g_return_val_if_fail (self, NULL);
+
+ if (!self->pv->executable)
+ self->pv->executable = g_strdup (LIBEXECDIR "/gnome-keyring-ask");
+
+ return G_OBJECT (self);
+}
+
+static void
+gkd_prompt_init (GkdPrompt *self)
+{
+ self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GKD_TYPE_PROMPT, GkdPromptPrivate);
+ gkd_prompt_reset (self);
+}
+
+static void
+gkd_prompt_dispose (GObject *obj)
+{
+ GkdPrompt *self = GKD_PROMPT (obj);
+
+ kill_process (self);
+ clear_prompt_data (self);
+
+ G_OBJECT_CLASS (gkd_prompt_parent_class)->dispose (obj);
+}
+
+static void
+gkd_prompt_finalize (GObject *obj)
+{
+ GkdPrompt *self = GKD_PROMPT (obj);
+
+ g_assert (self->pv->pid == 0);
+ g_assert (!self->pv->in_data);
+ g_assert (!self->pv->out_data);
+ g_assert (!self->pv->err_data);
+ g_assert (!self->pv->io_tag);
+ g_assert (!self->pv->prime);
+ g_assert (!self->pv->secret);
+ g_assert (!self->pv->key);
+
+ g_free (self->pv->executable);
+ self->pv->executable = NULL;
+
+ G_OBJECT_CLASS (gkd_prompt_parent_class)->finalize (obj);
+}
+
+static void
+gkd_prompt_class_init (GkdPromptClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->constructor = gkd_prompt_constructor;
+ gobject_class->dispose = gkd_prompt_dispose;
+ gobject_class->finalize = gkd_prompt_finalize;
+
+ g_type_class_add_private (klass, sizeof (GkdPromptPrivate));
+
+ signals[COMPLETED] = g_signal_new ("signal", GKD_TYPE_PROMPT,
+ G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GkdPromptClass, completed),
+ NULL, NULL, g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[RESPONDED] = g_signal_new ("signal", GKD_TYPE_PROMPT,
+ G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GkdPromptClass, responded),
+ g_signal_accumulator_true_handled, NULL, gkd_prompt_marshal_BOOLEAN__VOID,
+ G_TYPE_BOOLEAN, 0);
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+void
+gkd_prompt_set_title (GkdPrompt *self, const gchar *title)
+{
+ g_return_if_fail (GKD_IS_PROMPT (self));
+ g_return_if_fail (self->pv->input);
+ g_key_file_set_value (self->pv->input, "prompt", "title", title);
+}
+
+void
+gkd_prompt_set_primary_text (GkdPrompt *self, const gchar *primary)
+{
+ g_return_if_fail (GKD_IS_PROMPT (self));
+ g_return_if_fail (self->pv->input);
+ g_key_file_set_value (self->pv->input, "prompt", "primary", primary);
+}
+
+void
+gkd_prompt_set_secondary_text (GkdPrompt *self, const gchar *secondary)
+{
+ g_return_if_fail (GKD_IS_PROMPT (self));
+ g_return_if_fail (self->pv->input);
+ g_key_file_set_value (self->pv->input, "prompt", "secondary", secondary);
+}
+
+void
+gkd_prompt_show_widget (GkdPrompt *self, const gchar *widget)
+{
+ g_return_if_fail (GKD_IS_PROMPT (self));
+ g_return_if_fail (self->pv->input);
+ g_key_file_set_boolean (self->pv->input, "visibility", widget, TRUE);
+}
+
+void
+gkd_prompt_hide_widget (GkdPrompt *self, const gchar *widget)
+{
+ g_return_if_fail (GKD_IS_PROMPT (self));
+ g_return_if_fail (self->pv->input);
+ g_key_file_set_boolean (self->pv->input, "visibility", widget, FALSE);
+}
+
+void
+gkd_prompt_select_widget (GkdPrompt *self, const gchar *widget)
+{
+ g_return_if_fail (GKD_IS_PROMPT (self));
+ g_return_if_fail (self->pv->input);
+ g_key_file_set_boolean (self->pv->input, "selected", widget, TRUE);
+}
+
+gboolean
+gkd_prompt_has_response (GkdPrompt *self)
+{
+ g_return_val_if_fail (GKD_IS_PROMPT (self), FALSE);
+ return self->pv->output ? TRUE : FALSE;
+}
+
+gint
+gkd_prompt_get_response (GkdPrompt *self)
+{
+ gchar *response;
+ guint ret;
+
+ g_return_val_if_fail (GKD_IS_PROMPT (self), GKD_RESPONSE_FAILURE);
+ if (!self->pv->failure)
+ return GKD_RESPONSE_FAILURE;
+
+ g_return_val_if_fail (self->pv->output, GKD_RESPONSE_FAILURE);
+
+ response = g_key_file_get_value (self->pv->output, "prompt", "response", NULL);
+ if (!response) {
+ ret = GKD_RESPONSE_NONE;
+ } else if (g_str_equal (response, "ok")) {
+ ret = GKD_RESPONSE_OK;
+ } else if (g_str_equal (response, "no")) {
+ ret = GKD_RESPONSE_NO;
+ } else if (g_str_equal (response, "other")) {
+ ret = GKD_RESPONSE_OTHER;
+ } else {
+ g_warning ("invalid response field received from prompt: %s", response);
+ ret = GKD_RESPONSE_NONE;
+ }
+
+ g_free (response);
+ return ret;
+}
+
+gchar*
+gkd_prompt_get_password (GkdPrompt *self, const gchar *password_type)
+{
+ gboolean encrypted;
+ gchar *result;
+ guchar *data;
+ gsize n_data;
+ guchar *iv;
+ gsize n_iv;
+
+ g_return_val_if_fail (GKD_IS_PROMPT (self), NULL);
+ g_return_val_if_fail (self->pv->output, NULL);
+
+ if (!self->pv->failure)
+ return NULL;
+
+ g_assert (self->pv->output);
+
+ if (!password_type)
+ password_type = "password";
+
+ encrypted = g_key_file_get_boolean (self->pv->output, password_type, "encrypted", NULL);
+ if (!encrypted)
+ return g_key_file_get_string (self->pv->output, password_type, "value", NULL);
+
+ /* Parse the encryption params and figure out a key */
+ if (!self->pv->key && !receive_transport_crypto (self))
+ g_return_val_if_reached (NULL);
+
+ /* Parse out an IV */
+ iv = decode_output_hex (self, password_type, "iv", &n_iv);
+ if (iv == NULL) {
+ g_warning ("prompt response has encrypted password, but no iv set");
+ return NULL;
+ }
+
+ /* Parse out the password */
+ data = decode_output_hex (self, password_type, "value", &n_data);
+ if (data == NULL) {
+ g_warning ("prompt response missing encrypted password value");
+ g_free (iv);
+ return NULL;
+ }
+
+ result = decrypt_transport_crypto (self, data, n_data, iv, n_iv);
+ g_free (data);
+ g_free (iv);
+
+ return result;
+}
+
+gboolean
+gkd_prompt_is_widget_selected (GkdPrompt *self, const gchar *widget)
+{
+ g_return_val_if_fail (GKD_IS_PROMPT (self), FALSE);
+ g_return_val_if_fail (self->pv->output, FALSE);
+
+ if (!self->pv->failure)
+ return FALSE;
+
+ g_assert (self->pv->output);
+ return g_key_file_get_boolean (self->pv->output, "selected", widget, NULL);
+}
+
+void
+gkd_prompt_set_window_id (GkdPrompt *self, const gchar *window_id)
+{
+ g_return_if_fail (GKD_IS_PROMPT (self));
+ g_return_if_fail (self->pv->input);
+ if (!window_id)
+ g_key_file_remove_key (self->pv->input, "prompt", "window-id", NULL);
+ else
+ g_key_file_set_value (self->pv->input, "prompt", "window-id", window_id);
+}
+
+void
+gkd_prompt_set_warning (GkdPrompt *self, const gchar *warning)
+{
+ g_return_if_fail (GKD_IS_PROMPT (self));
+ g_return_if_fail (self->pv->input);
+ if (!warning)
+ g_key_file_remove_key (self->pv->input, "prompt", "warning", NULL);
+ else
+ g_key_file_set_value (self->pv->input, "prompt", "warning", warning);
+}
+
+void
+gkd_prompt_reset (GkdPrompt *self)
+{
+ g_return_if_fail (GKD_IS_PROMPT (self));
+ g_return_if_fail (self->pv->completed);
+
+ kill_process (self);
+ self->pv->pid = 0;
+
+ clear_prompt_data (self);
+ self->pv->input = g_key_file_new ();
+}
+
+/* ----------------------------------------------------------------------------------
+ * ATTENTION QUEUES
+ */
+
+/* Forward declaration */
+static void next_attention_req (const gchar *);
+
+typedef struct _Attention {
+ gchar *window_id;
+ GkdPromptAttentionFunc callback;
+ GDestroyNotify destroy;
+ gpointer user_data;
+ gulong completed_tag;
+ GkdPrompt *prompt;
+} AttentionReq;
+
+static GHashTable *attention_reqs = NULL;
+
+static void
+clear_attention_reqs (gpointer unused)
+{
+ g_assert (attention_reqs);
+ g_hash_table_destroy (attention_reqs);
+}
+
+static AttentionReq*
+alloc_attention_req (const gchar *window_id)
+{
+ AttentionReq *att;
+
+ g_assert (window_id);
+
+ att = g_slice_new0 (AttentionReq);
+ att->window_id = g_strdup (window_id);
+ return att;
+}
+
+static void
+free_attention_req (gpointer data)
+{
+ AttentionReq *att = data;
+
+ if (att) {
+ g_free (att->window_id);
+ if (att->destroy)
+ (att->destroy) (att->user_data);
+ if (att->prompt)
+ g_object_unref (att->prompt);
+ g_slice_free (AttentionReq, att);
+ }
+}
+
+static void
+free_attention_queue (gpointer data)
+{
+ GQueue *queue = data;
+ AttentionReq *att;
+
+ if (queue) {
+ while (!g_queue_is_empty (queue)) {
+ att = g_queue_pop_head (queue);
+ free_attention_req (att);
+ }
+ g_queue_free (queue);
+ }
+}
+
+static GQueue*
+alloc_attention_queue (void)
+{
+ return g_queue_new ();
+}
+
+static void
+done_attention_req (gpointer user_data, GClosure *unused)
+{
+ AttentionReq *att = user_data;
+ g_assert (att);
+ g_signal_handler_disconnect (att->prompt, att->completed_tag);
+ next_attention_req (att->window_id);
+}
+
+static void
+next_attention_req (const gchar *window_id)
+{
+ AttentionReq *att;
+ GQueue *queue;
+
+ g_assert (window_id);
+ g_assert (attention_reqs);
+
+ queue = g_hash_table_lookup (attention_reqs, window_id);
+ g_return_if_fail (queue);
+
+ /* Nothing more to process for this window */
+ if (g_queue_is_empty (queue)) {
+ g_hash_table_remove (attention_reqs, window_id);
+ return;
+ }
+
+ /* Get the next one out */
+ att = g_queue_pop_head (queue);
+ g_assert (att);
+ g_assert (att->window_id);
+ g_assert (g_str_equal (att->window_id, window_id));
+ g_assert (!att->prompt);
+ g_assert (att->callback);
+
+ /* Callback populates the prompt */
+ att->prompt = (att->callback) (att->user_data);
+
+ /* Don't show the prompt */
+ if (att->prompt == NULL) {
+ free_attention_req (att);
+ next_attention_req (window_id);
+ return;
+ }
+
+ att->completed_tag = g_signal_connect_data (att->prompt, "completed",
+ G_CALLBACK (done_attention_req), att,
+ (GClosureNotify)free_attention_req,
+ G_CONNECT_AFTER);
+
+ /* Actually display the prompt, "completed" signal will fire */
+ gkd_prompt_set_window_id (att->prompt, window_id);
+ display_async_prompt (att->prompt);
+}
+
+static gboolean
+service_attention_req (gpointer user_data)
+{
+ AttentionReq *att = user_data;
+ gboolean now = FALSE;
+ GQueue *queue;
+
+ g_assert (att);
+
+ if (!attention_reqs) {
+ attention_reqs = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, free_attention_queue);
+ egg_cleanup_register (clear_attention_reqs, NULL);
+ }
+
+ queue = g_hash_table_lookup (attention_reqs, att->window_id);
+ if (queue == NULL) {
+ queue = alloc_attention_queue ();
+ g_hash_table_insert (attention_reqs, g_strdup (att->window_id), queue);
+ now = TRUE;
+ }
+
+ g_queue_push_tail (queue, att);
+ if (now == TRUE)
+ next_attention_req (att->window_id);
+
+ /* Remove this timeout handler after one call */
+ return FALSE;
+}
+
+void
+gkd_prompt_request_attention_async (const gchar *window_id, GkdPromptAttentionFunc callback,
+ gpointer user_data, GDestroyNotify destroy_notify)
+{
+ AttentionReq *att;
+
+ g_return_if_fail (callback);
+
+ if (!window_id)
+ window_id = "";
+ att = alloc_attention_req (window_id);
+ att->callback = callback;
+ att->user_data = user_data;
+ att->destroy = destroy_notify;
+
+ g_timeout_add (0, service_attention_req, att);
+}
+
diff --git a/daemon/prompt/gkd-prompt.h b/daemon/prompt/gkd-prompt.h
new file mode 100644
index 0000000..2f5265f
--- /dev/null
+++ b/daemon/prompt/gkd-prompt.h
@@ -0,0 +1,102 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2009 Stefan Walter
+ *
+ * This program 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.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GKD_PROMPT_H__
+#define __GKD_PROMPT_H__
+
+#include <glib-object.h>
+
+typedef enum {
+ GKD_RESPONSE_FAILURE = -1,
+ GKD_RESPONSE_NONE = 0,
+ GKD_RESPONSE_NO = 1,
+ GKD_RESPONSE_OK = 2,
+ GKD_RESPONSE_OTHER = 3,
+} GkrAskResponse;
+
+#define GKD_TYPE_PROMPT (gkd_prompt_get_type ())
+#define GKD_PROMPT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GKD_TYPE_PROMPT, GkdPrompt))
+#define GKD_PROMPT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GKD_TYPE_PROMPT, GkdPromptClass))
+#define GKD_IS_PROMPT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GKD_TYPE_PROMPT))
+#define GKD_IS_PROMPT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GKD_TYPE_PROMPT))
+#define GKD_PROMPT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GKD_TYPE_PROMPT, GkdPromptClass))
+
+typedef struct _GkdPrompt GkdPrompt;
+typedef struct _GkdPromptClass GkdPromptClass;
+typedef struct _GkdPromptPrivate GkdPromptPrivate;
+
+struct _GkdPrompt {
+ GObject parent;
+ GkdPromptPrivate *pv;
+};
+
+struct _GkdPromptClass {
+ GObjectClass parent_class;
+ gboolean (*responded) (GkdPrompt *self);
+ void (*completed) (GkdPrompt *self);
+};
+
+GType gkd_prompt_get_type (void);
+
+void gkd_prompt_reset (GkdPrompt *prompt);
+
+void gkd_prompt_set_title (GkdPrompt *prompt,
+ const gchar *title);
+
+void gkd_prompt_set_primary_text (GkdPrompt *prompt,
+ const gchar *primary);
+
+void gkd_prompt_set_secondary_text (GkdPrompt *prompt,
+ const gchar *secondary);
+
+void gkd_prompt_set_warning (GkdPrompt *prompt,
+ const gchar *warning);
+
+void gkd_prompt_set_window_id (GkdPrompt *prompt,
+ const gchar *window_id);
+
+void gkd_prompt_show_widget (GkdPrompt *prompt,
+ const gchar *widget);
+
+void gkd_prompt_hide_widget (GkdPrompt *prompt,
+ const gchar *widget);
+
+void gkd_prompt_select_widget (GkdPrompt *prompt,
+ const gchar *widget);
+
+gboolean gkd_prompt_has_response (GkdPrompt *prompt);
+
+gint gkd_prompt_get_response (GkdPrompt *prompt);
+
+gchar* gkd_prompt_get_password (GkdPrompt *prompt,
+ const gchar *password_type);
+
+gboolean gkd_prompt_is_widget_selected (GkdPrompt *prompt,
+ const gchar *widget);
+
+typedef GkdPrompt* (*GkdPromptAttentionFunc) (gpointer user_data);
+
+void gkd_prompt_request_attention_async (const gchar *window_id,
+ GkdPromptAttentionFunc callback,
+ gpointer user_data,
+ GDestroyNotify destroy_notify);
+
+#endif /* __GKD_PROMPT_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]