[gcr] gcr: Add system prompt and prompter



commit 316bb56cadbdd56ee0e897dcc571dec7ea8ac77a
Author: Stef Walter <stefw collabora co uk>
Date:   Fri Oct 28 12:06:03 2011 +0200

    gcr: Add system prompt and prompter
    
     * GcrSystemPrompt is a class used on the client.
     * GcrSystemPrompter is a base class for implementing the actual prompter
     * GcrMockPrompter is a mock prompter, which returns predefined values
       useful in tests

 configure.ac                        |    2 +-
 docs/reference/gcr/Makefile.am      |    2 +
 docs/reference/gcr/gcr-sections.txt |    5 +
 gcr/Makefile.am                     |   40 +-
 gcr/gcr-base.h                      |    3 +
 gcr/gcr-base.symbols                |   59 ++
 gcr/gcr-dbus-constants.h            |   62 ++
 gcr/gcr-debug.c                     |    1 +
 gcr/gcr-debug.h                     |    1 +
 gcr/gcr-mock-prompter.c             |  456 ++++++++++++
 gcr/gcr-mock-prompter.h             |   62 ++
 gcr/gcr-prompter-tool.c             |  630 +++++++++++++++++
 gcr/gcr-system-prompt.c             | 1330 +++++++++++++++++++++++++++++++++++
 gcr/gcr-system-prompt.h             |  174 +++++
 gcr/gcr-system-prompter.c           | 1162 ++++++++++++++++++++++++++++++
 gcr/gcr-system-prompter.h           |  113 +++
 gcr/org.gnome.keyring.Prompt.xml    |   39 +
 gcr/org.gnome.keyring.Prompter.xml  |   15 +
 gcr/tests/Makefile.am               |    3 +-
 19 files changed, 4154 insertions(+), 5 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index e21581e..eff8f86 100644
--- a/configure.ac
+++ b/configure.ac
@@ -70,7 +70,7 @@ PKG_CHECK_MODULES(GLIB,
 	gmodule-no-export-2.0
 	gthread-2.0
 	gobject-2.0
-	gio-2.0)
+	gio-2.0 gio-unix-2.0)
 AC_SUBST(GLIB_CFLAGS)
 AC_SUBST(GLIB_LIBS)
 
diff --git a/docs/reference/gcr/Makefile.am b/docs/reference/gcr/Makefile.am
index 6dc143f..a984601 100644
--- a/docs/reference/gcr/Makefile.am
+++ b/docs/reference/gcr/Makefile.am
@@ -64,6 +64,8 @@ IGNORE_HFILES= \
 	gcr-certificate-basics-widget.h \
 	gcr-certificate-details-widget.h \
 	gcr-certificate-request-renderer.h \
+	gcr-dbus-constants.h \
+	gcr-dbus-generated.h \
 	gcr-deprecated.h \
 	gcr-deprecated-base.h \
 	gcr-display-scrolled.h \
diff --git a/docs/reference/gcr/gcr-sections.txt b/docs/reference/gcr/gcr-sections.txt
index 6fa835b..45840d8 100644
--- a/docs/reference/gcr/gcr-sections.txt
+++ b/docs/reference/gcr/gcr-sections.txt
@@ -706,3 +706,8 @@ GcrOpensshPubCallback
 GCR_TYPE_IMPORTER_PROMPT_BEHAVIOR
 gcr_importer_prompt_behavior_get_type
 </SECTION>
+
+<SECTION>
+<FILE>GcrPrompter1</FILE>
+<SUBSECTION Private>
+</SECTION>
diff --git a/gcr/Makefile.am b/gcr/Makefile.am
index 92af49f..adeb3e6 100644
--- a/gcr/Makefile.am
+++ b/gcr/Makefile.am
@@ -27,10 +27,13 @@ HEADER_BASE_FILES = \
 	gcr-importer.h \
 	gcr-import-interaction.h \
 	gcr-library.h \
+	gcr-mock-prompter.h \
 	gcr-parser.h \
 	gcr-pkcs11-certificate.h \
 	gcr-secret-exchange.h \
 	gcr-simple-certificate.h \
+	gcr-system-prompt.h \
+	gcr-system-prompter.h \
 	gcr-trust.h \
 	gcr-types.h \
 	gcr-union-collection.h \
@@ -76,7 +79,9 @@ INCLUDES = \
 	$(GLIB_CFLAGS) \
 	$(LIBGCRYPT_CFLAGS) \
 	$(P11_KIT_CFLAGS) \
-	-DG_LOG_DOMAIN=\"Gcr\"
+	-DG_LOG_DOMAIN=\"Gcr\" \
+	-DGCR_API_SUBJECT_TO_CHANGE \
+	-DLOCALEDIR=\""$(datadir)/locale"\"
 
 lib_LTLIBRARIES = \
 	libgcr-base- GCR_MAJOR@.la \
@@ -85,7 +90,8 @@ lib_LTLIBRARIES = \
 BUILT_BASE_FILES = \
 	gcr-marshal.c gcr-marshal.h \
 	gcr-enum-types-base.c gcr-enum-types-base.h \
-	gcr-oids.c gcr-oids.h
+	gcr-oids.c gcr-oids.h \
+	gcr-dbus-generated.c gcr-dbus-generated.h
 
 BUILT_UI_FILES = \
 	gcr-marshal.c gcr-marshal.h \
@@ -104,6 +110,7 @@ libgcr_base_ GCR_MAJOR@_la_SOURCES = \
 	gcr-certificate-request.c gcr-certificate-request.h \
 	gcr-collection.c gcr-collection.h \
 	gcr-comparable.c gcr-comparable.h \
+	gcr-dbus-constants.h \
 	gcr-debug.c gcr-debug.h \
 	gcr-filter-collection.c gcr-filter-collection.h \
 	gcr-fingerprint.c gcr-fingerprint.h \
@@ -121,6 +128,7 @@ libgcr_base_ GCR_MAJOR@_la_SOURCES = \
 	gcr-library.c gcr-library.h \
 	gcr-memory.c \
 	gcr-memory-icon.c gcr-memory-icon.h \
+	gcr-mock-prompter.c gcr-mock-prompter.h \
 	gcr-openpgp.c gcr-openpgp.h \
 	gcr-openssh.c gcr-openssh.h \
 	gcr-parser.c gcr-parser.h \
@@ -132,6 +140,8 @@ libgcr_base_ GCR_MAJOR@_la_SOURCES = \
 	gcr-simple-collection.c gcr-simple-collection.h \
 	gcr-single-collection.c gcr-single-collection.h \
 	gcr-subject-public-key.c gcr-subject-public-key.h \
+	gcr-system-prompt.c gcr-system-prompt.h \
+	gcr-system-prompter.c gcr-system-prompter.h \
 	gcr-trust.c gcr-trust.h \
 	gcr-types.h \
 	gcr-union-collection.c gcr-union-collection.h \
@@ -180,7 +190,6 @@ libgcr_ GCR_MAJOR@_la_SOURCES = \
 
 libgcr_base_ GCR_MAJOR@_la_CFLAGS = \
 	-DGCK_API_SUBJECT_TO_CHANGE \
-	-DGCR_API_SUBJECT_TO_CHANGE \
 	-DP11_KIT_API_SUBJECT_TO_CHANGE \
 	-DGCR_COMPILATION
 
@@ -247,6 +256,18 @@ gcr-oids.c: gcr-oids.list gcr-mkoids
 
 gcr-oids.h: gcr-oids.c
 
+DBUS_XML_DEFINITIONS = \
+	org.gnome.keyring.Prompter.xml \
+	org.gnome.keyring.Prompt.xml
+
+gcr-dbus-generated.c: $(DBUS_XML_DEFINITIONS)
+	$(AM_V_GEN) gdbus-codegen --interface-prefix org.gnome.keyring. \
+		--generate-c-code gcr-dbus-generated --c-namespace Gcr \
+		$(DBUS_XML_DEFINITIONS)
+	$(AM_V_GEN) sed -i -e 's/gcr_/_gcr_/g' gcr-dbus-generated.[ch]
+
+gcr-dbus-generated.h: gcr-dbus-generated.c
+
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = gcr-$(GCR_MAJOR).pc gcr-base-$(GCR_MAJOR).pc
 
@@ -304,6 +325,18 @@ gcr_viewer_LDADD = \
 	$(GLIB_LIBS) \
 	$(GTK_LIBS)
 
+libexec_PROGRAMS = gcr-prompter
+
+gcr_prompter_SOURCES = \
+	gcr-prompter-tool.c
+
+gcr_prompter_CFLAGS = \
+	$(GTK_CFLAGS)
+
+gcr_prompter_LDADD = \
+	$(builddir)/libgcr-$(GCR_MAJOR).la \
+	$(GTK_LIBS)
+
 # ------------------------------------------------------------------
 # INTROSPECTION
 
@@ -372,6 +405,7 @@ EXTRA_DIST = \
 	gcr-mkoids \
 	$(ui_DATA) \
 	$(conf_DATA) \
+	org.gnome.keyring.Prompter.xml \
 	gcr-enum-types.h.template \
 	gcr-enum-types.c.template \
 	gcr.symbols \
diff --git a/gcr/gcr-base.h b/gcr/gcr-base.h
index ff871cb..ff34ddc 100644
--- a/gcr/gcr-base.h
+++ b/gcr/gcr-base.h
@@ -43,11 +43,14 @@
 #include "gcr-icons.h"
 #include "gcr-importer.h"
 #include "gcr-library.h"
+#include "gcr-mock-prompter.h"
 #include "gcr-parser.h"
 #include "gcr-pkcs11-certificate.h"
 #include "gcr-secret-exchange.h"
 #include "gcr-simple-certificate.h"
 #include "gcr-simple-collection.h"
+#include "gcr-system-prompt.h"
+#include "gcr-system-prompter.h"
 #include "gcr-trust.h"
 #include "gcr-union-collection.h"
 #include "gcr-unlock-options.h"
diff --git a/gcr/gcr-base.symbols b/gcr/gcr-base.symbols
index 4061745..9256194 100644
--- a/gcr/gcr-base.symbols
+++ b/gcr/gcr-base.symbols
@@ -100,6 +100,12 @@ gcr_import_interaction_supplement
 gcr_import_interaction_supplement_async
 gcr_import_interaction_supplement_finish
 gcr_import_interaction_supplement_prep
+gcr_mock_prompter_expect_confirm_cancel
+gcr_mock_prompter_expect_confirm_ok
+gcr_mock_prompter_expect_password_cancel
+gcr_mock_prompter_expect_password_ok
+gcr_mock_prompter_get_type
+gcr_mock_prompter_new
 gcr_parsed_get_attributes
 gcr_parsed_get_data
 gcr_parsed_get_description
@@ -157,6 +163,59 @@ gcr_simple_collection_contains
 gcr_simple_collection_get_type
 gcr_simple_collection_new
 gcr_simple_collection_remove
+gcr_system_prompt_close
+gcr_system_prompt_confirm
+gcr_system_prompt_confirm_async
+gcr_system_prompt_confirm_finish
+gcr_system_prompter_get_caller_window
+gcr_system_prompter_get_choice_chosen
+gcr_system_prompter_get_choice_label
+gcr_system_prompter_get_description
+gcr_system_prompter_get_message
+gcr_system_prompter_get_password_new
+gcr_system_prompter_get_password_strength
+gcr_system_prompter_get_title
+gcr_system_prompter_get_type
+gcr_system_prompter_get_warning
+gcr_system_prompter_new
+gcr_system_prompter_register
+gcr_system_prompter_respond_cancelled
+gcr_system_prompter_respond_confirmed
+gcr_system_prompter_respond_with_password
+gcr_system_prompt_error_get_domain
+gcr_system_prompt_error_get_type
+gcr_system_prompter_set_choice_chosen
+gcr_system_prompter_set_password_strength
+gcr_system_prompter_set_warning
+gcr_system_prompter_unregister
+gcr_system_prompt_get_caller_window
+gcr_system_prompt_get_choice_chosen
+gcr_system_prompt_get_choice_label
+gcr_system_prompt_get_description
+gcr_system_prompt_get_message
+gcr_system_prompt_get_password_new
+gcr_system_prompt_get_password_strength
+gcr_system_prompt_get_secret_exchange
+gcr_system_prompt_get_title
+gcr_system_prompt_get_type
+gcr_system_prompt_get_warning
+gcr_system_prompt_open
+gcr_system_prompt_open_async
+gcr_system_prompt_open_finish
+gcr_system_prompt_open_for_prompter
+gcr_system_prompt_open_for_prompter_async
+gcr_system_prompt_password
+gcr_system_prompt_password_async
+gcr_system_prompt_password_finish
+gcr_system_prompt_set_caller_window
+gcr_system_prompt_set_choice_chosen
+gcr_system_prompt_set_choice_label
+gcr_system_prompt_set_description
+gcr_system_prompt_set_message
+gcr_system_prompt_set_password_new
+gcr_system_prompt_set_secret_exchange
+gcr_system_prompt_set_title
+gcr_system_prompt_set_warning
 gcr_trust_add_pinned_certificate
 gcr_trust_add_pinned_certificate_async
 gcr_trust_add_pinned_certificate_finish
diff --git a/gcr/gcr-dbus-constants.h b/gcr/gcr-dbus-constants.h
new file mode 100644
index 0000000..d47c6f3
--- /dev/null
+++ b/gcr/gcr-dbus-constants.h
@@ -0,0 +1,62 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2011 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.
+ *
+ * Author: Stef Walter <stef thewalter net>
+ */
+
+#ifndef __GCR_DBUS_CONSTANTS_H__
+#define __GCR_DBUS_CONSTANTS_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define GCR_DBUS_PROMPTER_BUS_NAME                   "org.gnome.keyring.Prompter"
+#define GCR_DBUS_PROMPTER_MOCK_BUS_NAME              "org.gnome.keyring.MockPrompter"
+
+#define GCR_DBUS_PROMPTER_OBJECT_PATH                "/org/gnome/keyring/Prompter"
+
+#define GCR_DBUS_PROMPTER_INTERFACE                  "org.gnome.keyring.Prompter"
+
+#define GCR_DBUS_PROMPTER_METHOD_BEGIN               "BeginPrompting"
+#define GCR_DBUS_PROMPTER_METHOD_FINISH              "FinishPrompting"
+
+#define GCR_DBUS_PROMPT_INTERFACE                    "org.gnome.keyring.Prompter.Prompt"
+
+#define GCR_DBUS_PROMPT_ERROR_IN_PROGRESS            "org.gnome.keyring.Prompter.InProgress"
+#define GCR_DBUS_PROMPT_ERROR_NOT_HAPPENING          "org.gnome.keyring.Prompter.NotHappening"
+#define GCR_DBUS_PROMPT_ERROR_FAILED                 "org.gnome.keyring.Prompter.Failed"
+
+#define GCR_DBUS_PROMPT_PROPERTY_TITLE               "Title"
+#define GCR_DBUS_PROMPT_PROPERTY_MESSAGE             "Message"
+#define GCR_DBUS_PROMPT_PROPERTY_DESCRIPTION         "Description"
+#define GCR_DBUS_PROMPT_PROPERTY_WARNING             "Warning"
+#define GCR_DBUS_PROMPT_PROPERTY_CHOICE_LABEL        "ChoiceLabel"
+#define GCR_DBUS_PROMPT_PROPERTY_CHOICE_CHOSEN       "ChoiceChosen"
+#define GCR_DBUS_PROMPT_PROPERTY_PASSWORD_NEW        "PasswordNew"
+#define GCR_DBUS_PROMPT_PROPERTY_PASSWORD_STRENGTH   "PasswordStrength"
+#define GCR_DBUS_PROMPT_PROPERTY_CALLER_WINDOW       "CallerWindow"
+
+#define GCR_DBUS_PROMPT_METHOD_PASSWORD              "RequestPassword"
+#define GCR_DBUS_PROMPT_METHOD_CONFIRM               "RequestConfirm"
+
+G_END_DECLS
+
+#endif /* __GCR_DBUS_CONSTANTS_H__ */
diff --git a/gcr/gcr-debug.c b/gcr/gcr-debug.c
index 9177e58..1f74250 100644
--- a/gcr/gcr-debug.c
+++ b/gcr/gcr-debug.c
@@ -43,6 +43,7 @@ static GDebugKey keys[] = {
 	{ "trust", GCR_DEBUG_TRUST },
 	{ "import", GCR_DEBUG_IMPORT },
 	{ "key", GCR_DEBUG_KEY },
+	{ "prompt", GCR_DEBUG_PROMPT },
 	{ 0, }
 };
 
diff --git a/gcr/gcr-debug.h b/gcr/gcr-debug.h
index 5063b77..54c13d5 100644
--- a/gcr/gcr-debug.h
+++ b/gcr/gcr-debug.h
@@ -35,6 +35,7 @@ typedef enum {
 	GCR_DEBUG_TRUST = 1 << 5,
 	GCR_DEBUG_IMPORT = 1 << 6,
 	GCR_DEBUG_KEY = 1 << 7,
+	GCR_DEBUG_PROMPT = 1 << 8,
 } GcrDebugFlags;
 
 gboolean           _gcr_debug_flag_is_set              (GcrDebugFlags flag);
diff --git a/gcr/gcr-mock-prompter.c b/gcr/gcr-mock-prompter.c
new file mode 100644
index 0000000..412c101
--- /dev/null
+++ b/gcr/gcr-mock-prompter.c
@@ -0,0 +1,456 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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.
+ *
+ * Author: Stef Walter <stfew collabora co uk>
+ */
+
+#include "config.h"
+
+#include "gcr-mock-prompter.h"
+
+#include "egg/egg-error.h"
+
+#include <gobject/gvaluecollector.h>
+
+#include <string.h>
+
+/**
+ * SECTION:gcr-mock-prompter
+ * @title: GcrMockPrompter
+ * @short_description: XXX
+ *
+ * XXXX
+ */
+
+/**
+ * GcrMockPrompter:
+ *
+ * XXX
+ */
+
+/**
+ * GcrMockPrompterClass:
+ *
+ * The class for #GcrMockPrompter.
+ */
+
+#define GCR_IS_MOCK_PROMPTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_MOCK_PROMPTER))
+#define GCR_MOCK_PROMPTER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_MOCK_PROMPTER, GcrMockPromptClass))
+#define GCR_MOCK_PROMPTER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_MOCK_PROMPTER, GcrMockPromptClass))
+
+typedef struct _GcrMockPrompterClass GcrMockPrompterClass;
+typedef struct _GcrMockPrompterPrivate GcrMockPrompterPrivate;
+
+enum {
+	PROP_0,
+	PROP_CONNECTION,
+};
+
+typedef struct {
+	gboolean proceed;
+	gchar *password;
+	GList *properties;
+} MockResponse;
+
+struct _GcrMockPrompter {
+	GcrSystemPrompter parent;
+	GDBusConnection *connection;
+	GQueue *responses;
+};
+
+struct _GcrMockPrompterClass {
+	GcrSystemPrompterClass parent_class;
+};
+
+G_DEFINE_TYPE (GcrMockPrompter, gcr_mock_prompter, GCR_TYPE_SYSTEM_PROMPTER);
+
+static void
+mock_property_free (gpointer data)
+{
+	GParameter *param = data;
+	g_value_unset (&param->value);
+	g_free (param);
+}
+
+static void
+mock_response_free (gpointer data)
+{
+	MockResponse *response = data;
+	g_free (response->password);
+	g_list_free_full (response->properties, mock_property_free);
+}
+
+static void
+gcr_mock_prompter_init (GcrMockPrompter *self)
+{
+	self->responses = g_queue_new ();
+}
+
+static void
+gcr_mock_prompter_set_property (GObject *obj,
+                                guint prop_id,
+                                const GValue *value,
+                                GParamSpec *pspec)
+{
+	GcrMockPrompter *self = GCR_MOCK_PROMPTER (obj);
+
+	switch (prop_id) {
+	case PROP_CONNECTION:
+		self->connection = g_value_get_object (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gcr_mock_prompter_get_property (GObject *obj,
+                                guint prop_id,
+                                GValue *value,
+                                GParamSpec *pspec)
+{
+	GcrMockPrompter *self = GCR_MOCK_PROMPTER (obj);
+
+	switch (prop_id) {
+	case PROP_CONNECTION:
+		g_value_set_object (value, self->connection);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+
+static void
+gcr_mock_prompter_dispose (GObject *obj)
+{
+	GcrMockPrompter *self = GCR_MOCK_PROMPTER (obj);
+	MockResponse *response;
+
+	if (self->connection) {
+		gcr_system_prompter_unregister (GCR_SYSTEM_PROMPTER (self), self->connection);
+		g_object_remove_weak_pointer (G_OBJECT (self->connection),
+		                              (gpointer *)&self->connection);
+		self->connection = NULL;
+	}
+
+	while ((response = g_queue_pop_head (self->responses)))
+		mock_response_free (response);
+
+	G_OBJECT_CLASS (gcr_mock_prompter_parent_class)->dispose (obj);
+}
+
+static void
+gcr_mock_prompter_finalize (GObject *obj)
+{
+	GcrMockPrompter *self = GCR_MOCK_PROMPTER (obj);
+
+	g_queue_free (self->responses);
+
+	G_OBJECT_CLASS (gcr_mock_prompter_parent_class)->finalize (obj);
+}
+
+static gboolean
+value_equal (const GValue *a, const GValue *b)
+{
+	gboolean ret = FALSE;
+
+	g_assert (G_VALUE_TYPE (a) == G_VALUE_TYPE (b));
+
+	switch (G_VALUE_TYPE (a)) {
+	case G_TYPE_BOOLEAN:
+		ret = (g_value_get_boolean (a) == g_value_get_boolean (b));
+		break;
+	case G_TYPE_UCHAR:
+		ret = (g_value_get_uchar (a) == g_value_get_uchar (b));
+		break;
+	case G_TYPE_INT:
+		ret = (g_value_get_int (a) == g_value_get_int (b));
+		break;
+	case G_TYPE_UINT:
+		ret = (g_value_get_uint (a) == g_value_get_uint (b));
+		break;
+	case G_TYPE_INT64:
+		ret = (g_value_get_int64 (a) == g_value_get_int64 (b));
+		break;
+	case G_TYPE_UINT64:
+		ret = (g_value_get_uint64 (a) == g_value_get_uint64 (b));
+		break;
+	case G_TYPE_DOUBLE:
+		ret = (g_value_get_double (a) == g_value_get_double (b));
+		break;
+	case G_TYPE_STRING:
+		ret = (g_strcmp0 (g_value_get_string (a), g_value_get_string (b)) == 0);
+		break;
+	default:
+		g_critical ("no support for comparing of type %s", g_type_name (G_VALUE_TYPE (a)));
+		break;
+	}
+
+	return ret;
+}
+
+static void
+prompter_set_properties (GcrMockPrompter *self,
+                         GList *properties)
+{
+	GObjectClass *object_class;
+	GParameter *param;
+	GParamSpec *spec;
+	GList *l;
+
+	object_class = G_OBJECT_GET_CLASS (self);
+	for (l = properties; l != NULL; l = g_list_next (l)) {
+		param = l->data;
+
+		spec = g_object_class_find_property (object_class, param->name);
+		g_assert (spec != NULL);
+
+		/* A writable property, set it */
+		if ((spec->flags & G_PARAM_WRITABLE)) {
+			g_object_set_property (G_OBJECT (self), param->name, &param->value);
+
+		/* Other properties get checked */
+		} else {
+			GValue value = G_VALUE_INIT;
+
+			g_value_init (&value, spec->value_type);
+			g_object_get_property (G_OBJECT (self), param->name, &value);
+			if (!value_equal (&value, &param->value)) {
+				gchar *expected = g_strdup_value_contents (&param->value);
+				gchar *actual = g_strdup_value_contents (&value);
+
+				g_critical ("expected prompt property '%s' to be %s, but it "
+				            "is instead %s", param->name, expected, actual);
+
+				g_free (expected);
+				g_free (actual);
+			}
+
+			g_value_unset (&value);
+		}
+	}
+}
+
+static gboolean
+gcr_mock_prompter_prompt_confirm (GcrSystemPrompter *prompter)
+{
+	GcrMockPrompter *self = GCR_MOCK_PROMPTER (prompter);
+	MockResponse *response = g_queue_pop_head (self->responses);
+
+	if (response == NULL) {
+		g_critical ("confirmation prompt requested, but not expected");
+		return FALSE;
+
+	} else if (response->password) {
+		g_critical ("confirmation prompt requested, but password prompt expected");
+		mock_response_free (response);
+		return FALSE;
+	}
+
+	prompter_set_properties (self, response->properties);
+
+	if (!response->proceed)
+		gcr_system_prompter_respond_cancelled (GCR_SYSTEM_PROMPTER (self));
+	else
+		gcr_system_prompter_respond_confirmed (GCR_SYSTEM_PROMPTER (self));
+
+	mock_response_free (response);
+	return TRUE;
+}
+
+static gboolean
+gcr_mock_prompter_prompt_password (GcrSystemPrompter *prompter)
+{
+	GcrMockPrompter *self = GCR_MOCK_PROMPTER (prompter);
+	MockResponse *response = g_queue_pop_head (self->responses);
+
+	if (response == NULL) {
+		g_critical ("password prompt requested, but not expected");
+		return FALSE;
+
+	} else if (!response->password) {
+		g_critical ("password prompt requested, but confirmation prompt expected");
+		mock_response_free (response);
+		return FALSE;
+
+	}
+
+	prompter_set_properties (self, response->properties);
+
+	if (!response->proceed)
+		gcr_system_prompter_respond_cancelled (GCR_SYSTEM_PROMPTER (self));
+	else
+		gcr_system_prompter_respond_with_password (GCR_SYSTEM_PROMPTER (self), response->password);
+
+	mock_response_free (response);
+	return TRUE;
+}
+
+static void
+gcr_mock_prompter_class_init (GcrMockPrompterClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	GcrSystemPrompterClass *prompter_class = GCR_SYSTEM_PROMPTER_CLASS (klass);
+
+	gobject_class->get_property = gcr_mock_prompter_get_property;
+	gobject_class->set_property = gcr_mock_prompter_set_property;
+	gobject_class->dispose = gcr_mock_prompter_dispose;
+	gobject_class->finalize = gcr_mock_prompter_finalize;
+
+	prompter_class->prompt_password= gcr_mock_prompter_prompt_password;
+	prompter_class->prompt_confirm = gcr_mock_prompter_prompt_confirm;
+
+	g_object_class_install_property (gobject_class, PROP_CONNECTION,
+	            g_param_spec_object ("connection", "Connection", "DBus connection",
+	                                 G_TYPE_DBUS_CONNECTION, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static GList *
+build_properties (GcrMockPrompter *self,
+                  const gchar *first_property,
+                  va_list var_args)
+{
+	GObjectClass *object_class;
+	GList *result = NULL;
+	const gchar *name;
+
+	object_class = G_OBJECT_GET_CLASS (self);
+
+	name = first_property;
+	while (name) {
+		GValue value = G_VALUE_INIT;
+		GParameter *parameter;
+		GParamSpec *spec;
+		gchar *error = NULL;
+
+		spec = g_object_class_find_property (object_class, name);
+		if (spec == NULL) {
+			g_warning ("%s object class has no property named '%s'",
+			           G_OBJECT_TYPE_NAME (self), name);
+			break;
+		}
+
+		if ((spec->flags & G_PARAM_CONSTRUCT_ONLY) && !(spec->flags & G_PARAM_READABLE)) {
+			g_warning ("%s property '%s' can't be set after construction",
+			           G_OBJECT_TYPE_NAME (self), name);
+			break;
+		}
+
+		G_VALUE_COLLECT_INIT (&value, spec->value_type, var_args, 0, &error);
+		if (error != NULL) {
+			g_warning ("%s", error);
+			g_free (error);
+			g_value_unset (&value);
+			break;
+		}
+
+		parameter = g_new0 (GParameter, 1);
+		parameter->name = g_intern_string (name);
+		memcpy (&parameter->value, &value, sizeof (value));
+		result = g_list_prepend (result, parameter);
+
+		name = va_arg (var_args, gchar *);
+	}
+
+	return result;
+}
+
+void
+gcr_mock_prompter_expect_confirm_ok (GcrMockPrompter *self,
+                                     const gchar *first_property_name,
+                                     ...)
+{
+	MockResponse *response;
+	va_list var_args;
+
+	g_return_if_fail (GCR_IS_MOCK_PROMPTER (self));
+
+	response = g_new0 (MockResponse, 1);
+	response->password = NULL;
+	response->proceed = TRUE;
+
+	va_start (var_args, first_property_name);
+	response->properties = build_properties (self, first_property_name, var_args);
+	va_end (var_args);
+
+	g_queue_push_tail (self->responses, response);
+}
+
+void
+gcr_mock_prompter_expect_confirm_cancel (GcrMockPrompter *self)
+{
+	MockResponse *response;
+
+	g_return_if_fail (GCR_IS_MOCK_PROMPTER (self));
+
+	response = g_new0 (MockResponse, 1);
+	response->password = NULL;
+	response->proceed = FALSE;
+
+	g_queue_push_tail (self->responses, response);
+
+}
+
+void
+gcr_mock_prompter_expect_password_ok (GcrMockPrompter *self,
+                                      const gchar *password,
+                                      const gchar *first_property_name,
+                                      ...)
+{
+	MockResponse *response;
+	va_list var_args;
+
+	g_return_if_fail (GCR_IS_MOCK_PROMPTER (self));
+	g_return_if_fail (password != NULL);
+
+	response = g_new0 (MockResponse, 1);
+	response->password = g_strdup (password);
+	response->proceed = TRUE;
+
+	va_start (var_args, first_property_name);
+	response->properties = build_properties (self, first_property_name, var_args);
+	va_end (var_args);
+
+	g_queue_push_tail (self->responses, response);
+}
+
+void
+gcr_mock_prompter_expect_password_cancel (GcrMockPrompter *self)
+{
+	MockResponse *response;
+
+	g_return_if_fail (GCR_IS_MOCK_PROMPTER (self));
+
+	response = g_new0 (MockResponse, 1);
+	response->password = g_strdup ("");
+	response->proceed = FALSE;
+
+	g_queue_push_tail (self->responses, response);
+}
+
+GcrMockPrompter *
+gcr_mock_prompter_new ()
+{
+	return g_object_new (GCR_TYPE_MOCK_PROMPTER,
+	                     NULL);
+}
diff --git a/gcr/gcr-mock-prompter.h b/gcr/gcr-mock-prompter.h
new file mode 100644
index 0000000..4b5f302
--- /dev/null
+++ b/gcr/gcr-mock-prompter.h
@@ -0,0 +1,62 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#if !defined (__GCR_INSIDE_HEADER__) && !defined (GCR_COMPILATION)
+#error "Only <gcr/gcr.h> or <gcr/gcr-base.h> can be included directly."
+#endif
+
+#ifndef __GCR_MOCK_PROMPTER_H__
+#define __GCR_MOCK_PROMPTER_H__
+
+#include "gcr-system-prompter.h"
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GCR_TYPE_MOCK_PROMPTER            (gcr_mock_prompter_get_type ())
+#define GCR_MOCK_PROMPTER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_MOCK_PROMPTER, GcrMockPrompter))
+#define GCR_IS_MOCK_PROMPTER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_MOCK_PROMPTER))
+
+typedef struct _GcrMockPrompter GcrMockPrompter;
+
+GType                gcr_mock_prompter_get_type                  (void) G_GNUC_CONST;
+
+GcrMockPrompter *    gcr_mock_prompter_new                       (void);
+
+void                 gcr_mock_prompter_expect_confirm_ok         (GcrMockPrompter *self,
+                                                                  const gchar *property_name,
+                                                                  ...);
+
+void                 gcr_mock_prompter_expect_confirm_cancel     (GcrMockPrompter *self);
+
+void                 gcr_mock_prompter_expect_password_ok        (GcrMockPrompter *self,
+                                                                  const gchar *password,
+                                                                  const gchar *property_name,
+                                                                  ...);
+
+void                 gcr_mock_prompter_expect_password_cancel    (GcrMockPrompter *self);
+
+G_END_DECLS
+
+#endif /* __GCR_MOCK_PROMPTER_H__ */
diff --git a/gcr/gcr-prompter-tool.c b/gcr/gcr-prompter-tool.c
new file mode 100644
index 0000000..aa5cebf
--- /dev/null
+++ b/gcr/gcr-prompter-tool.c
@@ -0,0 +1,630 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gcr-viewer-tool.c: Command line utility
+
+   Copyright (C) 2011 Collabora Ltd.
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <stefw collabora co uk>
+*/
+
+#include "config.h"
+
+#include "gcr.h"
+
+#include "gcr-dbus-constants.h"
+#define DEBUG_FLAG GCR_DEBUG_PROMPT
+#include "gcr-debug.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <pango/pango.h>
+
+#include <locale.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LOG_ERRORS 1
+#define GRAB_KEYBOARD 1
+
+static GcrSystemPrompter *the_prompter = NULL;
+
+GType   gcr_prompter_dialog_get_type           (void) G_GNUC_CONST;
+#define GCR_TYPE_PROMPTER_DIALOG               (gcr_prompter_dialog_get_type ())
+#define GCR_PROMPTER_DIALOG(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_PROMPTER_DIALOG, GcrPrompterDialog))
+#define GCR_IS_PROMPTER_DIALOG(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_PROMPTER_DIALOG))
+
+typedef enum {
+	PROMPT_NONE,
+	PROMPT_CONFIRMING,
+	PROMPT_PASSWORDING
+} PromptMode;
+
+enum {
+	PROP_0,
+	PROP_PASSWORD_VISIBLE,
+	PROP_CONFIRM_VISIBLE,
+	PROP_WARNING_VISIBLE,
+	PROP_CHOICE_VISIBLE,
+};
+
+typedef struct {
+	GtkDialog parent;
+	GtkWidget *spinner;
+	GtkWidget *image;
+	GtkEntryBuffer *password_buffer;
+	GtkEntryBuffer *confirm_buffer;
+	PromptMode mode;
+	GdkDevice *grabbed_device;
+	gulong grab_broken_id;
+} GcrPrompterDialog;
+
+typedef struct {
+	GtkDialogClass parent;
+} GcrPrompterDialogClass;
+
+G_DEFINE_TYPE (GcrPrompterDialog, gcr_prompter_dialog, GTK_TYPE_DIALOG);
+
+static void
+on_show_prompt (GcrSystemPrompter *prompter,
+                gpointer user_data)
+{
+	GcrPrompterDialog *self = GCR_PROMPTER_DIALOG (user_data);
+	gtk_widget_show (GTK_WIDGET (self));
+}
+
+static void
+on_hide_prompt (GcrSystemPrompter *prompter,
+                gpointer user_data)
+{
+	GcrPrompterDialog *self = GCR_PROMPTER_DIALOG (user_data);
+	gtk_widget_hide (GTK_WIDGET (self));
+}
+
+static gboolean
+on_prompt_confirm (GcrSystemPrompter *prompter,
+                   gpointer user_data)
+{
+	GcrPrompterDialog *self = GCR_PROMPTER_DIALOG (user_data);
+	GObject *obj;
+
+	g_return_val_if_fail (self->mode == PROMPT_NONE, FALSE);
+
+	self->mode = PROMPT_CONFIRMING;
+	gtk_image_set_from_stock (GTK_IMAGE (self->image),
+	                          GTK_STOCK_DIALOG_QUESTION,
+	                          GTK_ICON_SIZE_DIALOG);
+	gtk_widget_set_sensitive (GTK_WIDGET (self), TRUE);
+	gtk_widget_show (self->image);
+	gtk_widget_hide (self->spinner);
+
+	obj = G_OBJECT (self);
+	g_object_notify (obj, "password-visible");
+	g_object_notify (obj, "confirm-visible");
+	g_object_notify (obj, "warning-visible");
+	g_object_notify (obj, "choice-visible");
+
+	return TRUE;
+}
+
+static gboolean
+on_prompt_password (GcrSystemPrompter *prompter,
+                    gpointer user_data)
+{
+	GcrPrompterDialog *self = GCR_PROMPTER_DIALOG (user_data);
+	GObject *obj;
+
+	g_return_val_if_fail (self->mode == PROMPT_NONE, FALSE);
+
+	self->mode = PROMPT_PASSWORDING;
+	gtk_image_set_from_stock (GTK_IMAGE (self->image),
+	                          GTK_STOCK_DIALOG_AUTHENTICATION,
+	                          GTK_ICON_SIZE_DIALOG);
+	gtk_widget_set_sensitive (GTK_WIDGET (self), TRUE);
+	gtk_widget_show (self->image);
+	gtk_widget_hide (self->spinner);
+
+	obj = G_OBJECT (self);
+	g_object_notify (obj, "password-visible");
+	g_object_notify (obj, "confirm-visible");
+	g_object_notify (obj, "warning-visible");
+	g_object_notify (obj, "choice-visible");
+
+	return TRUE;
+}
+
+static void
+on_responded (GcrSystemPrompter *prompter,
+              gpointer user_data)
+{
+	GcrPrompterDialog *self = GCR_PROMPTER_DIALOG (user_data);
+	gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
+	gtk_widget_hide (GTK_WIDGET (self->image));
+	gtk_widget_show (GTK_WIDGET (self->spinner));
+}
+
+static const gchar *
+grab_status_message (GdkGrabStatus status)
+{
+	switch (status) {
+	case GDK_GRAB_SUCCESS:
+		g_return_val_if_reached ("");
+	case GDK_GRAB_ALREADY_GRABBED:
+		return "already grabbed";
+	case GDK_GRAB_INVALID_TIME:
+		return "invalid time";
+	case GDK_GRAB_NOT_VIEWABLE:
+		return "not viewable";
+	case GDK_GRAB_FROZEN:
+		return "frozen";
+	default:
+		g_message ("unknown grab status: %d", (int)status);
+		return "unknown";
+	}
+}
+
+static gboolean
+on_grab_broken (GtkWidget *widget,
+                GdkEventGrabBroken *event,
+                gpointer user_data)
+{
+	GcrPrompterDialog *self = GCR_PROMPTER_DIALOG (user_data);
+	if (self->grabbed_device && event->keyboard)
+		self->grabbed_device = NULL;
+	return TRUE;
+}
+
+static gboolean
+grab_keyboard (GtkWidget *widget,
+               GdkEvent *event,
+               gpointer user_data)
+{
+	GcrPrompterDialog *self = GCR_PROMPTER_DIALOG (user_data);
+	GdkGrabStatus status;
+	guint32 at;
+
+	GdkDevice *device = NULL;
+	GdkDeviceManager *manager;
+	GdkDisplay *display;
+	GList *devices, *l;
+
+	if (self->grabbed_device || !GRAB_KEYBOARD)
+		return FALSE;
+
+	display = gtk_widget_get_display (widget);
+	manager = gdk_display_get_device_manager (display);
+	devices = gdk_device_manager_list_devices (manager, GDK_DEVICE_TYPE_MASTER);
+	for (l = devices; l; l = g_list_next (l)) {
+		device = l->data;
+		if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
+			break;
+	}
+	g_list_free (devices);
+
+	if (!device) {
+		g_message ("couldn't find device to grab");
+		return FALSE;
+	}
+
+	at = event ? gdk_event_get_time (event) : GDK_CURRENT_TIME;
+	status = gdk_device_grab (device, gtk_widget_get_window (widget),
+	                          GDK_OWNERSHIP_WINDOW, TRUE,
+	                          GDK_KEY_PRESS | GDK_KEY_RELEASE, NULL, at);
+	if (status == GDK_GRAB_SUCCESS) {
+		self->grab_broken_id = g_signal_connect (widget, "grab-broken-event",
+		                                         G_CALLBACK (on_grab_broken), self);
+		gtk_device_grab_add (widget, device, TRUE);
+		self->grabbed_device = device;
+	} else {
+		g_message ("could not grab keyboard: %s", grab_status_message (status));
+	}
+
+	/* Always return false, so event is handled elsewhere */
+	return FALSE;
+}
+
+
+static gboolean
+ungrab_keyboard (GtkWidget *widget,
+                 GdkEvent *event,
+                 gpointer user_data)
+{
+	guint32 at = event ? gdk_event_get_time (event) : GDK_CURRENT_TIME;
+	GcrPrompterDialog *self = GCR_PROMPTER_DIALOG (user_data);
+
+	if (self->grabbed_device) {
+		g_signal_handler_disconnect (widget, self->grab_broken_id);
+		gdk_device_ungrab (self->grabbed_device, at);
+		gtk_device_grab_remove (widget, self->grabbed_device);
+		self->grabbed_device = NULL;
+		self->grab_broken_id = 0;
+	}
+
+	/* Always return false, so event is handled elsewhere */
+	return FALSE;
+}
+
+static gboolean
+window_state_changed (GtkWidget *widget,
+                      GdkEventWindowState *event,
+                      gpointer user_data)
+{
+	GdkWindowState state = gdk_window_get_state (gtk_widget_get_window (widget));
+
+	if (state & GDK_WINDOW_STATE_WITHDRAWN ||
+	    state & GDK_WINDOW_STATE_ICONIFIED ||
+	    state & GDK_WINDOW_STATE_FULLSCREEN ||
+	    state & GDK_WINDOW_STATE_MAXIMIZED)
+		ungrab_keyboard (widget, (GdkEvent*)event, user_data);
+	else
+		grab_keyboard (widget, (GdkEvent*)event, user_data);
+
+	return FALSE;
+}
+
+static void
+on_password_changed (GtkEditable *editable,
+                     gpointer user_data)
+{
+	int upper, lower, digit, misc;
+	const char *password;
+	gdouble pwstrength;
+	int length, i;
+
+	password = gtk_entry_get_text (GTK_ENTRY (editable));
+
+	/*
+	 * This code is based on the Master Password dialog in Firefox
+	 * (pref-masterpass.js)
+	 * Original code triple-licensed under the MPL, GPL, and LGPL
+	 * so is license-compatible with this file
+	 */
+
+	length = strlen (password);
+	upper = 0;
+	lower = 0;
+	digit = 0;
+	misc = 0;
+
+	for ( i = 0; i < length ; i++) {
+		if (g_ascii_isdigit (password[i]))
+			digit++;
+		else if (g_ascii_islower (password[i]))
+			lower++;
+		else if (g_ascii_isupper (password[i]))
+			upper++;
+		else
+			misc++;
+	}
+
+	if (length > 5)
+		length = 5;
+	if (digit > 3)
+		digit = 3;
+	if (upper > 3)
+		upper = 3;
+	if (misc > 3)
+		misc = 3;
+
+	pwstrength = ((length * 0.1) - 0.2) +
+	              (digit * 0.1) +
+	              (misc * 0.15) +
+	              (upper * 0.1);
+
+	if (pwstrength < 0.0)
+		pwstrength = 0.0;
+	if (pwstrength > 1.0)
+		pwstrength = 1.0;
+
+	gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (user_data), pwstrength);
+}
+
+static void
+handle_password_response (GcrPrompterDialog *self)
+{
+	const gchar *password;
+	const gchar *confirm;
+	const gchar *env;
+	gint strength;
+
+	password = gtk_entry_buffer_get_text (self->password_buffer);
+
+	/* Is it a new password? */
+	if (gcr_system_prompter_get_password_new (the_prompter)) {
+		confirm = gtk_entry_buffer_get_text (self->confirm_buffer);
+
+		/* Do the passwords match? */
+		if (!g_str_equal (password, confirm)) {
+			gcr_system_prompter_set_warning (the_prompter, _("Passwords do not match."));
+			return;
+		}
+
+		/* Don't allow blank passwords if in paranoid mode */
+		env = g_getenv ("GNOME_KEYRING_PARANOID");
+		if (env && *env) {
+			gcr_system_prompter_set_warning (the_prompter, _("Password cannot be blank"));
+			return;
+		}
+	}
+
+	if (g_str_equal (password, ""))
+		strength = 0;
+	else
+		strength = 1;
+	gcr_system_prompter_set_password_strength (the_prompter, strength);
+
+	gcr_system_prompter_respond_with_password (the_prompter, password);
+}
+
+static void
+gcr_prompter_dialog_response (GtkDialog *dialog,
+                              gint response_id)
+{
+	GcrPrompterDialog *self = GCR_PROMPTER_DIALOG (dialog);
+
+	if (response_id != GTK_RESPONSE_OK)
+		gcr_system_prompter_respond_cancelled (the_prompter);
+
+	else if (self->mode == PROMPT_PASSWORDING)
+		handle_password_response (self);
+
+	else if (self->mode == PROMPT_CONFIRMING)
+		gcr_system_prompter_respond_confirmed (the_prompter);
+
+	else
+		g_return_if_reached ();
+}
+
+static void
+gcr_prompter_dialog_init (GcrPrompterDialog *self)
+{
+	PangoAttrList *attrs;
+	GtkDialog *dialog;
+	GtkWidget *widget;
+	GtkWidget *entry;
+	GtkGrid *grid;
+
+	g_assert (GCR_IS_SYSTEM_PROMPTER (the_prompter));
+
+	g_signal_connect (the_prompter, "show-prompt", G_CALLBACK (on_show_prompt), self);
+	g_signal_connect (the_prompter, "prompt-password", G_CALLBACK (on_prompt_password), self);
+	g_signal_connect (the_prompter, "prompt-confirm", G_CALLBACK (on_prompt_confirm), self);
+	g_signal_connect (the_prompter, "responded", G_CALLBACK (on_responded), self);
+	g_signal_connect (the_prompter, "hide-prompt", G_CALLBACK (on_hide_prompt), self);
+
+	dialog = GTK_DIALOG (self);
+	gtk_dialog_add_buttons (dialog,
+	                        _("Continue"), GTK_RESPONSE_OK,
+	                        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+	                        NULL);
+
+	grid = GTK_GRID (gtk_grid_new ());
+
+	/* The prompt image */
+	self->image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_AUTHENTICATION,
+	                                        GTK_ICON_SIZE_DIALOG),
+	gtk_grid_attach (grid, self->image, -1, 0, 1, 4);
+	gtk_widget_show (self->image);
+
+	/* The prompt spinner */
+	self->spinner = gtk_spinner_new ();
+	gtk_grid_attach (grid, self->spinner, -2, -1, 1, 4);
+	gtk_widget_show (self->spinner);
+
+	/* The title label */
+	widget = gtk_label_new ("");
+	attrs = pango_attr_list_new ();
+	pango_attr_list_insert (attrs, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
+	pango_attr_list_insert (attrs, pango_attr_scale_new (PANGO_SCALE_LARGE));
+	gtk_label_set_attributes (GTK_LABEL (widget), attrs);
+	pango_attr_list_unref (attrs);
+	g_object_bind_property (the_prompter, "title", widget, "label", G_BINDING_DEFAULT);
+	gtk_grid_attach (grid, widget, 0, 0, 2, 1);
+	gtk_widget_show (widget);
+
+	/* The description label */
+	widget = gtk_label_new ("");
+	g_object_bind_property (the_prompter, "description", widget, "label", G_BINDING_DEFAULT);
+	gtk_grid_attach (grid, widget, 0, 1, 2, 1);
+	gtk_widget_show (widget);
+
+	/* The password label */
+	widget = gtk_label_new (_("Password:"));
+	g_object_bind_property (self, "password-visible", widget, "visible", G_BINDING_DEFAULT);
+	gtk_grid_attach (grid, widget, 0, 2, 1, 1);
+
+	/* The password entry */
+	self->password_buffer = gcr_secure_entry_buffer_new ();
+	entry = gtk_entry_new_with_buffer (self->password_buffer);
+	gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE);
+	g_object_bind_property (self, "password-visible", entry, "visible", G_BINDING_DEFAULT);
+	gtk_grid_attach (grid, entry, 1, 2, 1, 1);
+
+	/* The confirm label */
+	widget = gtk_label_new (_("Confirm:"));
+	g_object_bind_property (self, "confirm-visible", widget, "visible", G_BINDING_DEFAULT);
+	gtk_grid_attach (grid, widget, 0, 3, 1, 1);
+
+	/* The confirm entry */
+	self->confirm_buffer = gcr_secure_entry_buffer_new ();
+	widget = gtk_entry_new_with_buffer (self->password_buffer);
+	gtk_entry_set_visibility (GTK_ENTRY (widget), FALSE);
+	g_object_bind_property (self, "confirm-visible", widget, "visible", G_BINDING_DEFAULT);
+	gtk_grid_attach (grid, widget, 1, 3, 1, 1);
+
+	/* The quality progress bar */
+	widget = gtk_progress_bar_new ();
+	g_object_bind_property (self, "confirm-visible", widget, "visible", G_BINDING_DEFAULT);
+	gtk_grid_attach (grid, widget, 1, 4, 1, 1);
+	g_signal_connect (entry, "changed", G_CALLBACK (on_password_changed), widget);
+
+	/* The warning */
+	widget = gtk_label_new ("");
+	attrs = pango_attr_list_new ();
+	pango_attr_list_insert (attrs, pango_attr_style_new (PANGO_STYLE_ITALIC));
+	gtk_label_set_attributes (GTK_LABEL (widget), attrs);
+	pango_attr_list_unref (attrs);
+	g_object_bind_property (the_prompter, "warning", widget, "label", G_BINDING_DEFAULT);
+	g_object_bind_property (self, "warning-visible", widget, "visible", G_BINDING_DEFAULT);
+	gtk_grid_attach (grid, widget, 0, 5, 2, 1);
+	gtk_widget_show (widget);
+
+	/* The checkbox */
+	widget = gtk_check_button_new ();
+	g_object_bind_property (the_prompter, "choice-label", widget, "label", G_BINDING_DEFAULT);
+	g_object_bind_property (self, "choice-visible", widget, "visible", G_BINDING_DEFAULT);
+	g_object_bind_property (the_prompter, "choice-chosen", widget, "active", G_BINDING_BIDIRECTIONAL);
+	gtk_grid_attach (grid, widget, 0, 6, 2, 1);
+
+	gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (dialog)),
+	                   GTK_WIDGET (grid));
+	gtk_widget_show (GTK_WIDGET (grid));
+
+	g_signal_connect (self, "map-event", G_CALLBACK (grab_keyboard), self);
+	g_signal_connect (self, "unmap-event", G_CALLBACK (ungrab_keyboard), self);
+	g_signal_connect (self, "window-state-event", G_CALLBACK (window_state_changed), self);
+}
+
+static void
+gcr_prompter_dialog_get_property (GObject *obj,
+                                  guint prop_id,
+                                  GValue *value,
+                                  GParamSpec *pspec)
+{
+	GcrPrompterDialog *self = GCR_PROMPTER_DIALOG (obj);
+	const gchar *string;
+
+	switch (prop_id)
+	{
+	case PROP_PASSWORD_VISIBLE:
+		g_value_set_boolean (value, self->mode == PROMPT_PASSWORDING);
+		break;
+	case PROP_CONFIRM_VISIBLE:
+		g_value_set_boolean (value, gcr_system_prompter_get_password_new (the_prompter) &&
+		                            self->mode == PROMPT_PASSWORDING);
+		break;
+	case PROP_WARNING_VISIBLE:
+		string = gcr_system_prompter_get_warning (the_prompter);
+		g_value_set_boolean (value, string && string[0]);
+		break;
+	case PROP_CHOICE_VISIBLE:
+		string = gcr_system_prompter_get_choice_label (the_prompter);
+		g_value_set_boolean (value, string && string[0]);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gcr_prompter_dialog_finalize (GObject *obj)
+{
+	G_OBJECT_GET_CLASS (gcr_prompter_dialog_parent_class)->finalize (obj);
+}
+
+static void
+gcr_prompter_dialog_class_init (GcrPrompterDialogClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	GtkDialogClass *dialog_class = GTK_DIALOG_CLASS (klass);
+
+	gobject_class->finalize = gcr_prompter_dialog_finalize;
+	gobject_class->get_property = gcr_prompter_dialog_get_property;
+
+	dialog_class->response = gcr_prompter_dialog_response;
+
+	g_object_class_install_property (gobject_class, PROP_PASSWORD_VISIBLE,
+	           g_param_spec_boolean ("password-visible", "", "",
+	                                 FALSE, G_PARAM_READABLE));
+
+	g_object_class_install_property (gobject_class, PROP_CONFIRM_VISIBLE,
+	           g_param_spec_boolean ("confirm-visible", "", "",
+	                                 FALSE, G_PARAM_READABLE));
+
+	g_object_class_install_property (gobject_class, PROP_WARNING_VISIBLE,
+	           g_param_spec_boolean ("warning-visible", "", "",
+	                                 FALSE, G_PARAM_READABLE));
+
+	g_object_class_install_property (gobject_class, PROP_CHOICE_VISIBLE,
+	           g_param_spec_boolean ("choice-visible", "", "",
+	                                 FALSE, G_PARAM_READABLE));
+}
+
+static void
+on_bus_acquired (GDBusConnection *connection,
+                 const gchar *name,
+                 gpointer user_data)
+{
+	gcr_system_prompter_register (the_prompter, connection);
+}
+
+static void
+on_name_acquired (GDBusConnection *connection,
+                  const gchar *name,
+                  gpointer user_data)
+{
+	g_printerr ("bus name acquired");
+}
+
+static void
+on_name_lost (GDBusConnection *connection,
+              const gchar *name,
+              gpointer user_data)
+{
+	g_printerr ("bus name lost, quitting");
+	gtk_main_quit ();
+}
+
+int
+main (int argc, char *argv[])
+{
+	GtkDialog *dialog;
+	guint owner_id;
+
+	g_type_init ();
+	gtk_init (&argc, &argv);
+
+#ifdef HAVE_LOCALE_H
+	/* internationalisation */
+	setlocale (LC_ALL, "");
+#endif
+
+#ifdef HAVE_GETTEXT
+	bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+	textdomain (GETTEXT_PACKAGE);
+	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif
+
+	the_prompter = gcr_system_prompter_new ();
+	dialog = g_object_new (GCR_TYPE_PROMPTER_DIALOG, NULL);
+	owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
+	                           GCR_DBUS_PROMPTER_BUS_NAME,
+	                           G_BUS_NAME_OWNER_FLAGS_NONE,
+	                           on_bus_acquired,
+	                           on_name_acquired,
+	                           on_name_lost,
+	                           NULL,
+	                           NULL);
+
+	gtk_main ();
+
+	g_bus_unown_name (owner_id);
+	g_object_unref (the_prompter);
+	gtk_widget_destroy (GTK_WIDGET (dialog));
+
+	return 0;
+}
diff --git a/gcr/gcr-system-prompt.c b/gcr/gcr-system-prompt.c
new file mode 100644
index 0000000..16e1733
--- /dev/null
+++ b/gcr/gcr-system-prompt.c
@@ -0,0 +1,1330 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2011 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.
+ *
+ * Author: Stef Walter <stef thewalter net>
+ */
+
+#include "config.h"
+
+#include "gcr-dbus-constants.h"
+#include "gcr-dbus-generated.h"
+#include "gcr-internal.h"
+#include "gcr-library.h"
+#include "gcr-secret-exchange.h"
+#include "gcr-system-prompt.h"
+
+/**
+ * SECTION:gcr-system-prompt
+ * @title: GcrSystemPrompt
+ * @short_description: XXX
+ *
+ * XXXX
+ */
+
+/**
+ * GcrSystemPrompt:
+ *
+ * XXX
+ */
+
+/**
+ * GcrSystemPromptClass:
+ *
+ * The class for #GcrSystemPrompt.
+ */
+
+enum {
+	PROP_0,
+	PROP_BUS_NAME,
+	PROP_SECRET_EXCHANGE,
+	PROP_TIMEOUT_SECONDS,
+	PROP_TITLE,
+	PROP_MESSAGE,
+	PROP_DESCRIPTION,
+	PROP_WARNING,
+	PROP_PASSWORD_NEW,
+	PROP_PASSWORD_STRENGTH,
+	PROP_CHOICE_LABEL,
+	PROP_CHOICE_CHOSEN,
+	PROP_CALLER_WINDOW
+};
+
+struct _GcrSystemPromptPrivate {
+	gchar *prompter_bus_name;
+	GDBusConnection *connection;
+	GcrSecretExchange *exchange;
+	GHashTable *properties_to_write;
+	GHashTable *property_cache;
+	GDBusProxy *prompt_proxy;
+	gulong prompt_properties_sig;
+	gchar *prompt_path;
+	gboolean exchanged;
+	gboolean begun_prompting;
+	gint timeout_seconds;
+};
+
+static void     gcr_system_prompt_initable_iface       (GInitableIface *iface);
+
+static void     gcr_system_prompt_async_initable_iface (GAsyncInitableIface *iface);
+
+static void     perform_init_async                     (GcrSystemPrompt *self,
+                                                        GSimpleAsyncResult *res);
+
+G_DEFINE_TYPE_WITH_CODE (GcrSystemPrompt, gcr_system_prompt, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gcr_system_prompt_initable_iface);
+                         G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, gcr_system_prompt_async_initable_iface);
+);
+
+static void
+gcr_system_prompt_init (GcrSystemPrompt *self)
+{
+	self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_SYSTEM_PROMPT,
+	                                        GcrSystemPromptPrivate);
+
+	self->pv->timeout_seconds = -1;
+	self->pv->properties_to_write = g_hash_table_new (g_str_hash, g_str_equal);
+	self->pv->property_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
+	                                                  NULL, (GDestroyNotify)g_variant_unref);
+}
+
+static void
+gcr_system_prompt_set_property (GObject *obj,
+                                guint prop_id,
+                                const GValue *value,
+                                GParamSpec *pspec)
+{
+	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (obj);
+
+	switch (prop_id) {
+	case PROP_BUS_NAME:
+		g_assert (self->pv->prompter_bus_name == NULL);
+		self->pv->prompter_bus_name = g_value_dup_string (value);
+		break;
+	case PROP_SECRET_EXCHANGE:
+		gcr_system_prompt_set_secret_exchange (self, g_value_get_object (value));
+		break;
+	case PROP_TIMEOUT_SECONDS:
+		self->pv->timeout_seconds = g_value_get_int (value);
+		break;
+	case PROP_TITLE:
+		gcr_system_prompt_set_title (self, g_value_get_string (value));
+		break;
+	case PROP_MESSAGE:
+		gcr_system_prompt_set_message (self, g_value_get_string (value));
+		break;
+	case PROP_DESCRIPTION:
+		gcr_system_prompt_set_description (self, g_value_get_string (value));
+		break;
+	case PROP_WARNING:
+		gcr_system_prompt_set_warning (self, g_value_get_string (value));
+		break;
+	case PROP_PASSWORD_NEW:
+		gcr_system_prompt_set_password_new (self, g_value_get_boolean (value));
+		break;
+	case PROP_CHOICE_LABEL:
+		gcr_system_prompt_set_choice_label (self, g_value_get_string (value));
+		break;
+	case PROP_CHOICE_CHOSEN:
+		gcr_system_prompt_set_choice_chosen (self, g_value_get_boolean (value));
+		break;
+	case PROP_CALLER_WINDOW:
+		gcr_system_prompt_set_caller_window (self, g_value_get_string (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gcr_system_prompt_get_property (GObject *obj,
+                                guint prop_id,
+                                GValue *value,
+                                GParamSpec *pspec)
+{
+	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (obj);
+
+	switch (prop_id) {
+	case PROP_BUS_NAME:
+		g_value_set_string (value, self->pv->prompter_bus_name);
+		break;
+	case PROP_SECRET_EXCHANGE:
+		g_value_set_object (value, gcr_system_prompt_get_secret_exchange (self));
+		break;
+	case PROP_TITLE:
+		g_value_set_string (value, gcr_system_prompt_get_title (self));
+		break;
+	case PROP_DESCRIPTION:
+		g_value_set_string (value, gcr_system_prompt_get_description (self));
+		break;
+	case PROP_WARNING:
+		g_value_set_string (value, gcr_system_prompt_get_warning (self));
+		break;
+	case PROP_PASSWORD_NEW:
+		g_value_set_boolean (value, gcr_system_prompt_get_password_new (self));
+		break;
+	case PROP_PASSWORD_STRENGTH:
+		g_value_set_int (value, gcr_system_prompt_get_password_strength (self));
+		break;
+	case PROP_CHOICE_LABEL:
+		g_value_set_string (value, gcr_system_prompt_get_choice_label (self));
+		break;
+	case PROP_CHOICE_CHOSEN:
+		g_value_set_boolean (value, gcr_system_prompt_get_choice_chosen (self));
+		break;
+	case PROP_CALLER_WINDOW:
+		g_value_set_string (value, gcr_system_prompt_get_caller_window (self));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gcr_system_prompt_dispose (GObject *obj)
+{
+	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (obj);
+
+	g_clear_object (&self->pv->exchange);
+
+	if (self->pv->prompt_path) {
+		g_dbus_connection_call (self->pv->connection,
+		                        self->pv->prompter_bus_name,
+		                        GCR_DBUS_PROMPTER_OBJECT_PATH,
+		                        GCR_DBUS_PROMPTER_INTERFACE,
+		                        GCR_DBUS_PROMPTER_METHOD_FINISH,
+		                        g_variant_new ("()"),
+		                        NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START,
+		                        -1, NULL, NULL, NULL);
+		g_free (self->pv->prompt_path);
+		self->pv->prompt_path = NULL;
+	}
+
+	g_hash_table_remove_all (self->pv->properties_to_write);
+	g_hash_table_remove_all (self->pv->property_cache);
+
+	if (self->pv->prompt_proxy) {
+		g_signal_handler_disconnect (self->pv->prompt_proxy, self->pv->prompt_properties_sig);
+		g_object_unref (self->pv->prompt_proxy);
+		self->pv->prompt_proxy = NULL;
+		self->pv->prompt_properties_sig = 0;
+	}
+
+	g_clear_object (&self->pv->connection);
+
+	G_OBJECT_CLASS (gcr_system_prompt_parent_class)->dispose (obj);
+}
+
+static void
+gcr_system_prompt_finalize (GObject *obj)
+{
+	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (obj);
+
+	g_hash_table_destroy (self->pv->properties_to_write);
+	g_hash_table_destroy (self->pv->property_cache);
+
+	G_OBJECT_CLASS (gcr_system_prompt_parent_class)->finalize (obj);
+}
+
+static void
+gcr_system_prompt_class_init (GcrSystemPromptClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+	gobject_class->get_property = gcr_system_prompt_get_property;
+	gobject_class->set_property = gcr_system_prompt_set_property;
+	gobject_class->dispose = gcr_system_prompt_dispose;
+	gobject_class->finalize = gcr_system_prompt_finalize;
+
+	g_type_class_add_private (gobject_class, sizeof (GcrSystemPromptPrivate));
+
+	g_object_class_install_property (gobject_class, PROP_BUS_NAME,
+	            g_param_spec_string ("bus-name", "Bus name", "Prompter bus name",
+	                                 NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property (gobject_class, PROP_TIMEOUT_SECONDS,
+	               g_param_spec_int ("timeout-seconds", "Timeout seconds", "Timeout (in seconds) for opening prompt",
+	                                 -1, G_MAXINT, -1, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property (gobject_class, PROP_SECRET_EXCHANGE,
+	            g_param_spec_object ("secret-exchange", "Secret exchange", "Secret exchange for passing passwords",
+	                                 GCR_TYPE_SECRET_EXCHANGE, G_PARAM_READWRITE));
+
+	g_object_class_install_property (gobject_class, PROP_TITLE,
+	            g_param_spec_string ("title", "Title", "Prompt title",
+	                                 NULL, G_PARAM_READWRITE));
+
+	g_object_class_install_property (gobject_class, PROP_MESSAGE,
+	            g_param_spec_string ("message", "Message", "Prompt message",
+	                                 NULL, G_PARAM_READWRITE));
+
+	g_object_class_install_property (gobject_class, PROP_DESCRIPTION,
+	            g_param_spec_string ("description", "Description", "Prompt description",
+	                                 NULL, G_PARAM_READWRITE));
+
+	g_object_class_install_property (gobject_class, PROP_WARNING,
+	            g_param_spec_string ("warning", "Warning", "Prompt warning",
+	                                 NULL, G_PARAM_READWRITE));
+
+	g_object_class_install_property (gobject_class, PROP_PASSWORD_NEW,
+	           g_param_spec_boolean ("password-new", "Password new", "Whether prompting for a new password",
+	                                 FALSE, G_PARAM_READWRITE));
+
+	g_object_class_install_property (gobject_class, PROP_PASSWORD_STRENGTH,
+	               g_param_spec_int ("password-strength", "Password strength", "String of new password",
+	                                 0, G_MAXINT, 0, G_PARAM_READABLE));
+
+	g_object_class_install_property (gobject_class, PROP_CHOICE_LABEL,
+	            g_param_spec_string ("choice-label", "Choice label", "Label for prompt choice",
+	                                 NULL, G_PARAM_READWRITE));
+
+	g_object_class_install_property (gobject_class, PROP_CHOICE_CHOSEN,
+	           g_param_spec_boolean ("choice-chosen", "Choice chosen", "Whether prompt choice is chosen",
+	                                  FALSE, G_PARAM_READWRITE));
+
+	g_object_class_install_property (gobject_class, PROP_CALLER_WINDOW,
+	            g_param_spec_string ("caller-window", "Caller window", "Window ID of application window requesting prompt",
+	                                 NULL, G_PARAM_READWRITE));
+}
+
+/**
+ * gcr_system_prompt_get_secret_exchange:
+ * @self: a prompter
+ *
+ * Get the current #GcrSecretExchange used to transfer secrets in this prompt.
+ *
+ * Returns: (transfer none): the secret exchange
+ */
+GcrSecretExchange *
+gcr_system_prompt_get_secret_exchange (GcrSystemPrompt *self)
+{
+	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPT (self), NULL);
+
+	if (!self->pv->exchange)
+		self->pv->exchange = gcr_secret_exchange_new (NULL);
+
+	return self->pv->exchange;
+}
+
+void
+gcr_system_prompt_set_secret_exchange (GcrSystemPrompt *self,
+                                       GcrSecretExchange *exchange)
+{
+	g_return_if_fail (GCR_IS_SYSTEM_PROMPT (self));
+	g_return_if_fail (GCR_IS_SECRET_EXCHANGE (exchange));
+
+	if (self->pv->exchange) {
+		g_warning ("The secret exchange is already in use, and cannot be changed");
+		return;
+	}
+
+	self->pv->exchange = g_object_ref (exchange);
+	g_object_notify (G_OBJECT (self), "secret-exchange");
+}
+
+static GVariant *
+lookup_property_in_caches (GcrSystemPrompt *self,
+                           const gchar *property_name)
+{
+	GVariant *variant;
+
+	variant = g_hash_table_lookup (self->pv->property_cache, property_name);
+	if (variant == NULL && self->pv->prompt_proxy) {
+		variant = g_dbus_proxy_get_cached_property (self->pv->prompt_proxy, property_name);
+		if (variant != NULL)
+			g_hash_table_insert (self->pv->property_cache, (gpointer)property_name, variant);
+	}
+
+	return variant;
+}
+
+static void
+on_prompt_properties_changed (GDBusProxy *proxy,
+                              GVariant   *changed_properties,
+                              GStrv       invalidated_properties,
+                              gpointer    user_data)
+{
+	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (user_data);
+	GVariantIter iter;
+	GVariant *value;
+	gchar *key;
+	guint i;
+
+	g_variant_iter_init (&iter, changed_properties);
+	while (g_variant_iter_next (&iter, "{sv}", &key, &value)) {
+		if (!g_hash_table_lookup (self->pv->properties_to_write, key))
+			g_hash_table_remove (self->pv->property_cache, key);
+		g_free (key);
+		g_variant_unref (value);
+	}
+
+	for (i = 0; invalidated_properties != NULL && invalidated_properties[i] != NULL; i++) {
+		if (!g_hash_table_lookup (self->pv->properties_to_write, invalidated_properties[i]))
+			g_hash_table_remove (self->pv->property_cache, invalidated_properties[i]);
+	}
+}
+
+static const gchar *
+prompt_get_string_property (GcrSystemPrompt *self,
+                            const gchar *property_name)
+{
+	GVariant *variant;
+
+	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPT (self), NULL);
+
+	variant = lookup_property_in_caches (self, property_name);
+	if (variant != NULL)
+		return g_variant_get_string (variant, NULL);
+
+	return NULL;
+}
+
+static void
+prompt_set_string_property (GcrSystemPrompt *self,
+                            const gchar *property_name,
+                            const gchar *value)
+{
+	GVariant *variant;
+	gpointer key;
+
+	g_return_if_fail (GCR_IS_SYSTEM_PROMPT (self));
+
+	key = (gpointer)g_intern_string (property_name);
+	variant = g_variant_ref_sink (g_variant_new_string (value ? value : ""));
+	g_hash_table_insert (self->pv->property_cache, key, variant);
+	g_hash_table_insert (self->pv->properties_to_write, key, key);
+}
+
+static gboolean
+prompt_get_boolean_property (GcrSystemPrompt *self,
+                             const gchar *property_name)
+{
+	GVariant *variant;
+
+	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPT (self), FALSE);
+
+	variant = lookup_property_in_caches (self, property_name);
+	if (variant != NULL)
+		return g_variant_get_boolean (variant);
+
+	return FALSE;
+}
+
+static void
+prompt_set_boolean_property (GcrSystemPrompt *self,
+                             const gchar *property_name,
+                             gboolean value)
+{
+	GVariant *variant;
+	gpointer key;
+
+	g_return_if_fail (GCR_IS_SYSTEM_PROMPT (self));
+
+	key = (gpointer)g_intern_string (property_name);
+	variant = g_variant_ref_sink (g_variant_new_boolean (value));
+	g_hash_table_insert (self->pv->property_cache, key, variant);
+	g_hash_table_insert (self->pv->properties_to_write, key, key);
+}
+
+const gchar *
+gcr_system_prompt_get_title (GcrSystemPrompt *self)
+{
+	return prompt_get_string_property (self, GCR_DBUS_PROMPT_PROPERTY_TITLE);
+}
+
+void
+gcr_system_prompt_set_title (GcrSystemPrompt *self,
+                             const gchar *title)
+{
+	prompt_set_string_property (self, GCR_DBUS_PROMPT_PROPERTY_TITLE, title);
+	g_object_notify (G_OBJECT (self), "title");
+}
+
+const gchar *
+gcr_system_prompt_get_message (GcrSystemPrompt *self)
+{
+	return prompt_get_string_property (self, GCR_DBUS_PROMPT_PROPERTY_MESSAGE);
+}
+
+void
+gcr_system_prompt_set_message (GcrSystemPrompt *self,
+                               const gchar *message)
+{
+	prompt_set_string_property (self, GCR_DBUS_PROMPT_PROPERTY_MESSAGE, message);
+	g_object_notify (G_OBJECT (self), "message");
+}
+
+
+const gchar *
+gcr_system_prompt_get_description (GcrSystemPrompt *self)
+{
+	return prompt_get_string_property (self, GCR_DBUS_PROMPT_PROPERTY_DESCRIPTION);
+}
+
+void
+gcr_system_prompt_set_description (GcrSystemPrompt *self,
+                                   const gchar *description)
+{
+	prompt_set_string_property (self, GCR_DBUS_PROMPT_PROPERTY_DESCRIPTION, description);
+	g_object_notify (G_OBJECT (self), "description");
+}
+
+const gchar *
+gcr_system_prompt_get_warning (GcrSystemPrompt *self)
+{
+	return prompt_get_string_property (self, GCR_DBUS_PROMPT_PROPERTY_WARNING);
+}
+
+void
+gcr_system_prompt_set_warning (GcrSystemPrompt *self,
+                               const gchar *warning)
+{
+	prompt_set_string_property (self, GCR_DBUS_PROMPT_PROPERTY_WARNING, warning);
+	g_object_notify (G_OBJECT (self), "warning");
+}
+
+gboolean
+gcr_system_prompt_get_password_new (GcrSystemPrompt *self)
+{
+	return prompt_get_boolean_property (self, GCR_DBUS_PROMPT_PROPERTY_PASSWORD_NEW);
+}
+
+void
+gcr_system_prompt_set_password_new (GcrSystemPrompt *self,
+                                    gboolean new_password)
+{
+	prompt_set_boolean_property (self, GCR_DBUS_PROMPT_PROPERTY_PASSWORD_NEW, new_password);
+	g_object_notify (G_OBJECT (self), "password-new");
+}
+
+gint
+gcr_system_prompt_get_password_strength (GcrSystemPrompt *self)
+{
+	GVariant *variant;
+
+	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPT (self), 0);
+
+	variant = lookup_property_in_caches (self, GCR_DBUS_PROMPT_PROPERTY_PASSWORD_STRENGTH);
+	if (variant != NULL)
+		return g_variant_get_int32 (variant);
+
+	return 0;
+}
+
+
+const gchar *
+gcr_system_prompt_get_choice_label (GcrSystemPrompt *self)
+{
+	return prompt_get_string_property (self, GCR_DBUS_PROMPT_PROPERTY_CHOICE_LABEL);
+}
+
+void
+gcr_system_prompt_set_choice_label (GcrSystemPrompt *self,
+                                    const gchar *label)
+{
+	prompt_set_string_property (self, GCR_DBUS_PROMPT_PROPERTY_CHOICE_LABEL, label);
+	g_object_notify (G_OBJECT (self), "choice-label");
+}
+
+gboolean
+gcr_system_prompt_get_choice_chosen (GcrSystemPrompt *self)
+{
+	return prompt_get_boolean_property (self, GCR_DBUS_PROMPT_PROPERTY_CHOICE_CHOSEN);
+}
+
+void
+gcr_system_prompt_set_choice_chosen (GcrSystemPrompt *self,
+                                     gboolean chosen)
+{
+	prompt_set_boolean_property (self, GCR_DBUS_PROMPT_PROPERTY_CHOICE_CHOSEN, chosen);
+	g_object_notify (G_OBJECT (self), "choice-chosen");
+}
+
+const gchar *
+gcr_system_prompt_get_caller_window (GcrSystemPrompt *self)
+{
+	return prompt_get_string_property (self, GCR_DBUS_PROMPT_PROPERTY_CALLER_WINDOW);
+}
+
+void
+gcr_system_prompt_set_caller_window (GcrSystemPrompt *self,
+                                     const gchar *window_id)
+{
+	prompt_set_string_property (self, GCR_DBUS_PROMPT_PROPERTY_CALLER_WINDOW, window_id);
+	g_object_notify (G_OBJECT (self), "caller-window");
+}
+
+static gboolean
+gcr_system_prompt_real_init (GInitable *initable,
+                             GCancellable *cancellable,
+                             GError **error)
+{
+	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (initable);
+
+	/* 1. Connect to the session bus */
+	if (!self->pv->connection) {
+		self->pv->connection = g_bus_get_sync (G_BUS_TYPE_SESSION,
+		                                       cancellable, error);
+		if (self->pv->connection == NULL)
+			return FALSE;
+	}
+
+	/* 2. Tell the prompter we want to begin prompting */
+	if (!self->pv->prompt_path) {
+		GVariant *ret;
+
+		ret = g_dbus_connection_call_sync (self->pv->connection,
+		                                   self->pv->prompter_bus_name,
+		                                   GCR_DBUS_PROMPTER_OBJECT_PATH,
+		                                   GCR_DBUS_PROMPTER_INTERFACE,
+		                                   GCR_DBUS_PROMPTER_METHOD_BEGIN,
+		                                   g_variant_new ("()"),
+		                                   G_VARIANT_TYPE ("(o)"),
+		                                   G_DBUS_CALL_FLAGS_NONE,
+		                                   -1, cancellable, error);
+		if (ret == NULL)
+			return FALSE;
+
+		self->pv->begun_prompting = TRUE;
+		g_assert (self->pv->prompt_path == NULL);
+		g_variant_get (ret, "(o)", &self->pv->prompt_path);
+		g_variant_unref (ret);
+	}
+
+	/* 3. Create a dbus proxy */
+	if (!self->pv->prompt_proxy) {
+		self->pv->prompt_proxy = g_dbus_proxy_new_sync (self->pv->connection,
+		                                                G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+		                                                _gcr_prompter_prompt_interface_info (),
+		                                                self->pv->prompter_bus_name,
+		                                                self->pv->prompt_path,
+		                                                GCR_DBUS_PROMPT_INTERFACE,
+		                                                cancellable, error);
+		if (self->pv->prompt_proxy == NULL)
+			return FALSE;
+
+		g_dbus_proxy_set_default_timeout (self->pv->prompt_proxy, G_MAXINT);
+		self->pv->prompt_properties_sig = g_signal_connect (self->pv->prompt_proxy, "g-properties-changed",
+		                                                    G_CALLBACK (on_prompt_properties_changed), self);
+	}
+
+	return TRUE;
+}
+
+static void
+gcr_system_prompt_initable_iface (GInitableIface *iface)
+{
+	iface->init = gcr_system_prompt_real_init;
+}
+
+typedef struct {
+	GCancellable *cancellable;
+	guint subscribe_sig;
+} InitClosure;
+
+static void
+init_closure_free (gpointer data)
+{
+	InitClosure *closure = data;
+	g_clear_object (&closure->cancellable);
+	g_free (closure);
+}
+
+static void
+on_bus_connected (GObject *source,
+                  GAsyncResult *result,
+                  gpointer user_data)
+{
+	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (g_async_result_get_source_object (user_data));
+	GError *error = NULL;
+
+	g_assert (self->pv->connection == NULL);
+	self->pv->connection = g_bus_get_finish (result, &error);
+
+	if (error == NULL) {
+		g_return_if_fail (self->pv->connection != NULL);
+		perform_init_async (self, res);
+
+	} else {
+		g_simple_async_result_take_error (res, error);
+		g_simple_async_result_complete (res);
+	}
+
+	g_object_unref (self);
+	g_object_unref (res);
+}
+
+static void
+on_prompter_begin_prompting (GObject *source,
+                             GAsyncResult *result,
+                             gpointer user_data)
+{
+	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (g_async_result_get_source_object (user_data));
+	GError *error = NULL;
+	GVariant *ret;
+
+	ret = g_dbus_connection_call_finish (self->pv->connection, result, &error);
+
+	if (error == NULL) {
+		self->pv->begun_prompting = TRUE;
+		g_assert (self->pv->prompt_path == NULL);
+		g_variant_get (ret, "(o)", &self->pv->prompt_path);
+		g_variant_unref (ret);
+
+		g_return_if_fail (self->pv->prompt_path != NULL);
+		perform_init_async (self, res);
+
+	} else {
+		g_simple_async_result_take_error (res, error);
+		g_simple_async_result_complete (res);
+	}
+
+	g_object_unref (self);
+	g_object_unref (res);
+}
+
+static void
+on_prompt_proxy_new (GObject *source,
+                     GAsyncResult *result,
+                     gpointer user_data)
+{
+	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (g_async_result_get_source_object (user_data));
+	GError *error = NULL;
+
+	g_assert (self->pv->prompt_proxy == NULL);
+	self->pv->prompt_proxy = g_dbus_proxy_new_finish (result, &error);
+
+	if (error == NULL) {
+		g_return_if_fail (self->pv->prompt_proxy != NULL);
+		self->pv->prompt_properties_sig = g_signal_connect (self->pv->prompt_proxy, "g-properties-changed",
+		                                                   G_CALLBACK (on_prompt_properties_changed), self);
+
+		perform_init_async (self, res);
+
+	} else {
+		g_simple_async_result_take_error (res, error);
+	}
+
+	g_object_unref (self);
+	g_object_unref (res);
+}
+
+void
+perform_init_async (GcrSystemPrompt *self,
+                    GSimpleAsyncResult *res)
+{
+	InitClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
+
+	/* 1. Connect to the session bus */
+	if (!self->pv->connection) {
+		g_bus_get (G_BUS_TYPE_SESSION, closure->cancellable,
+		           on_bus_connected, g_object_ref (res));
+
+	/* 2. Tell the prompter we want to begin prompting */
+	} else if (!self->pv->prompt_path) {
+		g_dbus_connection_call (self->pv->connection,
+		                        self->pv->prompter_bus_name,
+		                        GCR_DBUS_PROMPTER_OBJECT_PATH,
+		                        GCR_DBUS_PROMPTER_INTERFACE,
+		                        GCR_DBUS_PROMPTER_METHOD_BEGIN,
+		                        g_variant_new ("()"),
+		                        G_VARIANT_TYPE ("(o)"),
+		                        G_DBUS_CALL_FLAGS_NONE,
+		                        -1, closure->cancellable,
+		                        on_prompter_begin_prompting,
+		                        g_object_ref (res));
+
+	/* 3. Create a dbus proxy */
+	} else if (!self->pv->prompt_proxy) {
+		g_dbus_proxy_new (self->pv->connection,
+		                  G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+		                  _gcr_prompter_prompt_interface_info (),
+		                  self->pv->prompter_bus_name,
+		                  self->pv->prompt_path,
+		                  GCR_DBUS_PROMPT_INTERFACE,
+		                  closure->cancellable,
+		                  on_prompt_proxy_new,
+		                  g_object_ref (res));
+
+	}
+
+	g_simple_async_result_complete (res);
+}
+
+static void
+gcr_system_prompt_real_init_async (GAsyncInitable *initable,
+                                   int io_priority,
+                                   GCancellable *cancellable,
+                                   GAsyncReadyCallback callback,
+                                   gpointer user_data)
+{
+	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (initable);
+	GSimpleAsyncResult *res;
+	InitClosure *closure;
+
+	res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
+	                                 gcr_system_prompt_real_init_async);
+
+	closure = g_new0 (InitClosure, 1);
+	closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+	g_simple_async_result_set_op_res_gpointer (res, closure, init_closure_free);
+
+	perform_init_async (self, res);
+
+	g_object_unref (res);
+
+}
+
+static gboolean
+gcr_system_prompt_real_init_finish (GAsyncInitable *initable,
+                                    GAsyncResult *result,
+                                    GError **error)
+{
+	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (initable);
+
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
+	                      gcr_system_prompt_real_init_async), FALSE);
+
+	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+		return FALSE;
+
+	return TRUE;
+}
+
+static void
+gcr_system_prompt_async_initable_iface (GAsyncInitableIface *iface)
+{
+	iface->init_async = gcr_system_prompt_real_init_async;
+	iface->init_finish = gcr_system_prompt_real_init_finish;
+}
+
+static GVariant *
+parameter_properties (GcrSystemPrompt *self)
+{
+	GHashTableIter iter;
+	GVariantBuilder builder;
+	const gchar *property_name;
+	GVariant *variant;
+	gchar *name;
+
+	g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+
+	g_hash_table_iter_init (&iter, self->pv->properties_to_write);
+	while (g_hash_table_iter_next (&iter, (gpointer *)&property_name, NULL)) {
+		variant = g_hash_table_lookup (self->pv->property_cache, property_name);
+		if (variant == NULL) {
+			g_warning ("couldn't find prompt property to write: %s", property_name);
+		} else {
+			name = g_strdup_printf ("%s.%s", GCR_DBUS_PROMPT_INTERFACE, property_name);
+			g_variant_builder_add (&builder, "{sv}", name, variant);
+			g_free (name);
+		}
+	}
+
+	g_hash_table_remove_all (self->pv->properties_to_write);
+	return g_variant_builder_end (&builder);
+}
+
+static GVariant *
+parameters_for_password (GcrSystemPrompt *self)
+{
+	GcrSecretExchange *exchange;
+	GVariant *properties;
+	GVariant *params;
+	gchar *input;
+
+	exchange = gcr_system_prompt_get_secret_exchange (self);
+
+	if (self->pv->exchanged)
+		input = gcr_secret_exchange_send (exchange, NULL, 0);
+	else
+		input = gcr_secret_exchange_begin (exchange);
+	self->pv->exchanged = TRUE;
+
+	properties = parameter_properties (self);
+	params = g_variant_new ("(@a{sv}s)", properties, input);
+	g_free (input);
+
+	return params;
+}
+
+static const gchar *
+return_for_password (GcrSystemPrompt *self,
+                     GVariant *retval,
+                     GError **error)
+{
+	GcrSecretExchange *exchange;
+	const gchar *ret = NULL;
+	gchar *output;
+
+	exchange = gcr_system_prompt_get_secret_exchange (self);
+	g_variant_get (retval, "(s)", &output);
+
+	if (output && output[0]) {
+		if (!gcr_secret_exchange_receive (exchange, output)) {
+			g_set_error (error, GCR_SYSTEM_PROMPT_ERROR,
+			             GCR_SYSTEM_PROMPT_FAILED, "Invalid secret exchanged");
+		} else {
+			ret = gcr_secret_exchange_get_secret (exchange, NULL);
+		}
+	}
+
+	g_free (output);
+
+	return ret;
+}
+
+static void
+on_prompt_requested_password (GObject *source,
+                              GAsyncResult *result,
+                              gpointer user_data)
+{
+	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (g_async_result_get_source_object (user_data));
+	const gchar *password;
+	GError *error = NULL;
+	GVariant *retval;
+
+	retval = g_dbus_proxy_call_finish (self->pv->prompt_proxy, result, &error);
+
+	if (retval != NULL) {
+		password = return_for_password (self, retval, &error);
+		g_simple_async_result_set_op_res_gpointer (res, (gpointer)password, NULL);
+		g_variant_unref (retval);
+	}
+
+	if (error != NULL)
+		g_simple_async_result_take_error (res, error);
+
+	g_simple_async_result_complete (res);
+	g_object_unref (self);
+	g_object_unref (res);
+}
+
+void
+gcr_system_prompt_password_async (GcrSystemPrompt *self,
+                                  GCancellable *cancellable,
+                                  GAsyncReadyCallback callback,
+                                  gpointer user_data)
+{
+	GSimpleAsyncResult *res;
+
+	g_return_if_fail (GCR_IS_SYSTEM_PROMPT (self));
+	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+	res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
+	                                 gcr_system_prompt_password_async);
+
+	g_dbus_proxy_call (G_DBUS_PROXY (self->pv->prompt_proxy),
+	                   GCR_DBUS_PROMPT_METHOD_PASSWORD,
+	                   parameters_for_password (self),
+	                   G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, cancellable,
+	                   on_prompt_requested_password, g_object_ref (res));
+
+	g_object_unref (res);
+}
+
+const gchar *
+gcr_system_prompt_password_finish (GcrSystemPrompt *self,
+                                   GAsyncResult *result,
+                                   GError **error)
+{
+	GSimpleAsyncResult *res;
+
+	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPT (self), FALSE);
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
+	                      gcr_system_prompt_password_async), FALSE);
+
+	res = G_SIMPLE_ASYNC_RESULT (result);
+	if (g_simple_async_result_propagate_error (res, error))
+		return FALSE;
+
+	return g_simple_async_result_get_op_res_gpointer (res);
+}
+
+const gchar *
+gcr_system_prompt_password (GcrSystemPrompt *self,
+                            GCancellable *cancellable,
+                            GError **error)
+{
+	GVariant *retval;
+	const gchar *ret = NULL;
+
+	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPT (self), NULL);
+	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
+	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+	if (!self->pv->prompt_proxy) {
+		g_warning ("GcrSystemPrompt was not successfully opened");
+		return NULL;
+	}
+
+	retval = g_dbus_proxy_call_sync (self->pv->prompt_proxy,
+	                                 GCR_DBUS_PROMPT_METHOD_PASSWORD,
+	                                 parameters_for_password (self),
+	                                 G_DBUS_CALL_FLAGS_NO_AUTO_START, -1,
+	                                 cancellable, error);
+
+	if (retval != NULL) {
+		ret = return_for_password (self, retval, error);
+		g_variant_unref (retval);
+	}
+
+	return ret;
+}
+
+static GVariant *
+parameters_for_confirm (GcrSystemPrompt *self)
+{
+	GVariant *properties;
+
+	properties = parameter_properties (self);
+	return g_variant_new ("(@a{sv})", properties);
+}
+
+static void
+on_prompt_requested_confirm (GObject *source,
+                             GAsyncResult *result,
+                             gpointer user_data)
+{
+	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (g_async_result_get_source_object (user_data));
+	GError *error = NULL;
+	GVariant *retval;
+
+	retval = g_dbus_proxy_call_finish (self->pv->prompt_proxy, result, &error);
+
+	if (retval != NULL) {
+		g_simple_async_result_set_op_res_gpointer (res, retval,
+		                                           (GDestroyNotify)g_variant_unref);
+	}
+
+	if (error != NULL)
+		g_simple_async_result_take_error (res, error);
+
+	g_simple_async_result_complete (res);
+	g_object_unref (self);
+	g_object_unref (res);
+}
+
+/**
+ * gcr_system_prompt_confirm_async:
+ * @self: a prompt
+ * @cancellable: optional cancellation object
+ *
+ * xxx
+ */
+void
+gcr_system_prompt_confirm_async (GcrSystemPrompt *self,
+                                 GCancellable *cancellable,
+                                 GAsyncReadyCallback callback,
+                                 gpointer user_data)
+{
+	GSimpleAsyncResult *res;
+
+	g_return_if_fail (GCR_IS_SYSTEM_PROMPT (self));
+	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+	res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
+	                                 gcr_system_prompt_confirm_async);
+
+	g_dbus_proxy_call (G_DBUS_PROXY (self->pv->prompt_proxy),
+	                   GCR_DBUS_PROMPT_METHOD_CONFIRM,
+	                   parameters_for_confirm (self),
+	                   G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, cancellable,
+	                   on_prompt_requested_confirm, g_object_ref (res));
+
+	g_object_unref (res);
+}
+
+gboolean
+gcr_system_prompt_confirm_finish (GcrSystemPrompt *self,
+                                  GAsyncResult *result,
+                                  GError **error)
+{
+	GSimpleAsyncResult *res;
+	gboolean ret = FALSE;
+
+	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPT (self), FALSE);
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
+	                      gcr_system_prompt_confirm_async), FALSE);
+
+	res = G_SIMPLE_ASYNC_RESULT (result);
+	if (g_simple_async_result_propagate_error (res, error))
+		return FALSE;
+
+	g_variant_get (g_simple_async_result_get_op_res_gpointer (res),
+	               "(b)", &ret);
+	return ret;
+}
+
+/**
+ * gcr_system_prompt_confirm:
+ * @self: a prompt
+ * @cancellable: optional cancellation object
+ * @error: location to place error on failure
+ *
+ * Prompts for confirmation asking a cancel/continue style question.
+ * Set the various properties on the prompt to represent the question
+ * correctly.
+ *
+ * This method will block until the a response is returned from the prompter.
+ * and %TRUE or %FALSE will be returned based on the response. The return value
+ * will also be %FALSE if an error occurs. Check the @error argument to tell
+ * the difference.
+ *
+ * Returns: whether the prompt was confirmed or not
+ */
+gboolean
+gcr_system_prompt_confirm (GcrSystemPrompt *self,
+                           GCancellable *cancellable,
+                           GError **error)
+{
+	GVariant *retval;
+	gboolean ret = FALSE;
+
+	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPT (self), FALSE);
+	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
+	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+	if (!self->pv->prompt_proxy) {
+		g_warning ("GcrSystemPrompt was not successfully opened");
+		return FALSE;
+	}
+
+	retval = g_dbus_proxy_call_sync (self->pv->prompt_proxy,
+	                                 GCR_DBUS_PROMPT_METHOD_CONFIRM,
+	                                 parameters_for_confirm (self),
+	                                 G_DBUS_CALL_FLAGS_NO_AUTO_START, -1,
+	                                 cancellable, error);
+
+	if (retval != NULL) {
+		g_variant_get (retval, "(b)", &ret);
+		g_variant_unref (retval);
+	}
+
+	return ret;
+}
+
+/**
+ * gcr_system_prompt_open_async:
+ * @timeout_seconds: the number of seconds to wait to access the prompt, or -1
+ * @cancellable: optional cancellation object
+ * @callback: called when the operation completes
+ * @user_data: data to pass the callback
+ *
+ * Asynchronously open a system prompt with the default system prompter.
+ *
+ * Most system prompters only allow showing one prompt at a time, and if
+ * another prompt is shown then this method will block for up to
+ * @timeout_seconds seconds. If @timeout_seconds is equal to -1, then this
+ * will block indefinitely until the prompt can be opened. If @timeout_seconds
+ * expires, then this operation will fail with a %GCR_SYSTEM_PROMPT_IN_PROGRESS
+ * error.
+ */
+void
+gcr_system_prompt_open_async (gint timeout_seconds,
+                              GCancellable *cancellable,
+                              GAsyncReadyCallback callback,
+                              gpointer user_data)
+{
+	g_return_if_fail (timeout_seconds >= -1);
+	g_return_if_fail (cancellable == NULL || G_CANCELLABLE (cancellable));
+
+	gcr_system_prompt_open_for_prompter_async (NULL, timeout_seconds,
+	                                           cancellable, callback,
+	                                           user_data);
+}
+
+/**
+ * gcr_system_prompt_open_for_prompter_async:
+ * @prompter_name: (allow-none): the prompter dbus name
+ * @timeout_seconds: the number of seconds to wait to access the prompt, or -1
+ * @cancellable: optional cancellation object
+ * @callback: called when the operation completes
+ * @user_data: data to pass the callback
+ *
+ * Opens a system prompt asynchronously. If prompter_name is %NULL, then the
+ * default system prompter is used.
+ *
+ * Most system prompters only allow showing one prompt at a time, and if
+ * another prompt is shown then this method will block for up to
+ * @timeout_seconds seconds. If @timeout_seconds is equal to -1, then this
+ * will block indefinitely until the prompt can be opened. If @timeout_seconds
+ * expires, then this operation will fail with a %GCR_SYSTEM_PROMPT_IN_PROGRESS
+ * error.
+ */
+void
+gcr_system_prompt_open_for_prompter_async (const gchar *prompter_name,
+                                           gint timeout_seconds,
+                                           GCancellable *cancellable,
+                                           GAsyncReadyCallback callback,
+                                           gpointer user_data)
+{
+	g_return_if_fail (timeout_seconds >= -1);
+	g_return_if_fail (cancellable == NULL || G_CANCELLABLE (cancellable));
+
+	g_async_initable_new_async (GCR_TYPE_SYSTEM_PROMPT,
+	                            G_PRIORITY_DEFAULT,
+	                            cancellable,
+	                            callback,
+	                            user_data,
+	                            "timeout-seconds", timeout_seconds,
+	                            "bus-name", prompter_name,
+	                            NULL);
+}
+
+/**
+ * gcr_system_prompt_open_finish:
+ * @result: the asynchronous result
+ * @error: location to place an error on failure
+ *
+ * Complete an operation to asynchronously open a system prompt.
+ *
+ * Returns: (transfer full): the prompt, or %NULL if prompt could not
+ *          be opened
+ */
+GcrSystemPrompt *
+gcr_system_prompt_open_finish (GAsyncResult *result,
+                               GError **error)
+{
+	GObject *object;
+	GObject *source_object;
+
+	g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+	source_object = g_async_result_get_source_object (result);
+	g_assert (source_object != NULL);
+
+	object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
+	                                      result, error);
+	g_object_unref (source_object);
+
+	if (object != NULL)
+		return GCR_SYSTEM_PROMPT (object);
+	else
+		return NULL;
+}
+
+/**
+ * gcr_system_prompt_open:
+ * @timeout_seconds: the number of seconds to wait to access the prompt, or -1
+ * @cancellable: optional cancellation object
+ * @error: location to place error on failure
+ *
+ * Opens a system prompt with the default prompter.
+ *
+ * Most system prompters only allow showing one prompt at a time, and if
+ * another prompt is shown then this method will block for up to
+ * @timeout_seconds seconds. If @timeout_seconds is equal to -1, then this
+ * will block indefinitely until the prompt can be opened. If @timeout_seconds
+ * expires, then this function will fail with a %GCR_SYSTEM_PROMPT_IN_PROGRESS
+ * error.
+ *
+ * Returns: (transfer full): the prompt, or %NULL if prompt could not
+ *          be opened
+ */
+GcrSystemPrompt *
+gcr_system_prompt_open (gint timeout_seconds,
+                        GCancellable *cancellable,
+                        GError **error)
+{
+	g_return_val_if_fail (timeout_seconds >= -1, NULL);
+	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
+	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+	return gcr_system_prompt_open_for_prompter (NULL, timeout_seconds,
+	                                            cancellable, error);
+}
+
+/**
+ * gcr_system_prompt_open_for_prompter:
+ * @prompter_name: (allow-none): the prompter dbus name
+ * @timeout_seconds: the number of seconds to wait to access the prompt, or -1
+ * @cancellable: optional cancellation object
+ * @error: location to place error on failure
+ *
+ * Opens a system prompt. If prompter_name is %NULL, then the default
+ * system prompter is used.
+ *
+ * Most system prompters only allow showing one prompt at a time, and if
+ * another prompt is shown then this method will block for up to
+ * @timeout_seconds seconds. If @timeout_seconds is equal to -1, then this
+ * will block indefinitely until the prompt can be opened. If @timeout_seconds
+ * expires, then this function will fail with a %GCR_SYSTEM_PROMPT_IN_PROGRESS
+ * error.
+ *
+ * Returns: (transfer full): the prompt, or %NULL if prompt could not
+ *          be opened
+ */
+GcrSystemPrompt *
+gcr_system_prompt_open_for_prompter (const gchar *prompter_name,
+                                     gint timeout_seconds,
+                                     GCancellable *cancellable,
+                                     GError **error)
+{
+	g_return_val_if_fail (timeout_seconds >= -1, NULL);
+	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
+	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+	return g_initable_new (GCR_TYPE_SYSTEM_PROMPT, cancellable, error,
+	                       "timeout-seconds", timeout_seconds,
+	                       "bus-name", prompter_name,
+	                       NULL);
+}
+
+/**
+ * gcr_system_prompt_close:
+ * @self: the prompt
+ *
+ * Close this prompt. After closing the prompt, no further methods may be
+ * called on this object. The prompt object is not unreferenced by this
+ * function, and you must unreference it once done.
+ */
+void
+gcr_system_prompt_close (GcrSystemPrompt *self)
+{
+	g_return_if_fail (GCR_IS_SYSTEM_PROMPT (self));
+
+	g_object_run_dispose (G_OBJECT (self));
+}
+
+static const GDBusErrorEntry SYSTEM_PROMPT_ERRORS[] = {
+	{ GCR_SYSTEM_PROMPT_IN_PROGRESS, GCR_DBUS_PROMPT_ERROR_IN_PROGRESS },
+	{ GCR_SYSTEM_PROMPT_NOT_HAPPENING, GCR_DBUS_PROMPT_ERROR_NOT_HAPPENING },
+	{ GCR_SYSTEM_PROMPT_FAILED, GCR_DBUS_PROMPT_ERROR_FAILED },
+};
+
+GQuark
+gcr_system_prompt_error_get_domain (void)
+{
+	static volatile gsize quark_volatile = 0;
+	g_dbus_error_register_error_domain ("gcr-system-prompt-error-domain",
+	                                    &quark_volatile,
+	                                    SYSTEM_PROMPT_ERRORS,
+	                                    G_N_ELEMENTS (SYSTEM_PROMPT_ERRORS));
+	G_STATIC_ASSERT (G_N_ELEMENTS (SYSTEM_PROMPT_ERRORS) == GCR_SYSTEM_PROMPT_FAILED);
+	return (GQuark) quark_volatile;
+}
diff --git a/gcr/gcr-system-prompt.h b/gcr/gcr-system-prompt.h
new file mode 100644
index 0000000..19ffa1d
--- /dev/null
+++ b/gcr/gcr-system-prompt.h
@@ -0,0 +1,174 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2011 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.
+ *
+ * Author: Stef Walter <stef thewalter net>
+ */
+
+#if !defined (__GCR_INSIDE_HEADER__) && !defined (GCR_COMPILATION)
+#error "Only <gcr/gcr.h> or <gcr/gcr-base.h> can be included directly."
+#endif
+
+#ifndef __GCR_SYSTEM_PROMPT_H__
+#define __GCR_SYSTEM_PROMPT_H__
+
+#include "gcr-types.h"
+#include "gcr-secret-exchange.h"
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+
+typedef enum {
+	GCR_SYSTEM_PROMPT_IN_PROGRESS = 1,
+	GCR_SYSTEM_PROMPT_NOT_HAPPENING,
+	GCR_SYSTEM_PROMPT_FAILED
+} GcrSystemPromptError;
+
+#define GCR_SYSTEM_PROMPT_ERROR              (gcr_system_prompt_error_get_domain ())
+
+GQuark           gcr_system_prompt_error_get_domain     (void) G_GNUC_CONST;
+
+#define GCR_TYPE_SYSTEM_PROMPT               (gcr_system_prompt_get_type ())
+#define GCR_SYSTEM_PROMPT(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_SYSTEM_PROMPT, GcrSystemPrompt))
+#define GCR_SYSTEM_PROMPT_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_SYSTEM_PROMPT, GcrSystemPromptClass))
+#define GCR_IS_SYSTEM_PROMPT(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_SYSTEM_PROMPT))
+#define GCR_IS_SYSTEM_PROMPT_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_SYSTEM_PROMPT))
+#define GCR_SYSTEM_PROMPT_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_SYSTEM_PROMPT, GcrSystemPromptClass))
+
+typedef struct _GcrSystemPrompt GcrSystemPrompt;
+typedef struct _GcrSystemPromptClass GcrSystemPromptClass;
+typedef struct _GcrSystemPromptPrivate GcrSystemPromptPrivate;
+
+struct _GcrSystemPrompt {
+	GObject parent;
+	GcrSystemPromptPrivate *pv;
+};
+
+struct _GcrSystemPromptClass {
+	GObjectClass parent_class;
+
+	void   (*prompt_ready)   ();
+};
+
+GType                gcr_system_prompt_get_type                  (void);
+
+GcrSecretExchange *  gcr_system_prompt_get_secret_exchange       (GcrSystemPrompt *self);
+
+void                 gcr_system_prompt_set_secret_exchange       (GcrSystemPrompt *self,
+                                                                  GcrSecretExchange *exchange);
+
+const gchar *        gcr_system_prompt_get_title                 (GcrSystemPrompt *self);
+
+void                 gcr_system_prompt_set_title                 (GcrSystemPrompt *self,
+                                                                  const gchar *title);
+
+const gchar *        gcr_system_prompt_get_message               (GcrSystemPrompt *self);
+
+void                 gcr_system_prompt_set_message               (GcrSystemPrompt *self,
+                                                                  const gchar *message);
+
+const gchar *        gcr_system_prompt_get_description           (GcrSystemPrompt *self);
+
+void                 gcr_system_prompt_set_description           (GcrSystemPrompt *self,
+                                                                  const gchar *description);
+
+const gchar *        gcr_system_prompt_get_warning               (GcrSystemPrompt *self);
+
+void                 gcr_system_prompt_set_warning               (GcrSystemPrompt *self,
+                                                                  const gchar *warning);
+
+const gchar *        gcr_system_prompt_get_choice_label          (GcrSystemPrompt *self);
+
+void                 gcr_system_prompt_set_choice_label          (GcrSystemPrompt *self,
+                                                                  const gchar *warning);
+
+gboolean             gcr_system_prompt_get_choice_chosen         (GcrSystemPrompt *self);
+
+void                 gcr_system_prompt_set_choice_chosen         (GcrSystemPrompt *self,
+                                                                  gboolean chosen);
+
+gboolean             gcr_system_prompt_get_password_new          (GcrSystemPrompt *self);
+
+void                 gcr_system_prompt_set_password_new          (GcrSystemPrompt *self,
+                                                                  gboolean new_password);
+
+gint                 gcr_system_prompt_get_password_strength     (GcrSystemPrompt *self);
+
+const gchar *        gcr_system_prompt_get_caller_window         (GcrSystemPrompt *self);
+
+void                 gcr_system_prompt_set_caller_window         (GcrSystemPrompt *self,
+                                                                  const gchar *window_id);
+
+void                 gcr_system_prompt_open_async                (gint timeout_seconds,
+                                                                  GCancellable *cancellable,
+                                                                  GAsyncReadyCallback callback,
+                                                                  gpointer user_data);
+
+void                 gcr_system_prompt_open_for_prompter_async   (const gchar *prompter_name,
+                                                                  gint timeout_seconds,
+                                                                  GCancellable *cancellable,
+                                                                  GAsyncReadyCallback callback,
+                                                                  gpointer user_data);
+
+
+GcrSystemPrompt *    gcr_system_prompt_open_finish               (GAsyncResult *result,
+                                                                  GError **error);
+
+GcrSystemPrompt *    gcr_system_prompt_open_for_prompter         (const gchar *prompter_name,
+                                                                  gint timeout_seconds,
+                                                                  GCancellable *cancellable,
+                                                                  GError **error);
+
+GcrSystemPrompt *    gcr_system_prompt_open                      (gint timeout_seconds,
+                                                                  GCancellable *cancellable,
+                                                                  GError **error);
+
+void                 gcr_system_prompt_password_async            (GcrSystemPrompt *self,
+                                                                  GCancellable *cancellable,
+                                                                  GAsyncReadyCallback callback,
+                                                                  gpointer user_data);
+
+const gchar *        gcr_system_prompt_password_finish           (GcrSystemPrompt *self,
+                                                                  GAsyncResult *result,
+                                                                  GError **error);
+
+const gchar *        gcr_system_prompt_password                  (GcrSystemPrompt *self,
+                                                                  GCancellable *cancellable,
+                                                                  GError **error);
+
+void                 gcr_system_prompt_confirm_async             (GcrSystemPrompt *self,
+                                                                  GCancellable *cancellable,
+                                                                  GAsyncReadyCallback callback,
+                                                                  gpointer user_data);
+
+gboolean             gcr_system_prompt_confirm_finish            (GcrSystemPrompt *self,
+                                                                  GAsyncResult *result,
+                                                                  GError **error);
+
+gboolean             gcr_system_prompt_confirm                   (GcrSystemPrompt *self,
+                                                                  GCancellable *cancellable,
+                                                                  GError **error);
+
+void                 gcr_system_prompt_close                     (GcrSystemPrompt *self);
+
+G_END_DECLS
+
+#endif /* __GCR_SYSTEM_PROMPT_H__ */
diff --git a/gcr/gcr-system-prompter.c b/gcr/gcr-system-prompter.c
new file mode 100644
index 0000000..e95e61d
--- /dev/null
+++ b/gcr/gcr-system-prompter.c
@@ -0,0 +1,1162 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2011 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.
+ *
+ * Author: Stef Walter <stef thewalter net>
+ */
+
+#include "config.h"
+
+#include "gcr-dbus-constants.h"
+#include "gcr-dbus-generated.h"
+#include "gcr-internal.h"
+#include "gcr-library.h"
+#include "gcr-secret-exchange.h"
+#include "gcr-system-prompter.h"
+#include "gcr-system-prompt.h"
+
+#include "egg/egg-error.h"
+
+#include <string.h>
+
+/**
+ * SECTION:gcr-system-prompter
+ * @title: GcrSystemPrompter
+ * @short_description: XXX
+ *
+ * XXXX
+ */
+
+/**
+ * GcrSystemPrompter:
+ *
+ * XXX
+ */
+
+/**
+ * GcrSystemPrompterClass:
+ *
+ * The class for #GcrSystemPrompter.
+ */
+
+enum {
+	PROP_0,
+	PROP_TITLE,
+	PROP_MESSAGE,
+	PROP_DESCRIPTION,
+	PROP_WARNING,
+	PROP_PASSWORD_NEW,
+	PROP_PASSWORD_STRENGTH,
+	PROP_CHOICE_LABEL,
+	PROP_CHOICE_CHOSEN,
+	PROP_CALLER_WINDOW,
+};
+
+enum {
+	SHOW_PROMPT,
+	PROMPT_PASSWORD,
+	PROMPT_CONFIRM,
+	RESPONDED,
+	HIDE_PROMPT,
+	LAST_SIGNAL
+};
+
+struct _GcrSystemPrompterPrivate {
+	guint prompter_registered;
+	GDBusConnection *connection;
+
+	/* Prompt state */
+	gchar *prompt_path;
+	guint prompt_registered;
+	gchar *owner_name;
+	guint owner_watching_id;
+	GcrSecretExchange *exchange;
+	GDBusMethodInvocation *invocation;
+	gboolean shown;
+
+	/* Properties */
+	GHashTable *properties;
+	GQueue *properties_changed;
+	guint changed_source;
+};
+
+static gint signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (GcrSystemPrompter, gcr_system_prompter, G_TYPE_OBJECT);
+
+static void
+dispatch_changed_properties (GcrSystemPrompter *self)
+{
+	const gchar *property_name;
+	GVariantBuilder builder;
+	GVariantBuilder invalidated;
+	GVariant *value;
+	GError *error = NULL;
+
+	if (self->pv->changed_source) {
+		g_source_remove (self->pv->changed_source);
+		self->pv->changed_source = 0;
+	}
+
+	g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+	g_variant_builder_init (&invalidated, G_VARIANT_TYPE ("as"));
+
+	while ((property_name = g_queue_pop_head (self->pv->properties_changed))) {
+		value = g_hash_table_lookup (self->pv->properties, property_name);
+		g_variant_builder_add (&builder, "{sv}", property_name, value);
+	}
+
+	g_dbus_connection_emit_signal (self->pv->connection,
+	                               self->pv->owner_name,
+	                               self->pv->prompt_path,
+	                               "org.freedesktop.DBus.Properties",
+	                               "PropertiesChanged",
+	                               g_variant_new ("(sa{sv}as)", GCR_DBUS_PROMPT_INTERFACE,
+	                                              &builder, &invalidated),
+	                               &error);
+
+	if (error != NULL) {
+		g_warning ("couldn't emit properties changed signal: %s", egg_error_message (error));
+		g_clear_error (&error);
+	}
+}
+
+static gboolean
+on_idle_dispatch_changed (gpointer data)
+{
+	GcrSystemPrompter *self = GCR_SYSTEM_PROMPTER (data);
+
+	self->pv->changed_source = 0;
+	dispatch_changed_properties (self);
+
+	return FALSE; /* Don't run again */
+}
+
+static void
+prompt_emit_changed (GcrSystemPrompter *self,
+                     const gchar *dbus_property)
+{
+	const gchar *object_property;
+
+	g_assert (dbus_property);
+
+	g_queue_push_tail (self->pv->properties_changed,
+	                   (gpointer)g_intern_string (dbus_property));
+
+	if (!self->pv->changed_source)
+		self->pv->changed_source = g_idle_add (on_idle_dispatch_changed, self);
+
+	if (g_str_equal ("Title", dbus_property))
+		object_property = "title";
+	else if (g_str_equal ("Message", dbus_property))
+		object_property = "message";
+	else if (g_str_equal ("Warning", dbus_property))
+		object_property = "warning";
+	else if (g_str_equal ("Description", dbus_property))
+		object_property = "description";
+	else if (g_str_equal ("PasswordNew", dbus_property))
+		object_property = "password-new";
+	else if (g_str_equal ("PasswordStrength", dbus_property))
+		object_property = "password-strength";
+	else if (g_str_equal ("ChoiceLabel", dbus_property))
+		object_property = "choice-label";
+	else if (g_str_equal ("ChoiceChosen", dbus_property))
+		object_property = "choice-chosen";
+	else if (g_str_equal ("CallerWindow", dbus_property))
+		object_property = "caller-window";
+	else
+		g_assert_not_reached ();
+
+	g_object_notify (G_OBJECT (self), object_property);
+}
+
+static GVariant *
+prompt_get_property (GDBusConnection *connection,
+                     const gchar *sender,
+                     const gchar *object_path,
+                     const gchar *interface_name,
+                     const gchar *property_name,
+                     GError **error,
+                     gpointer user_data)
+{
+	GcrSystemPrompter *self = GCR_SYSTEM_PROMPTER (user_data);
+	GVariant *ret;
+
+	g_return_val_if_fail (property_name != NULL, NULL);
+
+	if (g_strcmp0 (self->pv->owner_name, sender) != 0) {
+		g_error_new_literal (G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
+		                     "This prompt is owned by another process.");
+		return NULL;
+	}
+
+	ret = g_hash_table_lookup (self->pv->properties, property_name);
+	g_return_val_if_fail (ret != NULL, NULL);
+
+	return g_variant_ref (ret);
+}
+
+static gboolean
+prompt_set_property (GDBusConnection *connection,
+                     const gchar *sender,
+                     const gchar *object_path,
+                     const gchar *interface_name,
+                     const gchar *property_name,
+                     GVariant *value,
+                     GError **error,
+                     gpointer user_data)
+{
+	GcrSystemPrompter *self = GCR_SYSTEM_PROMPTER (user_data);
+
+	g_return_val_if_fail (property_name != NULL, FALSE);
+
+	if (g_strcmp0 (self->pv->owner_name, sender) != 0) {
+		g_error_new_literal (G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
+		                     "This prompt is owned by another process.");
+		return FALSE;
+	}
+
+	if (!g_hash_table_lookup (self->pv->properties, property_name))
+		g_return_val_if_reached (FALSE);
+
+	g_hash_table_insert (self->pv->properties,
+	                     (gpointer)g_intern_string (property_name),
+	                     g_variant_ref (value));
+
+	prompt_emit_changed (self, property_name);
+	return TRUE;
+}
+
+static void
+prompt_update_properties (GcrSystemPrompter *self,
+                          GVariant *properties)
+{
+	const gchar *prefix = GCR_DBUS_PROMPT_INTERFACE ".";
+	GObject *obj = G_OBJECT (self);
+	GVariantIter *iter;
+	GVariant *variant;
+	gchar *full_property_name;
+	const gchar *property_name;
+
+	g_object_freeze_notify (obj);
+
+	g_variant_get (properties, "a{sv}", &iter);
+	while (g_variant_iter_loop (iter, "{sv}", &full_property_name, &variant)) {
+		if (g_str_has_prefix (full_property_name, prefix)) {
+			property_name = full_property_name + strlen (prefix);
+			if (g_hash_table_lookup (self->pv->properties, property_name)) {
+				g_hash_table_insert (self->pv->properties,
+				                     (gpointer)g_intern_string (property_name),
+				                     g_variant_ref (variant));
+				prompt_emit_changed (self, property_name);
+			}
+		}
+	}
+
+	g_variant_iter_free (iter);
+
+	g_object_thaw_notify (obj);
+}
+
+static void
+prompt_clear_property (GcrSystemPrompter *self,
+                       const gchar *property_name,
+                       GVariant *value)
+{
+	g_hash_table_insert (self->pv->properties,
+	                     (gpointer)g_intern_string (property_name),
+	                     g_variant_ref (value));
+}
+
+static void
+prompt_clear_properties (GcrSystemPrompter *self)
+{
+	GVariant *variant;
+
+	if (self->pv->changed_source) {
+		g_source_remove (self->pv->changed_source);
+		self->pv->changed_source = 0;
+	}
+
+	variant = g_variant_ref_sink (g_variant_new_string (""));
+	prompt_clear_property (self, GCR_DBUS_PROMPT_PROPERTY_TITLE, variant);
+	prompt_clear_property (self, GCR_DBUS_PROMPT_PROPERTY_MESSAGE, variant);
+	prompt_clear_property (self, GCR_DBUS_PROMPT_PROPERTY_WARNING, variant);
+	prompt_clear_property (self, GCR_DBUS_PROMPT_PROPERTY_DESCRIPTION, variant);
+	prompt_clear_property (self, GCR_DBUS_PROMPT_PROPERTY_CHOICE_LABEL, variant);
+	prompt_clear_property (self, GCR_DBUS_PROMPT_PROPERTY_CALLER_WINDOW, variant);
+	g_variant_unref (variant);
+
+	variant = g_variant_ref_sink (g_variant_new_boolean (FALSE));
+	prompt_clear_property (self, GCR_DBUS_PROMPT_PROPERTY_PASSWORD_NEW, variant);
+	prompt_clear_property (self, GCR_DBUS_PROMPT_PROPERTY_CHOICE_CHOSEN, variant);
+	g_variant_unref (variant);
+
+	variant = g_variant_ref_sink (g_variant_new_int32 (0));
+	prompt_clear_property (self, GCR_DBUS_PROMPT_PROPERTY_PASSWORD_STRENGTH, variant);
+	g_variant_unref (variant);
+
+	g_queue_clear (self->pv->properties_changed);
+}
+
+static void
+prompt_method_request_password (GcrSystemPrompter *self,
+                                GDBusMethodInvocation *invocation,
+                                GVariant *properties,
+                                const gchar *exchange)
+{
+	gboolean result;
+
+	if (self->pv->invocation != NULL) {
+		g_dbus_method_invocation_return_error_literal (invocation, GCR_SYSTEM_PROMPT_ERROR,
+		                                               GCR_SYSTEM_PROMPT_IN_PROGRESS,
+		                                               "The prompt is already requesting.");
+		return;
+	}
+
+	if (!gcr_secret_exchange_receive (self->pv->exchange, exchange)) {
+		g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR,
+		                                               G_DBUS_ERROR_INVALID_ARGS,
+		                                               "The exchange argument was invalid.");
+		return;
+	}
+
+	prompt_update_properties (self, properties);
+
+	if (!self->pv->shown) {
+		self->pv->shown = TRUE;
+		g_signal_emit (self, signals[SHOW_PROMPT], 0);
+	}
+
+	self->pv->invocation = invocation;
+	g_signal_emit (self, signals[PROMPT_PASSWORD], 0, &result);
+
+	if (!result) {
+		g_return_if_fail (self->pv->invocation == invocation);
+		self->pv->invocation = NULL;
+		g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR,
+		                                               G_DBUS_ERROR_NOT_SUPPORTED,
+		                                               "Prompting for confirmation not supported.");
+	}
+}
+
+static void
+prompt_method_request_confirm (GcrSystemPrompter *self,
+                               GDBusMethodInvocation *invocation,
+                               GVariant *properties)
+{
+	gboolean result;
+
+	if (self->pv->invocation != NULL) {
+		g_dbus_method_invocation_return_error_literal (invocation, GCR_SYSTEM_PROMPT_ERROR,
+		                                               GCR_SYSTEM_PROMPT_IN_PROGRESS,
+		                                               "The prompt is already requesting.");
+		return;
+	}
+
+	prompt_update_properties (self, properties);
+
+	if (!self->pv->shown) {
+		self->pv->shown = TRUE;
+		g_signal_emit (self, signals[SHOW_PROMPT], 0);
+	}
+
+	self->pv->invocation = invocation;
+	g_signal_emit (self, signals[PROMPT_CONFIRM], 0, &result);
+
+	if (!result) {
+		g_return_if_fail (self->pv->invocation == invocation);
+		self->pv->invocation = NULL;
+		g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR,
+		                                               G_DBUS_ERROR_NOT_SUPPORTED,
+		                                               "Prompting for confirmation not supported.");
+	}
+}
+
+static void
+prompt_method_call (GDBusConnection *connection,
+		    const gchar *sender,
+		    const gchar *object_path,
+		    const gchar *interface_name,
+		    const gchar *method_name,
+		    GVariant *parameters,
+		    GDBusMethodInvocation *invocation,
+		    gpointer user_data)
+{
+	GcrSystemPrompter *self = GCR_SYSTEM_PROMPTER (user_data);
+	GVariant *dict = NULL;
+	gchar *string = NULL;;
+
+	g_return_if_fail (method_name != NULL);
+
+	if (g_strcmp0 (self->pv->owner_name, sender) != 0) {
+		g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR,
+		                                               G_DBUS_ERROR_ACCESS_DENIED,
+		                                               "This prompt is owned by another process.");
+
+	} else if (g_str_equal (method_name, "RequestPassword")) {
+		g_variant_get (parameters, "(@a{sv}s)", &dict, &string);
+		prompt_method_request_password (self, invocation, dict, string);
+
+	} else if (g_str_equal (method_name, "RequestConfirm")) {
+		g_variant_get (parameters, "(@a{sv})", &dict);
+		prompt_method_request_confirm (self, invocation, dict);
+
+	} else {
+		g_return_if_reached ();
+	}
+
+	if (dict)
+		g_variant_unref (dict);
+	g_free (string);
+}
+
+static GDBusInterfaceVTable prompt_dbus_vtable = {
+	prompt_method_call,
+	prompt_get_property,
+	prompt_set_property,
+};
+
+static const gchar *
+begin_prompting (GcrSystemPrompter *self,
+                 GDBusConnection *connection)
+{
+	GError *error = NULL;
+
+	g_assert (self->pv->prompt_path == NULL);
+
+	self->pv->prompt_path = g_strdup (GCR_DBUS_PROMPTER_OBJECT_PATH "/prompt0");
+	self->pv->prompt_registered = g_dbus_connection_register_object (connection, self->pv->prompt_path,
+	                                                                 _gcr_prompter_prompt_interface_info (),
+	                                                                 &prompt_dbus_vtable, self, NULL, &error);
+
+	if (error != NULL) {
+		g_warning ("couldn't register prompt object: %s", error->message);
+		g_clear_object (&error);
+	}
+
+	/* Setup the secret exchange */
+	g_assert (self->pv->exchange == NULL);
+	self->pv->exchange = gcr_secret_exchange_new (NULL);
+
+	return self->pv->prompt_path;
+}
+
+static void
+finish_prompting (GcrSystemPrompter *self)
+{
+	g_assert (self->pv->invocation == NULL);
+
+	prompt_clear_properties (self);
+
+	/* Tell the implementation to hide the display */
+	if (self->pv->shown) {
+		self->pv->shown = FALSE;
+		g_signal_emit (self, signals[HIDE_PROMPT], 0);
+	}
+
+	if (self->pv->owner_watching_id) {
+		g_bus_unwatch_name (self->pv->owner_watching_id);
+		self->pv->owner_watching_id = 0;
+	}
+
+	g_free (self->pv->owner_name);
+	self->pv->owner_name = NULL;
+
+	if (self->pv->prompt_registered) {
+		if (!g_dbus_connection_unregister_object (self->pv->connection,
+		                                          self->pv->prompt_registered))
+			g_return_if_reached ();
+		self->pv->prompt_registered = 0;
+	}
+
+	g_free (self->pv->prompt_path);
+	self->pv->prompt_path = NULL;
+
+	g_clear_object (&self->pv->exchange);
+}
+
+
+static void
+gcr_system_prompter_init (GcrSystemPrompter *self)
+{
+	self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_SYSTEM_PROMPTER,
+	                                        GcrSystemPrompterPrivate);
+	self->pv->properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
+	                                              (GDestroyNotify)g_variant_unref);
+	self->pv->properties_changed = g_queue_new ();
+	prompt_clear_properties (self);
+}
+
+static void
+gcr_system_prompter_set_property (GObject *obj,
+                                  guint prop_id,
+                                  const GValue *value,
+                                  GParamSpec *pspec)
+{
+	GcrSystemPrompter *self = GCR_SYSTEM_PROMPTER (obj);
+
+	switch (prop_id) {
+	case PROP_CHOICE_CHOSEN:
+		gcr_system_prompter_set_choice_chosen (self, g_value_get_boolean (value));
+		break;
+	case PROP_PASSWORD_STRENGTH:
+		gcr_system_prompter_set_password_strength (self, g_value_get_int (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gcr_system_prompter_get_property (GObject *obj,
+                                  guint prop_id,
+                                  GValue *value,
+                                  GParamSpec *pspec)
+{
+	GcrSystemPrompter *self = GCR_SYSTEM_PROMPTER (obj);
+
+	switch (prop_id) {
+	case PROP_TITLE:
+		g_value_set_string (value, gcr_system_prompter_get_title (self));
+		break;
+	case PROP_MESSAGE:
+		g_value_set_string (value, gcr_system_prompter_get_message (self));
+		break;
+	case PROP_DESCRIPTION:
+		g_value_set_string (value, gcr_system_prompter_get_description (self));
+		break;
+	case PROP_WARNING:
+		g_value_set_string (value, gcr_system_prompter_get_warning (self));
+		break;
+	case PROP_PASSWORD_NEW:
+		g_value_set_boolean (value, gcr_system_prompter_get_password_new (self));
+		break;
+	case PROP_PASSWORD_STRENGTH:
+		g_value_set_int (value, gcr_system_prompter_get_password_strength (self));
+		break;
+	case PROP_CHOICE_LABEL:
+		g_value_set_string (value, gcr_system_prompter_get_choice_label (self));
+		break;
+	case PROP_CHOICE_CHOSEN:
+		g_value_set_boolean (value, gcr_system_prompter_get_choice_chosen (self));
+		break;
+	case PROP_CALLER_WINDOW:
+		g_value_set_string (value, gcr_system_prompter_get_caller_window (self));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gcr_system_prompter_dispose (GObject *obj)
+{
+	GcrSystemPrompter *self = GCR_SYSTEM_PROMPTER (obj);
+
+	if (self->pv->invocation)
+		gcr_system_prompter_respond_cancelled (self);
+
+	finish_prompting (self);
+
+	if (self->pv->prompter_registered)
+		gcr_system_prompter_unregister (self, self->pv->connection);
+
+	G_OBJECT_CLASS (gcr_system_prompter_parent_class)->dispose (obj);
+}
+
+static void
+gcr_system_prompter_finalize (GObject *obj)
+{
+	GcrSystemPrompter *self = GCR_SYSTEM_PROMPTER (obj);
+
+	g_hash_table_destroy (self->pv->properties);
+	g_queue_free (self->pv->properties_changed);
+
+	G_OBJECT_CLASS (gcr_system_prompter_parent_class)->finalize (obj);
+}
+
+static void
+gcr_system_prompter_real_show_prompt (GcrSystemPrompter *self)
+{
+
+}
+
+static gboolean
+gcr_system_prompter_real_prompt_confirm (GcrSystemPrompter *self)
+{
+	return FALSE;
+}
+
+static gboolean
+gcr_system_prompter_real_prompt_password (GcrSystemPrompter *self)
+{
+	return FALSE;
+}
+
+static void
+gcr_system_prompter_real_hide_prompt (GcrSystemPrompter *self)
+{
+
+}
+
+static void
+gcr_system_prompter_class_init (GcrSystemPrompterClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+	gobject_class->get_property = gcr_system_prompter_get_property;
+	gobject_class->set_property = gcr_system_prompter_set_property;
+	gobject_class->dispose = gcr_system_prompter_dispose;
+	gobject_class->finalize = gcr_system_prompter_finalize;
+
+	klass->show_prompt = gcr_system_prompter_real_show_prompt;
+	klass->prompt_password= gcr_system_prompter_real_prompt_password;
+	klass->prompt_confirm = gcr_system_prompter_real_prompt_confirm;
+	klass->hide_prompt = gcr_system_prompter_real_hide_prompt;
+
+	g_type_class_add_private (gobject_class, sizeof (GcrSystemPrompterPrivate));
+
+	g_object_class_install_property (gobject_class, PROP_TITLE,
+	            g_param_spec_string ("title", "Title", "Prompt title",
+	                                 "", G_PARAM_READABLE));
+
+	g_object_class_install_property (gobject_class, PROP_MESSAGE,
+	            g_param_spec_string ("message", "Message", "Prompt message",
+	                                 "", G_PARAM_READABLE));
+
+	g_object_class_install_property (gobject_class, PROP_DESCRIPTION,
+	            g_param_spec_string ("description", "Description", "Prompt description",
+	                                 "", G_PARAM_READABLE));
+
+	g_object_class_install_property (gobject_class, PROP_WARNING,
+	            g_param_spec_string ("warning", "Warning", "Prompt warning",
+	                                 "", G_PARAM_READABLE));
+
+	g_object_class_install_property (gobject_class, PROP_PASSWORD_NEW,
+	           g_param_spec_boolean ("password-new", "Password new", "Whether is a new password",
+	                                 FALSE, G_PARAM_READABLE));
+
+	g_object_class_install_property (gobject_class, PROP_PASSWORD_STRENGTH,
+	               g_param_spec_int ("password-strength", "Password strength", "Strength of password",
+	                                 0, G_MAXINT, 0, G_PARAM_READWRITE));
+
+	g_object_class_install_property (gobject_class, PROP_CHOICE_LABEL,
+	            g_param_spec_string ("choice-label", "Choice label", "Label for prompt choice",
+	                                 "", G_PARAM_READABLE));
+
+	g_object_class_install_property (gobject_class, PROP_CHOICE_CHOSEN,
+	           g_param_spec_boolean ("choice-chosen", "Choice chosen", "Whether choice is chosen",
+	                                 FALSE, G_PARAM_READWRITE));
+
+	g_object_class_install_property (gobject_class, PROP_CALLER_WINDOW,
+	            g_param_spec_string ("caller-window", "Caller window", "Window id of caller",
+	                                 "", G_PARAM_READABLE));
+
+	signals[SHOW_PROMPT] = g_signal_new ("show-prompt", GCR_TYPE_SYSTEM_PROMPTER, G_SIGNAL_RUN_LAST,
+	                                     G_STRUCT_OFFSET (GcrSystemPrompterClass, show_prompt),
+	                                     NULL, NULL, NULL, G_TYPE_NONE, 0);
+
+	signals[PROMPT_PASSWORD] = g_signal_new ("prompt-password", GCR_TYPE_SYSTEM_PROMPTER, G_SIGNAL_RUN_LAST,
+	                                         G_STRUCT_OFFSET (GcrSystemPrompterClass, prompt_password),
+	                                         g_signal_accumulator_true_handled, NULL, NULL,
+	                                         G_TYPE_BOOLEAN, 0);
+
+	signals[PROMPT_CONFIRM] = g_signal_new ("prompt-confirm", GCR_TYPE_SYSTEM_PROMPTER, G_SIGNAL_RUN_LAST,
+	                                        G_STRUCT_OFFSET (GcrSystemPrompterClass, prompt_confirm),
+	                                        g_signal_accumulator_true_handled, NULL, NULL,
+	                                        G_TYPE_BOOLEAN, 0);
+
+	signals[RESPONDED] = g_signal_new ("responded", GCR_TYPE_SYSTEM_PROMPTER, G_SIGNAL_RUN_LAST,
+	                                   G_STRUCT_OFFSET (GcrSystemPrompterClass, responded),
+	                                   NULL, NULL, NULL, G_TYPE_NONE, 0);
+
+	signals[HIDE_PROMPT] = g_signal_new ("hide-prompt", GCR_TYPE_SYSTEM_PROMPTER, G_SIGNAL_RUN_LAST,
+	                                     G_STRUCT_OFFSET (GcrSystemPrompterClass, hide_prompt),
+	                                     NULL, NULL, NULL, G_TYPE_NONE, 0);
+}
+
+static void
+on_owner_vanished (GDBusConnection *connection,
+                   const gchar *name,
+                   gpointer user_data)
+{
+	GcrSystemPrompter *self = GCR_SYSTEM_PROMPTER (user_data);
+
+	if (self->pv->invocation != NULL)
+		gcr_system_prompter_respond_cancelled (self);
+
+	finish_prompting (self);
+
+	/* Now let everyone else know, we're ready! */
+	_gcr_prompter_emit_prompter_ready (GCR_PROMPTER (self));
+}
+
+static GVariant *
+prompter_get_property (GDBusConnection *connection,
+                       const gchar *sender,
+                       const gchar *object_path,
+                       const gchar *interface_name,
+                       const gchar *property_name,
+                       GError **error,
+                       gpointer user_data)
+{
+	g_return_val_if_reached (NULL);
+}
+
+static gboolean
+prompter_set_property (GDBusConnection *connection,
+                       const gchar *sender,
+                       const gchar *object_path,
+                       const gchar *interface_name,
+                       const gchar *property_name,
+                       GVariant *value,
+                       GError **error,
+                       gpointer user_data)
+{
+	g_return_val_if_reached (FALSE);
+}
+
+static void
+prompter_method_begin_prompting (GcrSystemPrompter *self,
+                                 GDBusMethodInvocation *invocation)
+{
+	GDBusConnection *connection;
+	const gchar *path;
+
+	if (self->pv->owner_name != NULL) {
+		g_dbus_method_invocation_return_error_literal (invocation,
+		                                               GCR_SYSTEM_PROMPT_ERROR,
+		                                               GCR_SYSTEM_PROMPT_IN_PROGRESS,
+		                                               "There is already a prompt in progress");
+		return;
+	}
+
+	/* Setup the owner of the prompting */
+	self->pv->owner_name = g_strdup (g_dbus_method_invocation_get_sender (invocation));
+	g_return_if_fail (self->pv->owner_name != NULL);
+
+	connection = g_dbus_method_invocation_get_connection (invocation);
+	self->pv->owner_watching_id = g_bus_watch_name_on_connection (connection,
+	                                                              self->pv->owner_name,
+	                                                              G_BUS_NAME_WATCHER_FLAGS_NONE,
+	                                                              NULL, on_owner_vanished,
+	                                                              self, NULL);
+
+	/* And respond */
+	path = begin_prompting (self, connection);
+	g_dbus_method_invocation_return_value (invocation, g_variant_new ("(o)", path));
+}
+
+static void
+prompter_method_finish_prompting (GcrSystemPrompter *self,
+                                  GDBusMethodInvocation *invocation,
+                                  const gchar *prompt)
+{
+	if (g_strcmp0 (prompt, self->pv->prompt_path) != 0) {
+		g_dbus_method_invocation_return_error_literal (invocation,
+		                                               G_DBUS_ERROR,
+		                                               G_DBUS_ERROR_INVALID_ARGS,
+		                                               "The prompt argument is not valid");
+		return;
+	}
+
+	if (self->pv->owner_name == NULL) {
+		g_dbus_method_invocation_return_error_literal (invocation,
+		                                               GCR_SYSTEM_PROMPT_ERROR,
+		                                               GCR_SYSTEM_PROMPT_NOT_HAPPENING,
+		                                               "The prompt is not in progress");
+		return;
+	}
+
+	/* Close a prompt that's prompting */
+	if (self->pv->invocation != NULL)
+		gcr_system_prompter_respond_cancelled (self);
+
+	finish_prompting (self);
+
+	g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
+}
+
+static void
+prompter_method_call (GDBusConnection *connection,
+                      const gchar *sender,
+                      const gchar *object_path,
+                      const gchar *interface_name,
+                      const gchar *method_name,
+                      GVariant *parameters,
+                      GDBusMethodInvocation *invocation,
+                      gpointer user_data)
+{
+	GcrSystemPrompter *self = GCR_SYSTEM_PROMPTER (user_data);
+	gchar *path = NULL;
+
+	g_return_if_fail (method_name != NULL);
+
+	if (g_str_equal (method_name, GCR_DBUS_PROMPTER_METHOD_BEGIN)) {
+		prompter_method_begin_prompting (self, invocation);
+
+	} else if (g_str_equal (method_name, GCR_DBUS_PROMPTER_METHOD_FINISH)) {
+		g_variant_get (parameters, "(o)", &path);
+		prompter_method_finish_prompting (self, invocation, path);
+
+	} else {
+		g_return_if_reached ();
+	}
+
+	g_free (path);
+}
+
+static GDBusInterfaceVTable prompter_dbus_vtable = {
+	prompter_method_call,
+	prompter_get_property,
+	prompter_set_property,
+};
+
+void
+gcr_system_prompter_register (GcrSystemPrompter *self,
+                              GDBusConnection *connection)
+{
+	GError *error = NULL;
+
+	g_return_if_fail (GCR_IS_SYSTEM_PROMPTER (self));
+	g_return_if_fail (G_DBUS_CONNECTION (connection));
+	g_return_if_fail (self->pv->prompter_registered == 0);
+	g_return_if_fail (self->pv->connection == NULL);
+
+	self->pv->connection = connection;
+	g_object_add_weak_pointer (G_OBJECT (connection), (gpointer *)&self->pv->connection);
+
+	self->pv->prompter_registered = g_dbus_connection_register_object (connection,
+	                                                                   GCR_DBUS_PROMPTER_OBJECT_PATH,
+	                                                                   _gcr_prompter_interface_info (),
+	                                                                   &prompter_dbus_vtable,
+	                                                                   g_object_ref (self),
+	                                                                   g_object_unref,
+	                                                                   &error);
+	if (error != NULL) {
+		g_warning ("error registering prompter %s", egg_error_message (error));
+		g_clear_error (&error);
+	}
+}
+
+void
+gcr_system_prompter_unregister (GcrSystemPrompter *self,
+                                GDBusConnection *connection)
+{
+	g_return_if_fail (GCR_IS_SYSTEM_PROMPTER (self));
+	g_return_if_fail (G_DBUS_CONNECTION (connection));
+	g_return_if_fail (self->pv->prompter_registered != 0);
+	g_return_if_fail (self->pv->connection == connection);
+
+	if (self->pv->invocation)
+		gcr_system_prompter_respond_cancelled (self);
+
+	finish_prompting (self);
+
+	g_dbus_connection_unregister_object (connection, self->pv->prompter_registered);
+	self->pv->prompter_registered = 0;
+
+	g_object_remove_weak_pointer (G_OBJECT (connection), (gpointer *)&self->pv->connection);
+	self->pv->connection = NULL;
+}
+
+const gchar *
+gcr_system_prompter_get_title (GcrSystemPrompter *self)
+{
+	GVariant *variant;
+
+	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPTER (self), NULL);
+
+	variant = g_hash_table_lookup (self->pv->properties, GCR_DBUS_PROMPT_PROPERTY_TITLE);
+	g_return_val_if_fail (variant != NULL, NULL);
+	return g_variant_get_string (variant, NULL);
+}
+
+const gchar *
+gcr_system_prompter_get_message (GcrSystemPrompter *self)
+{
+	GVariant *variant;
+
+	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPTER (self), NULL);
+
+	variant = g_hash_table_lookup (self->pv->properties, GCR_DBUS_PROMPT_PROPERTY_MESSAGE);
+	g_return_val_if_fail (variant != NULL, NULL);
+	return g_variant_get_string (variant, NULL);
+}
+
+const gchar *
+gcr_system_prompter_get_description (GcrSystemPrompter *self)
+{
+	GVariant *variant;
+
+	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPTER (self), NULL);
+
+	variant = g_hash_table_lookup (self->pv->properties, GCR_DBUS_PROMPT_PROPERTY_DESCRIPTION);
+	g_return_val_if_fail (variant != NULL, NULL);
+	return g_variant_get_string (variant, NULL);
+}
+
+const gchar *
+gcr_system_prompter_get_warning (GcrSystemPrompter *self)
+{
+	GVariant *variant;
+
+	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPTER (self), NULL);
+
+	variant = g_hash_table_lookup (self->pv->properties, GCR_DBUS_PROMPT_PROPERTY_WARNING);
+	g_return_val_if_fail (variant != NULL, NULL);
+	return g_variant_get_string (variant, NULL);
+}
+
+void
+gcr_system_prompter_set_warning (GcrSystemPrompter *self,
+                                 const gchar *warning)
+{
+	GVariant *variant;
+
+	g_return_if_fail (GCR_IS_SYSTEM_PROMPTER (self));
+
+	if (warning == NULL)
+		warning = "";
+
+	variant = g_variant_new_string (warning ? warning : "");
+	g_hash_table_insert (self->pv->properties,
+	                     (gpointer)GCR_DBUS_PROMPT_PROPERTY_WARNING,
+	                     g_variant_ref_sink (variant));
+
+	prompt_emit_changed (self, GCR_DBUS_PROMPT_PROPERTY_WARNING);
+}
+
+gboolean
+gcr_system_prompter_get_password_new (GcrSystemPrompter *self)
+{
+	GVariant *variant;
+
+	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPTER (self), FALSE);
+
+	variant = g_hash_table_lookup (self->pv->properties, GCR_DBUS_PROMPT_PROPERTY_PASSWORD_NEW);
+	g_return_val_if_fail (variant != NULL, FALSE);
+	return g_variant_get_boolean (variant);
+}
+
+gint
+gcr_system_prompter_get_password_strength (GcrSystemPrompter *self)
+{
+	GVariant *variant;
+
+	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPTER (self), 0);
+
+	variant = g_hash_table_lookup (self->pv->properties, GCR_DBUS_PROMPT_PROPERTY_PASSWORD_STRENGTH);
+	g_return_val_if_fail (variant != NULL, 0);
+	return g_variant_get_int32 (variant);
+}
+
+void
+gcr_system_prompter_set_password_strength (GcrSystemPrompter *self,
+                                           gint strength)
+{
+	GVariant *variant;
+
+	g_return_if_fail (GCR_IS_SYSTEM_PROMPTER (self));
+
+	variant = g_variant_new_int32 (strength);
+	g_hash_table_insert (self->pv->properties,
+	                     (gpointer) GCR_DBUS_PROMPT_PROPERTY_PASSWORD_STRENGTH,
+	                     g_variant_ref_sink (variant));
+
+	prompt_emit_changed (self, GCR_DBUS_PROMPT_PROPERTY_PASSWORD_STRENGTH);
+}
+
+const gchar *
+gcr_system_prompter_get_choice_label (GcrSystemPrompter *self)
+{
+	GVariant *variant;
+
+	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPTER (self), NULL);
+
+	variant = g_hash_table_lookup (self->pv->properties, GCR_DBUS_PROMPT_PROPERTY_CHOICE_LABEL);
+	g_return_val_if_fail (variant != NULL, NULL);
+	return g_variant_get_string (variant, NULL);
+}
+
+/**
+ * gcr_system_prompter_get_choice_chosen:
+ * @self: a prompter
+ *
+ * Used by prompter implementations to check if the choice offered by the
+ * prompt should be checked or unchecked.
+ *
+ * Returns: whether the choice is chosen
+ */
+gboolean
+gcr_system_prompter_get_choice_chosen (GcrSystemPrompter *self)
+{
+	GVariant *variant;
+
+	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPTER (self), FALSE);
+
+	variant = g_hash_table_lookup (self->pv->properties, GCR_DBUS_PROMPT_PROPERTY_CHOICE_CHOSEN);
+	g_return_val_if_fail (variant != NULL, FALSE);
+	return g_variant_get_boolean (variant);
+}
+
+/**
+ * gcr_system_prompter_set_choice_chosen:
+ * @self: a prompter
+ * @chosen: the user's choice
+ *
+ * Used by prompter implementations to let the prompter know when the user
+ * has checked or unchecked the choice offered by the prompt.
+ */
+void
+gcr_system_prompter_set_choice_chosen (GcrSystemPrompter *self,
+                                       gboolean chosen)
+{
+	GVariant *variant;
+
+	g_return_if_fail (GCR_IS_SYSTEM_PROMPTER (self));
+
+	variant = g_variant_new_boolean (chosen);
+	g_hash_table_insert (self->pv->properties,
+	                     (gpointer)GCR_DBUS_PROMPT_PROPERTY_CHOICE_CHOSEN,
+	                     g_variant_ref_sink (variant));
+
+	prompt_emit_changed (self, GCR_DBUS_PROMPT_PROPERTY_CHOICE_CHOSEN);
+}
+
+const gchar *
+gcr_system_prompter_get_caller_window (GcrSystemPrompter *self)
+{
+	GVariant *variant;
+
+	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPTER (self), NULL);
+
+	variant = g_hash_table_lookup (self->pv->properties, GCR_DBUS_PROMPT_PROPERTY_CALLER_WINDOW);
+	g_return_val_if_fail (variant != NULL, NULL);
+	return g_variant_get_string (variant, NULL);
+}
+
+/**
+ * gcr_system_prompter_respond_cancelled:
+ * @self: a prompter
+ *
+ * Used by prompter implementations to let the prompter know when the user
+ * has cancelled the current prompt.
+ */
+void
+gcr_system_prompter_respond_cancelled (GcrSystemPrompter *self)
+{
+	GDBusMethodInvocation *invocation;
+	const gchar *method;
+
+	g_return_if_fail (GCR_IS_SYSTEM_PROMPTER (self));
+	g_return_if_fail (self->pv->invocation != NULL);
+
+	invocation = self->pv->invocation;
+	self->pv->invocation = NULL;
+
+	/* Send back all the properties before we respond */
+	dispatch_changed_properties (self);
+
+	method = g_dbus_method_invocation_get_method_name (invocation);
+	if (method && g_str_equal (method, GCR_DBUS_PROMPT_METHOD_PASSWORD))
+		g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", ""));
+
+	else if (method && g_str_equal (method, GCR_DBUS_PROMPT_METHOD_CONFIRM))
+		g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", FALSE));
+
+	else
+		g_return_if_reached ();
+
+	g_signal_emit (self, signals[RESPONDED], 0);
+}
+
+/**
+ * gcr_system_prompter_respond_confirmed:
+ * @self: a prompter
+ * @password: the password
+ *
+ * Used by prompter implementations to let the prompter know when the user
+ * has entered a password and clicked 'OK'.
+ */
+void
+gcr_system_prompter_respond_with_password (GcrSystemPrompter *self,
+                                           const gchar *password)
+{
+	GDBusMethodInvocation *invocation;
+	const gchar *method;
+	gchar *exchange;
+
+	g_return_if_fail (GCR_IS_SYSTEM_PROMPTER (self));
+	g_return_if_fail (password != NULL);
+	g_return_if_fail (self->pv->invocation != NULL);
+
+	invocation = self->pv->invocation;
+	self->pv->invocation = NULL;
+
+	/* Send back all the properties before we respond */
+	dispatch_changed_properties (self);
+
+	method = g_dbus_method_invocation_get_method_name (invocation);
+	g_return_if_fail (method != NULL && g_str_equal (method, GCR_DBUS_PROMPT_METHOD_PASSWORD));
+
+	exchange = gcr_secret_exchange_send (self->pv->exchange, password, -1);
+	g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", exchange));
+	g_free (exchange);
+
+	g_signal_emit (self, signals[RESPONDED], 0);
+}
+
+/**
+ * gcr_system_prompter_respond_confirmed:
+ * @self: a prompter
+ *
+ * Used by prompter implementations to let the prompter know when the user
+ * has confirmed the request, ie: clicked 'OK'.
+ */
+void
+gcr_system_prompter_respond_confirmed (GcrSystemPrompter *self)
+{
+	GDBusMethodInvocation *invocation;
+	const gchar *method;
+
+	g_return_if_fail (GCR_IS_SYSTEM_PROMPTER (self));
+	g_return_if_fail (self->pv->invocation != NULL);
+
+	invocation = self->pv->invocation;
+	self->pv->invocation = NULL;
+
+	/* Send back all the properties before we respond */
+	dispatch_changed_properties (self);
+
+	method = g_dbus_method_invocation_get_method_name (invocation);
+	g_return_if_fail (method != NULL && g_str_equal (method, GCR_DBUS_PROMPT_METHOD_CONFIRM));
+	g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", TRUE));
+
+	g_signal_emit (self, signals[RESPONDED], 0);
+}
+
+GcrSystemPrompter *
+gcr_system_prompter_new (void)
+{
+	return g_object_new (GCR_TYPE_SYSTEM_PROMPTER, NULL);
+}
diff --git a/gcr/gcr-system-prompter.h b/gcr/gcr-system-prompter.h
new file mode 100644
index 0000000..ad84ecb
--- /dev/null
+++ b/gcr/gcr-system-prompter.h
@@ -0,0 +1,113 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2011 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.
+ *
+ * Author: Stef Walter <stef thewalter net>
+ */
+
+#if !defined (__GCR_INSIDE_HEADER__) && !defined (GCR_COMPILATION)
+#error "Only <gcr/gcr.h> or <gcr/gcr-base.h> can be included directly."
+#endif
+
+#ifndef __GCR_SYSTEM_PROMPTER_H__
+#define __GCR_SYSTEM_PROMPTER_H__
+
+#include "gcr-types.h"
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GCR_TYPE_SYSTEM_PROMPTER               (gcr_system_prompter_get_type ())
+#define GCR_SYSTEM_PROMPTER(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_SYSTEM_PROMPTER, GcrSystemPrompter))
+#define GCR_SYSTEM_PROMPTER_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_SYSTEM_PROMPTER, GcrSystemPrompterClass))
+#define GCR_IS_SYSTEM_PROMPTER(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_SYSTEM_PROMPTER))
+#define GCR_IS_SYSTEM_PROMPTER_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_SYSTEM_PROMPTER))
+#define GCR_SYSTEM_PROMPTER_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_SYSTEM_PROMPTER, GcrSystemPrompterClass))
+
+typedef struct _GcrSystemPrompter GcrSystemPrompter;
+typedef struct _GcrSystemPrompterClass GcrSystemPrompterClass;
+typedef struct _GcrSystemPrompterPrivate GcrSystemPrompterPrivate;
+
+struct _GcrSystemPrompter {
+	GObject parent;
+	GcrSystemPrompterPrivate *pv;
+};
+
+struct _GcrSystemPrompterClass {
+	GObjectClass parent_class;
+
+	void         (*show_prompt)             (GcrSystemPrompter *self);
+
+	gboolean     (*prompt_password)         (GcrSystemPrompter *self);
+
+	gboolean     (*prompt_confirm)          (GcrSystemPrompter *self);
+
+	void         (*responded)               (GcrSystemPrompter *self);
+
+	void         (*hide_prompt)             (GcrSystemPrompter *self);
+};
+
+GType                gcr_system_prompter_get_type                  (void) G_GNUC_CONST;
+
+GcrSystemPrompter *  gcr_system_prompter_new                       (void);
+
+void                 gcr_system_prompter_register                  (GcrSystemPrompter *self,
+                                                                    GDBusConnection *connection);
+
+void                 gcr_system_prompter_unregister                (GcrSystemPrompter *self,
+                                                                    GDBusConnection *connection);
+
+const gchar *        gcr_system_prompter_get_title                 (GcrSystemPrompter *self);
+
+const gchar *        gcr_system_prompter_get_message               (GcrSystemPrompter *self);
+
+const gchar *        gcr_system_prompter_get_description           (GcrSystemPrompter *self);
+
+const gchar *        gcr_system_prompter_get_warning               (GcrSystemPrompter *self);
+
+void                 gcr_system_prompter_set_warning               (GcrSystemPrompter *self,
+                                                                    const gchar *warning);
+
+gboolean             gcr_system_prompter_get_password_new          (GcrSystemPrompter *self);
+
+gint                 gcr_system_prompter_get_password_strength     (GcrSystemPrompter *self);
+
+void                 gcr_system_prompter_set_password_strength     (GcrSystemPrompter *self,
+                                                                    gint strength);
+
+const gchar *        gcr_system_prompter_get_choice_label          (GcrSystemPrompter *self);
+
+gboolean             gcr_system_prompter_get_choice_chosen         (GcrSystemPrompter *self);
+
+void                 gcr_system_prompter_set_choice_chosen         (GcrSystemPrompter *self,
+                                                                    gboolean chosen);
+
+const gchar *        gcr_system_prompter_get_caller_window         (GcrSystemPrompter *self);
+
+void                 gcr_system_prompter_respond_cancelled         (GcrSystemPrompter *self);
+
+void                 gcr_system_prompter_respond_with_password     (GcrSystemPrompter *self,
+                                                                    const gchar *password);
+
+void                 gcr_system_prompter_respond_confirmed         (GcrSystemPrompter *self);
+
+G_END_DECLS
+
+#endif /* __GCR_SYSTEM_PROMPTER_H__ */
diff --git a/gcr/org.gnome.keyring.Prompt.xml b/gcr/org.gnome.keyring.Prompt.xml
new file mode 100644
index 0000000..0028575
--- /dev/null
+++ b/gcr/org.gnome.keyring.Prompt.xml
@@ -0,0 +1,39 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd";>
+
+<node>
+	<interface name="org.gnome.keyring.Prompter.Prompt">
+		<property name="Title" type="s" access="readwrite"/>
+
+		<property name="Message" type="s" access="readwrite"/>
+
+		<property name="Description" type="s" access="readwrite"/>
+
+		<property name="Warning" type="s" access="readwrite"/>
+
+		<property name="PasswordNew" type="b" access="readwrite"/>
+
+		<property name="PasswordStrength" type="i" access="read"/>
+
+		<property name="ChoiceLabel" type="s" access="readwrite"/>
+
+		<property name="ChoiceChosen" type="b" access="readwrite"/>
+
+		<property name="CallerWindow" type="s" access="readwrite"/>
+
+		<method name="RequestPassword">
+			<arg name="properties" type="a{sv}" direction="in"/>
+			<arg name="input" type="s" direction="in"/>
+			xxx
+			<arg name="properties" type="a{sv}" direction="out"/>
+			<arg name="output" type="s" direction="out"/>
+		</method>
+
+		<method name="RequestConfirm">
+			<arg name="properties" type="a{sv}" direction="in"/>
+			xxx
+			<arg name="properties" type="a{sv}" direction="out"/>
+			<arg name="confirmed" type="b" direction="out"/>
+		</method>
+	</interface>
+</node>
diff --git a/gcr/org.gnome.keyring.Prompter.xml b/gcr/org.gnome.keyring.Prompter.xml
new file mode 100644
index 0000000..8fdda02
--- /dev/null
+++ b/gcr/org.gnome.keyring.Prompter.xml
@@ -0,0 +1,15 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd";>
+
+<node name="/org/gnome/keyring/Prompter">
+	<interface name="org.gnome.keyring.Prompter">
+		<method name="BeginPrompting">
+			<arg name="prompt" type="o" direction="out"/>
+		</method>
+		<method name="FinishPrompting">
+			<arg name="prompt" type="o" direction="in"/>
+		</method>
+		<signal name="PrompterReady">
+		</signal>
+	</interface>
+</node>
diff --git a/gcr/tests/Makefile.am b/gcr/tests/Makefile.am
index 7f71f68..165c7ec 100644
--- a/gcr/tests/Makefile.am
+++ b/gcr/tests/Makefile.am
@@ -37,7 +37,8 @@ TEST_PROGS = \
 	test-memory-icon \
 	test-gnupg-key \
 	test-gnupg-collection \
-	test-gnupg-process
+	test-gnupg-process \
+	test-system-prompt
 
 check_PROGRAMS = $(TEST_PROGS)
 



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