[gcr] gcr: Rework how prompter works by introducing GcrPrompt interface



commit b548891251edd0bf2afc9651c36ac3c7d1248e00
Author: Stef Walter <stefw collabora co uk>
Date:   Mon Nov 21 08:46:47 2011 +0100

    gcr: Rework how prompter works by introducing GcrPrompt interface
    
     * GcrSystemPrompt is an implementation of GcrPrompt which
       calls to GcrSystemPrompter
     * GcrSystemPrompter creates GcrPrompt objects on its side
       which do the actual prompting.

 Makefile.decl                      |    4 +-
 gcr/Makefile.am                    |   10 +-
 gcr/gcr-base.h                     |    1 +
 gcr/gcr-base.symbols               |   86 ++--
 gcr/gcr-dbus-constants.h           |   30 +-
 gcr/gcr-mock-prompter.c            |  556 +++++++++------
 gcr/gcr-mock-prompter.h            |    4 +-
 gcr/gcr-prompt-dialog.c            |  808 +++++++++++++++++++++
 gcr/gcr-prompt-dialog.h            |   61 ++
 gcr/gcr-prompt.c                   |  441 ++++++++++++
 gcr/gcr-prompt.h                   |  152 ++++
 gcr/gcr-prompter-tool.c            |  595 +---------------
 gcr/gcr-secret-exchange.c          |   24 +-
 gcr/gcr-system-prompt.c            | 1333 ++++++++++++++++-------------------
 gcr/gcr-system-prompt.h            |   76 +--
 gcr/gcr-system-prompter.c          | 1390 ++++++++++++++----------------------
 gcr/gcr-system-prompter.h          |   63 +--
 gcr/gcr.h                          |    1 +
 gcr/gcr.symbols                    |    1 +
 gcr/org.gnome.keyring.Prompt.xml   |   36 -
 gcr/org.gnome.keyring.Prompter.xml |   29 +-
 gcr/tests/frob-system-prompt.c     |   10 +-
 gcr/tests/test-system-prompt.c     |  227 +++++--
 po/POTFILES.in                     |    2 +
 24 files changed, 3243 insertions(+), 2697 deletions(-)
---
diff --git a/Makefile.decl b/Makefile.decl
index f399af3..4eaaa42 100644
--- a/Makefile.decl
+++ b/Makefile.decl
@@ -1,5 +1,7 @@
 NULL =
 
+TEST_ARGS =
+
 TEST_SUPPRESSIONS = $(top_builddir)/build/valgrind-suppressions
 
 perform-memcheck: $(TEST_PROGS)
@@ -10,7 +12,7 @@ perform-memcheck: $(TEST_PROGS)
 			--suppressions=$(TEST_SUPPRESSIONS) \
 			--leak-check=full --show-reachable=yes --num-callers=16 \
 			--quiet --error-exitcode=3 \
-			$(builddir)/$$test || exit 3; \
+			$(builddir)/$$test $(TEST_ARGS) || exit 3; \
 	done
 
 coverage:
diff --git a/gcr/Makefile.am b/gcr/Makefile.am
index 631b62d..be40c75 100644
--- a/gcr/Makefile.am
+++ b/gcr/Makefile.am
@@ -30,6 +30,7 @@ HEADER_BASE_FILES = \
 	gcr-mock-prompter.h \
 	gcr-parser.h \
 	gcr-pkcs11-certificate.h \
+	gcr-prompt.h \
 	gcr-secret-exchange.h \
 	gcr-simple-certificate.h \
 	gcr-system-prompt.h \
@@ -54,6 +55,7 @@ HEADER_UI_FILES = \
 	gcr-key-widget.h \
 	gcr-import-button.h \
 	gcr-list-selector.h \
+	gcr-prompt-dialog.h \
 	gcr-renderer.h \
 	gcr-secure-entry-buffer.h \
 	gcr-simple-collection.h \
@@ -134,6 +136,7 @@ libgcr_base_ GCR_MAJOR@_la_SOURCES = \
 	gcr-parser.c gcr-parser.h \
 	gcr-pkcs11-certificate.c gcr-pkcs11-certificate.h \
 	gcr-pkcs11-importer.c gcr-pkcs11-importer.h \
+	gcr-prompt.c gcr-prompt.h \
 	gcr-record.c gcr-record.h \
 	gcr-secret-exchange.c gcr-secret-exchange.h \
 	gcr-simple-certificate.c gcr-simple-certificate.h \
@@ -175,6 +178,7 @@ libgcr_ GCR_MAJOR@_la_SOURCES = \
 	gcr-menu-button.c gcr-menu-button.h \
 	gcr-pkcs11-import-interaction.c gcr-pkcs11-import-interaction.h \
 	gcr-pkcs11-import-dialog.c gcr-pkcs11-import-dialog.h \
+	gcr-prompt-dialog.c gcr-prompt-dialog.h \
 	gcr-record.c gcr-record.h \
 	gcr-renderer.c gcr-renderer.h \
 	gcr-secure-entry-buffer.c gcr-secure-entry-buffer.h \
@@ -261,10 +265,10 @@ DBUS_XML_DEFINITIONS = \
 	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 \
+	$(AM_V_GEN) gdbus-codegen --interface-prefix org.gnome.keyring.internal. \
+		--generate-c-code gcr-dbus-generated --c-namespace GcrDBus \
 		$(DBUS_XML_DEFINITIONS)
-	$(AM_V_GEN) sed -i -e 's/gcr_/_gcr_/g' gcr-dbus-generated.[ch]
+	$(AM_V_GEN) sed -i -e 's/gcr_dbus/_gcr_dbus/g' gcr-dbus-generated.[ch]
 
 gcr-dbus-generated.h: gcr-dbus-generated.c
 
diff --git a/gcr/gcr-base.h b/gcr/gcr-base.h
index ff34ddc..873b2de 100644
--- a/gcr/gcr-base.h
+++ b/gcr/gcr-base.h
@@ -46,6 +46,7 @@
 #include "gcr-mock-prompter.h"
 #include "gcr-parser.h"
 #include "gcr-pkcs11-certificate.h"
+#include "gcr-prompt.h"
 #include "gcr-secret-exchange.h"
 #include "gcr-simple-certificate.h"
 #include "gcr-simple-collection.h"
diff --git a/gcr/gcr-base.symbols b/gcr/gcr-base.symbols
index 9256194..4a0e1ad 100644
--- a/gcr/gcr-base.symbols
+++ b/gcr/gcr-base.symbols
@@ -104,8 +104,12 @@ 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_mock_prompter_get_delay_msec
+gcr_mock_prompter_is_expecting
+gcr_mock_prompter_is_prompting
+gcr_mock_prompter_set_delay_msec
+gcr_mock_prompter_start
+gcr_mock_prompter_stop
 gcr_parsed_get_attributes
 gcr_parsed_get_data
 gcr_parsed_get_description
@@ -148,6 +152,33 @@ gcr_pkcs11_initialize_finish
 gcr_pkcs11_set_modules
 gcr_pkcs11_set_trust_lookup_uris
 gcr_pkcs11_set_trust_store_uri
+gcr_prompt_confirm
+gcr_prompt_confirm_async
+gcr_prompt_confirm_finish
+gcr_prompt_confirm_run
+gcr_prompt_get_caller_window
+gcr_prompt_get_choice_chosen
+gcr_prompt_get_choice_label
+gcr_prompt_get_description
+gcr_prompt_get_message
+gcr_prompt_get_password_new
+gcr_prompt_get_password_strength
+gcr_prompt_get_title
+gcr_prompt_get_type
+gcr_prompt_get_warning
+gcr_prompt_password
+gcr_prompt_password_async
+gcr_prompt_password_finish
+gcr_prompt_password_run
+gcr_prompt_reply_get_type
+gcr_prompt_set_caller_window
+gcr_prompt_set_choice_chosen
+gcr_prompt_set_choice_label
+gcr_prompt_set_description
+gcr_prompt_set_message
+gcr_prompt_set_password_new
+gcr_prompt_set_title
+gcr_prompt_set_warning
 gcr_secret_exchange_begin
 gcr_secret_exchange_get_protocol
 gcr_secret_exchange_get_secret
@@ -163,59 +194,26 @@ 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_mode
+gcr_system_prompter_get_prompt_type
 gcr_system_prompter_get_type
-gcr_system_prompter_get_warning
+gcr_system_prompter_mode_get_type
 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_prompter_unregister
+gcr_system_prompt_close
+gcr_system_prompt_close_async
+gcr_system_prompt_close_finish
 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_get_secret_exchange
 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_system_prompt_response_get_type
 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
index ac3787f..ebfc4b8 100644
--- a/gcr/gcr-dbus-constants.h
+++ b/gcr/gcr-dbus-constants.h
@@ -32,32 +32,28 @@ G_BEGIN_DECLS
 #define GCR_DBUS_PROMPTER_MOCK_BUS_NAME              "org.gnome.keyring.MockPrompter"
 
 #define GCR_DBUS_PROMPTER_OBJECT_PATH                "/org/gnome/keyring/Prompter"
+#define GCR_DBUS_PROMPT_OBJECT_PREFIX                "/org/gnome/keyring/internal/Prompt"
 
-#define GCR_DBUS_PROMPTER_INTERFACE                  "org.gnome.keyring.Prompter"
+#define GCR_DBUS_PROMPTER_INTERFACE                  "org.gnome.keyring.internal.Prompter"
 
 #define GCR_DBUS_PROMPTER_METHOD_BEGIN               "BeginPrompting"
-#define GCR_DBUS_PROMPTER_METHOD_FINISH              "FinishPrompting"
+#define GCR_DBUS_PROMPTER_METHOD_STOP                "StopPrompting"
+#define GCR_DBUS_PROMPTER_METHOD_PERFORM             "PerformPrompt"
 
-#define GCR_DBUS_PROMPTER_SIGNAL_READY               "PrompterReady"
-
-#define GCR_DBUS_PROMPT_INTERFACE                    "org.gnome.keyring.Prompter.Prompt"
+#define GCR_DBUS_CALLBACK_INTERFACE                  "org.gnome.keyring.internal.Prompter.Callback"
 
 #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_CALLBACK_METHOD_READY               "PromptReady"
+#define GCR_DBUS_CALLBACK_METHOD_DONE                "PromptDone"
+
+#define GCR_DBUS_PROMPT_TYPE_NONE                    ""
+#define GCR_DBUS_PROMPT_TYPE_PASSWORD                "password"
+#define GCR_DBUS_PROMPT_TYPE_CONFIRM                 "confirm"
 
-#define GCR_DBUS_PROMPT_METHOD_PASSWORD              "RequestPassword"
-#define GCR_DBUS_PROMPT_METHOD_CONFIRM               "RequestConfirm"
+#define GCR_DBUS_PROMPT_REPLY_YES                    "yes"
+#define GCR_DBUS_PROMPT_REPLY_NO                     "no"
 
 G_END_DECLS
 
diff --git a/gcr/gcr-mock-prompter.c b/gcr/gcr-mock-prompter.c
index 836ef35..fc0b6a6 100644
--- a/gcr/gcr-mock-prompter.c
+++ b/gcr/gcr-mock-prompter.c
@@ -24,6 +24,7 @@
 #include "config.h"
 
 #include "gcr-mock-prompter.h"
+#include "gcr-prompt.h"
 
 #include "egg/egg-error.h"
 
@@ -52,45 +53,47 @@
  */
 
 
-GType   gcr_mock_prompter_get_type        (void) G_GNUC_CONST;
-#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))
-#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))
+GType   _gcr_mock_prompt_get_type       (void) G_GNUC_CONST;
+#define GCR_TYPE_MOCK_PROMPT            (_gcr_mock_prompt_get_type ())
+#define GCR_MOCK_PROMPT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_MOCK_PROMPT, GcrMockPrompt))
+#define GCR_IS_MOCK_PROMPT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_MOCK_PROMPT))
+#define GCR_IS_MOCK_PROMPT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_MOCK_PROMPT))
+#define GCR_MOCK_PROMPT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_MOCK_PROMPT, GcrMockPromptClass))
+#define GCR_MOCK_PROMPT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_MOCK_PROMPT, GcrMockPromptClass))
 
-typedef struct _GcrMockPrompter GcrMockPrompter;
-typedef struct _GcrMockPrompterClass GcrMockPrompterClass;
-typedef struct _GcrMockPrompterPrivate GcrMockPrompterPrivate;
+typedef struct _GcrMockPrompt GcrMockPrompt;
+typedef struct _GcrMockPromptClass GcrMockPromptClass;
+typedef struct _GcrMockPromptPrivate GcrMockPromptPrivate;
 
 enum {
 	PROP_0,
-	PROP_CONNECTION,
-	PROP_SHOWING,
-	PROP_DELAY_MSEC
+
+	PROP_TITLE,
+	PROP_MESSAGE,
+	PROP_DESCRIPTION,
+	PROP_WARNING,
+	PROP_PASSWORD_NEW,
+	PROP_PASSWORD_STRENGTH,
+	PROP_CHOICE_LABEL,
+	PROP_CHOICE_CHOSEN,
+	PROP_CALLER_WINDOW
 };
 
-struct _GcrMockPrompter {
-	GcrSystemPrompter parent;
-	GDBusConnection *connection;
-	GQueue *responses;
-	gboolean showing;
-	guint delay_msec;
+struct _GcrMockPrompt {
+	GObject parent;
+	GHashTable *properties;
 	guint delay_source;
+	gboolean disposed;
 };
 
-struct _GcrMockPrompterClass {
-	GcrSystemPrompterClass parent_class;
+struct _GcrMockPromptClass {
+	GObjectClass parent_class;
 };
 
 typedef struct {
 	gboolean proceed;
 	gchar *password;
 	GList *properties;
-
-	/* Used while responding */
-	GcrMockPrompter *prompter;
 } MockResponse;
 
 typedef struct {
@@ -99,15 +102,23 @@ typedef struct {
 	GCond *start_cond;
 	GThread *thread;
 
+	guint delay_msec;
+	GQueue responses;
+
 	/* Owned by the prompter thread*/
-	GcrMockPrompter *prompter;
+	GcrSystemPrompter *prompter;
 	const gchar *bus_name;
 	GMainLoop *loop;
 } ThreadData;
 
+static gint prompts_a_prompting = 0;
 static ThreadData *running = NULL;
 
-G_DEFINE_TYPE (GcrMockPrompter, gcr_mock_prompter, GCR_TYPE_SYSTEM_PROMPTER);
+static void    gcr_mock_prompt_iface     (GcrPromptIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GcrMockPrompt, _gcr_mock_prompt, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (GCR_TYPE_PROMPT, gcr_mock_prompt_iface);
+);
 
 static void
 mock_property_free (gpointer data)
@@ -121,32 +132,100 @@ static void
 mock_response_free (gpointer data)
 {
 	MockResponse *response = data;
+
+	if (response == NULL)
+		return;
+
 	g_free (response->password);
 	g_list_free_full (response->properties, mock_property_free);
-	g_clear_object (&response->prompter);
 	g_free (response);
 }
 
 static void
-gcr_mock_prompter_init (GcrMockPrompter *self)
+blank_string_property (GHashTable *properties,
+                       const gchar *property)
+{
+	GParameter *param;
+
+	param = g_new0 (GParameter, 1);
+	param->name = property;
+	g_value_init (&param->value, G_TYPE_STRING);
+	g_value_set_string (&param->value, "");
+	g_hash_table_insert (properties, (gpointer)param->name, param);
+}
+
+
+static void
+blank_boolean_property (GHashTable *properties,
+                        const gchar *property)
+{
+	GParameter *param;
+
+	param = g_new0 (GParameter, 1);
+	param->name = property;
+	g_value_init (&param->value, G_TYPE_BOOLEAN);
+	g_value_set_boolean (&param->value, FALSE);
+	g_hash_table_insert (properties, (gpointer)param->name, param);
+}
+
+static void
+blank_int_property (GHashTable *properties,
+                    const gchar *property)
+{
+	GParameter *param;
+
+	param = g_new0 (GParameter, 1);
+	param->name = property;
+	g_value_init (&param->value, G_TYPE_INT);
+	g_value_set_int (&param->value, 0);
+	g_hash_table_insert (properties, (gpointer)param->name, param);
+}
+
+static void
+_gcr_mock_prompt_init (GcrMockPrompt *self)
 {
-	self->responses = g_queue_new ();
+	g_atomic_int_add (&prompts_a_prompting, 1);
+
+	self->properties = g_hash_table_new_full (g_str_hash, g_str_equal,
+	                                          NULL, mock_property_free);
+
+	blank_string_property (self->properties, "title");
+	blank_string_property (self->properties, "message");
+	blank_string_property (self->properties, "description");
+	blank_string_property (self->properties, "warning");
+	blank_string_property (self->properties, "choice-label");
+	blank_string_property (self->properties, "caller-window");
+
+	blank_boolean_property (self->properties, "choice-chosen");
+	blank_boolean_property (self->properties, "password-new");
+
+	blank_int_property (self->properties, "password-strength");
 }
 
 static void
-gcr_mock_prompter_set_property (GObject *obj,
-                                guint prop_id,
-                                const GValue *value,
-                                GParamSpec *pspec)
+_gcr_mock_prompt_set_property (GObject *obj,
+                              guint prop_id,
+                              const GValue *value,
+                              GParamSpec *pspec)
 {
-	GcrMockPrompter *self = GCR_MOCK_PROMPTER (obj);
+	GcrMockPrompt *self = GCR_MOCK_PROMPT (obj);
+	GParameter *param;
 
 	switch (prop_id) {
-	case PROP_CONNECTION:
-		self->connection = g_value_get_object (value);
-		break;
-	case PROP_DELAY_MSEC:
-		self->delay_msec = g_value_get_uint (value);
+	case PROP_TITLE:
+	case PROP_MESSAGE:
+	case PROP_DESCRIPTION:
+	case PROP_WARNING:
+	case PROP_PASSWORD_NEW:
+	case PROP_CHOICE_LABEL:
+	case PROP_CHOICE_CHOSEN:
+	case PROP_CALLER_WINDOW:
+		param = g_new0 (GParameter, 1);
+		param->name = pspec->name;
+		g_value_init (&param->value, pspec->value_type);
+		g_value_copy (value, &param->value);
+		g_hash_table_replace (self->properties, (gpointer)param->name, param);
+		g_object_notify (G_OBJECT (self), param->name);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
@@ -155,22 +234,27 @@ gcr_mock_prompter_set_property (GObject *obj,
 }
 
 static void
-gcr_mock_prompter_get_property (GObject *obj,
-                                guint prop_id,
-                                GValue *value,
-                                GParamSpec *pspec)
+_gcr_mock_prompt_get_property (GObject *obj,
+                              guint prop_id,
+                              GValue *value,
+                              GParamSpec *pspec)
 {
-	GcrMockPrompter *self = GCR_MOCK_PROMPTER (obj);
+	GcrMockPrompt *self = GCR_MOCK_PROMPT (obj);
+	GParameter *param;
 
 	switch (prop_id) {
-	case PROP_CONNECTION:
-		g_value_set_object (value, self->connection);
-		break;
-	case PROP_SHOWING:
-		g_value_set_boolean (value, self->showing);
-		break;
-	case PROP_DELAY_MSEC:
-		g_value_set_uint (value, self->delay_msec);
+	case PROP_TITLE:
+	case PROP_MESSAGE:
+	case PROP_DESCRIPTION:
+	case PROP_WARNING:
+	case PROP_PASSWORD_NEW:
+	case PROP_PASSWORD_STRENGTH:
+	case PROP_CHOICE_LABEL:
+	case PROP_CHOICE_CHOSEN:
+	case PROP_CALLER_WINDOW:
+		param = g_hash_table_lookup (self->properties, pspec->name);
+		g_return_if_fail (param != NULL);
+		g_value_copy (&param->value, value);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
@@ -180,37 +264,31 @@ gcr_mock_prompter_get_property (GObject *obj,
 
 
 static void
-gcr_mock_prompter_dispose (GObject *obj)
+_gcr_mock_prompt_dispose (GObject *obj)
 {
-	GcrMockPrompter *self = GCR_MOCK_PROMPTER (obj);
-	MockResponse *response;
+	GcrMockPrompt *self = GCR_MOCK_PROMPT (obj);
 
 	if (self->delay_source) {
 		g_source_remove (self->delay_source);
 		self->delay_source = 0;
 	}
 
-	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;
+	if (!self->disposed) {
+		g_atomic_int_add (&prompts_a_prompting, -1);
+		self->disposed = TRUE;
 	}
 
-	while ((response = g_queue_pop_head (self->responses)))
-		mock_response_free (response);
-
-	G_OBJECT_CLASS (gcr_mock_prompter_parent_class)->dispose (obj);
+	G_OBJECT_CLASS (_gcr_mock_prompt_parent_class)->dispose (obj);
 }
 
 static void
-gcr_mock_prompter_finalize (GObject *obj)
+_gcr_mock_prompt_finalize (GObject *obj)
 {
-	GcrMockPrompter *self = GCR_MOCK_PROMPTER (obj);
+	GcrMockPrompt *self = GCR_MOCK_PROMPT (obj);
 
-	g_queue_free (self->responses);
+	g_hash_table_destroy(self->properties);
 
-	G_OBJECT_CLASS (gcr_mock_prompter_parent_class)->finalize (obj);
+	G_OBJECT_CLASS (_gcr_mock_prompt_parent_class)->finalize (obj);
 }
 
 static gboolean
@@ -254,9 +332,10 @@ value_equal (const GValue *a, const GValue *b)
 }
 
 static void
-prompter_set_properties (GcrMockPrompter *self,
-                         GList *properties)
+prompt_set_or_check_properties (GcrMockPrompt *self,
+                                GList *properties)
 {
+	GValue value = G_VALUE_INIT;
 	GObjectClass *object_class;
 	GParameter *param;
 	GParamSpec *spec;
@@ -269,189 +348,212 @@ prompter_set_properties (GcrMockPrompter *self,
 		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)) {
+		/* For these we set the value */
+		if (g_str_equal (param->name, "choice-chosen")) {
 			g_object_set_property (G_OBJECT (self), param->name, &param->value);
 
-		/* Other properties get checked */
+		/* For others we check that the value is correct */
 		} else {
-			GValue value = G_VALUE_INIT;
-
-			g_value_init (&value, spec->value_type);
+			g_value_init (&value, G_VALUE_TYPE (&param->value));
 			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 void
-gcr_mock_prompter_open (GcrSystemPrompter *prompter)
-{
-	GcrMockPrompter *self = GCR_MOCK_PROMPTER (prompter);
-	self->showing = TRUE;
-}
-
-static void
-gcr_mock_prompter_close (GcrSystemPrompter *prompter)
+_gcr_mock_prompt_class_init (GcrMockPromptClass *klass)
 {
-	GcrMockPrompter *self = GCR_MOCK_PROMPTER (prompter);
-
-	if (self->delay_source != 0) {
-		g_source_remove (self->delay_source);
-		self->delay_source = 0;
-	}
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
-	self->showing = FALSE;
+	gobject_class->get_property = _gcr_mock_prompt_get_property;
+	gobject_class->set_property = _gcr_mock_prompt_set_property;
+	gobject_class->dispose = _gcr_mock_prompt_dispose;
+	gobject_class->finalize = _gcr_mock_prompt_finalize;
+
+	g_object_class_override_property (gobject_class, PROP_TITLE, "title");
+	g_object_class_override_property (gobject_class, PROP_MESSAGE, "message");
+	g_object_class_override_property (gobject_class, PROP_DESCRIPTION, "description");
+	g_object_class_override_property (gobject_class, PROP_WARNING, "warning");
+	g_object_class_override_property (gobject_class, PROP_CALLER_WINDOW, "caller-window");
+	g_object_class_override_property (gobject_class, PROP_CHOICE_LABEL, "choice-label");
+	g_object_class_override_property (gobject_class, PROP_CHOICE_CHOSEN, "choice-chosen");
+	g_object_class_override_property (gobject_class, PROP_PASSWORD_NEW, "password-new");
+	g_object_class_override_property (gobject_class, PROP_PASSWORD_STRENGTH, "password-strength");
 }
 
 static gboolean
-on_timeout_prompt_confirm (gpointer data)
+on_timeout_complete_response (gpointer data)
 {
-	MockResponse *response = data;
-	GcrSystemPrompter *prompter = GCR_SYSTEM_PROMPTER (response->prompter);
-
-	response->prompter->delay_source = 0;
-
-	if (!response->proceed)
-		gcr_system_prompter_respond_cancelled (prompter);
-	else
-		gcr_system_prompter_respond_confirmed (prompter);
-
+	GSimpleAsyncResult *res = data;
+	g_simple_async_result_complete_in_idle (res);
 	return FALSE;
 }
 
-static gboolean
-gcr_mock_prompter_prompt_confirm (GcrSystemPrompter *prompter)
+static void
+gcr_mock_prompt_confirm_async (GcrPrompt *prompt,
+                               GCancellable *cancellable,
+                               GAsyncReadyCallback callback,
+                               gpointer user_data)
 {
-	GcrMockPrompter *self = GCR_MOCK_PROMPTER (prompter);
-	MockResponse *response = g_queue_pop_head (self->responses);
+	GcrMockPrompt *self = GCR_MOCK_PROMPT (prompt);
+	GSimpleAsyncResult *res;
+	MockResponse *response;
+	guint delay_msec;
+
+	g_mutex_lock (running->mutex);
+	delay_msec = running->delay_msec;
+	response = g_queue_pop_head (&running->responses);
+	g_mutex_unlock (running->mutex);
+
+	res = g_simple_async_result_new (G_OBJECT (prompt), callback, user_data,
+	                                 gcr_mock_prompt_confirm_async);
 
 	if (response == NULL) {
-		g_critical ("confirmation prompt requested, but not expected");
-		return FALSE;
+		g_critical ("password prompt requested, but not expected");
+		g_simple_async_result_set_op_res_gboolean (res, FALSE);
 
 	} else if (response->password) {
 		g_critical ("confirmation prompt requested, but password prompt expected");
-		mock_response_free (response);
-		return FALSE;
+		g_simple_async_result_set_op_res_gboolean (res, FALSE);
+
+	} else {
+		prompt_set_or_check_properties (self, response->properties);
+		g_simple_async_result_set_op_res_gboolean (res, response->proceed);
 	}
 
-	prompter_set_properties (self, response->properties);
-	response->prompter = g_object_ref (prompter);
+	mock_response_free (response);
 
-	if (self->delay_msec > 0) {
+	if (delay_msec > 0) {
 		g_assert (!self->delay_source);
-		self->delay_source = g_timeout_add_full (G_PRIORITY_DEFAULT, self->delay_msec,
-		                                         on_timeout_prompt_confirm,
-		                                         response, mock_response_free);
+		self->delay_source = g_timeout_add_full (G_PRIORITY_DEFAULT, delay_msec,
+		                                         on_timeout_complete_response,
+		                                         g_object_ref (res), g_object_unref);
 	} else {
-		on_timeout_prompt_confirm (response);
-		mock_response_free (response);
+		on_timeout_complete_response (res);
 	}
 
-	return TRUE;
+	g_object_unref (res);
 }
 
-static gboolean
-on_timeout_prompt_password (gpointer data)
+static GcrPromptReply
+gcr_mock_prompt_confirm_finish (GcrPrompt *prompt,
+                                GAsyncResult *result,
+                                GError **error)
 {
-	MockResponse *response = data;
-	GcrSystemPrompter *prompter = GCR_SYSTEM_PROMPTER (response->prompter);
-
-	response->prompter->delay_source = 0;
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (prompt),
+	                      gcr_mock_prompt_confirm_async), GCR_PROMPT_REPLY_CANCEL);
 
-	if (!response->proceed)
-		gcr_system_prompter_respond_cancelled (prompter);
-	else
-		gcr_system_prompter_respond_with_password (prompter, response->password);
+	return g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (result)) ?
+	               GCR_PROMPT_REPLY_OK : GCR_PROMPT_REPLY_CANCEL;
+}
 
-	return FALSE;
+static void
+ensure_password_strength (GcrMockPrompt *self,
+                          const gchar *password)
+{
+	GParameter *param;
+	gint strength;
+
+	strength = strlen (password) > 0 ? 1 : 0;
+	param = g_new0 (GParameter, 1);
+	param->name = "password-strength";
+	g_value_init (&param->value, G_TYPE_INT);
+	g_value_set_int (&param->value, strength);
+	g_hash_table_replace (self->properties, (gpointer)param->name, param);
+	g_object_notify (G_OBJECT (self), param->name);
 }
 
-static gboolean
-gcr_mock_prompter_prompt_password (GcrSystemPrompter *prompter)
+static void
+gcr_mock_prompt_password_async (GcrPrompt *prompt,
+                                GCancellable *cancellable,
+                                GAsyncReadyCallback callback,
+                                gpointer user_data)
 {
-	GcrMockPrompter *self = GCR_MOCK_PROMPTER (prompter);
-	MockResponse *response = g_queue_pop_head (self->responses);
+	GcrMockPrompt *self = GCR_MOCK_PROMPT (prompt);
+	GSimpleAsyncResult *res;
+	MockResponse *response;
+	guint delay_msec;
+
+	g_mutex_lock (running->mutex);
+	delay_msec = running->delay_msec;
+	response = g_queue_pop_head (&running->responses);
+	g_mutex_unlock (running->mutex);
+
+	res = g_simple_async_result_new (G_OBJECT (prompt), callback, user_data,
+	                                 gcr_mock_prompt_password_async);
 
 	if (response == NULL) {
 		g_critical ("password prompt requested, but not expected");
-		return FALSE;
+		g_simple_async_result_set_op_res_gpointer (res, NULL, NULL);
 
 	} else if (!response->password) {
 		g_critical ("password prompt requested, but confirmation prompt expected");
-		mock_response_free (response);
-		return FALSE;
+		g_simple_async_result_set_op_res_gpointer (res, NULL, NULL);
+
+	} else if (!response->proceed) {
+		prompt_set_or_check_properties (self, response->properties);
+		g_simple_async_result_set_op_res_gpointer (res, NULL, NULL);
 
+	} else {
+		ensure_password_strength (self, response->password);
+		prompt_set_or_check_properties (self, response->properties);
+		g_simple_async_result_set_op_res_gpointer (res, response->password, g_free);
+		response->password = NULL;
 	}
 
-	prompter_set_properties (self, response->properties);
-	response->prompter = g_object_ref (prompter);
+	mock_response_free (response);
 
-	if (self->delay_msec > 0) {
+	if (delay_msec > 0) {
 		g_assert (!self->delay_source);
-		self->delay_source = g_timeout_add_full (G_PRIORITY_DEFAULT, self->delay_msec,
-		                                         on_timeout_prompt_password,
-		                                         response, mock_response_free);
+		self->delay_source = g_timeout_add_full (G_PRIORITY_DEFAULT, delay_msec,
+		                                         on_timeout_complete_response,
+		                                         g_object_ref (res), g_object_unref);
 	} else {
-		on_timeout_prompt_password (response);
-		mock_response_free (response);
+		on_timeout_complete_response (res);
 	}
 
-	return TRUE;
+	g_object_unref (res);
 }
 
-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->open = gcr_mock_prompter_open;
-	prompter_class->close = gcr_mock_prompter_close;
-	prompter_class->prompt_password= gcr_mock_prompter_prompt_password;
-	prompter_class->prompt_confirm = gcr_mock_prompter_prompt_confirm;
+static const gchar *
+gcr_mock_prompt_password_finish (GcrPrompt *prompt,
+                                 GAsyncResult *result,
+                                 GError **error)
+{
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (prompt),
+	                      gcr_mock_prompt_password_async), NULL);
 
-	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));
+	return g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
 
-	g_object_class_install_property (gobject_class, PROP_SHOWING,
-	            g_param_spec_boolean ("showing", "Showing", "Whether showing a prompt",
-	                                  FALSE, G_PARAM_READABLE));
+}
 
-	g_object_class_install_property (gobject_class, PROP_DELAY_MSEC,
-	              g_param_spec_uint ("delay-msec", "Delay msec", "Prompt delay in milliseconds",
-	                                 0, G_MAXUINT, 0, G_PARAM_READWRITE));
+static void
+gcr_mock_prompt_iface (GcrPromptIface *iface)
+{
+	iface->prompt_confirm_async = gcr_mock_prompt_confirm_async;
+	iface->prompt_confirm_finish = gcr_mock_prompt_confirm_finish;
+	iface->prompt_password_async = gcr_mock_prompt_password_async;
+	iface->prompt_password_finish = gcr_mock_prompt_password_finish;
 }
 
 static GList *
-build_properties (GcrMockPrompter *self,
+build_properties (GObjectClass *object_class,
                   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;
@@ -461,14 +563,12 @@ build_properties (GcrMockPrompter *self,
 
 		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);
+			g_warning ("prompt object class has no property named '%s'", 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);
+			g_warning ("prompt property '%s' can't be set after construction", name);
 			break;
 		}
 
@@ -492,16 +592,9 @@ build_properties (GcrMockPrompter *self,
 }
 
 gboolean
-gcr_mock_prompter_get_showing (void)
+gcr_mock_prompter_is_prompting (void)
 {
-	gboolean showing = FALSE;
-
-	g_assert (running != NULL);
-	g_mutex_lock (running->mutex);
-	g_object_get (running->prompter, "showing", &showing, NULL);
-	g_mutex_unlock (running->mutex);
-
-	return showing;
+	return g_atomic_int_get (&prompts_a_prompting) > 0;
 }
 
 guint
@@ -511,7 +604,7 @@ gcr_mock_prompter_get_delay_msec (void)
 
 	g_assert (running != NULL);
 	g_mutex_lock (running->mutex);
-	g_object_get (running->prompter, "delay-msec", &delay_msec, NULL);
+	delay_msec = running->delay_msec;
 	g_mutex_unlock (running->mutex);
 
 	return delay_msec;
@@ -522,7 +615,7 @@ gcr_mock_prompter_set_delay_msec (guint delay_msec)
 {
 	g_assert (running != NULL);
 	g_mutex_lock (running->mutex);
-	g_object_set (running->prompter, "delay-msec", delay_msec, NULL);
+	running->delay_msec = delay_msec;
 	g_mutex_unlock (running->mutex);
 }
 
@@ -531,6 +624,7 @@ gcr_mock_prompter_expect_confirm_ok (const gchar *first_property_name,
                                      ...)
 {
 	MockResponse *response;
+	gpointer klass;
 	va_list var_args;
 
 	g_assert (running != NULL);
@@ -541,11 +635,14 @@ gcr_mock_prompter_expect_confirm_ok (const gchar *first_property_name,
 	response->password = NULL;
 	response->proceed = TRUE;
 
+	klass = g_type_class_ref (gcr_system_prompter_get_prompt_type (running->prompter));
+
 	va_start (var_args, first_property_name);
-	response->properties = build_properties (running->prompter, first_property_name, var_args);
+	response->properties = build_properties (G_OBJECT_CLASS (klass), first_property_name, var_args);
 	va_end (var_args);
 
-	g_queue_push_tail (running->prompter->responses, response);
+	g_type_class_unref (klass);
+	g_queue_push_tail (&running->responses, response);
 	g_mutex_unlock (running->mutex);
 }
 
@@ -562,7 +659,7 @@ gcr_mock_prompter_expect_confirm_cancel (void)
 	response->password = NULL;
 	response->proceed = FALSE;
 
-	g_queue_push_tail (running->prompter->responses, response);
+	g_queue_push_tail (&running->responses, response);
 
 	g_mutex_unlock (running->mutex);
 }
@@ -573,6 +670,7 @@ gcr_mock_prompter_expect_password_ok (const gchar *password,
                                       ...)
 {
 	MockResponse *response;
+	gpointer klass;
 	va_list var_args;
 
 	g_assert (running != NULL);
@@ -584,11 +682,14 @@ gcr_mock_prompter_expect_password_ok (const gchar *password,
 	response->password = g_strdup (password);
 	response->proceed = TRUE;
 
+	klass = g_type_class_ref (gcr_system_prompter_get_prompt_type (running->prompter));
+
 	va_start (var_args, first_property_name);
-	response->properties = build_properties (running->prompter, first_property_name, var_args);
+	response->properties = build_properties (G_OBJECT_CLASS (klass), first_property_name, var_args);
 	va_end (var_args);
 
-	g_queue_push_tail (running->prompter->responses, response);
+	g_type_class_unref (klass);
+	g_queue_push_tail (&running->responses, response);
 
 	g_mutex_unlock (running->mutex);
 }
@@ -606,9 +707,33 @@ gcr_mock_prompter_expect_password_cancel (void)
 	response->password = g_strdup ("");
 	response->proceed = FALSE;
 
-	g_queue_push_tail (running->prompter->responses, response);
+	g_queue_push_tail (&running->responses, response);
+
+	g_mutex_unlock (running->mutex);
+}
+
+gboolean
+gcr_mock_prompter_is_expecting (void)
+{
+	gboolean expecting;
+
+	g_assert (running != NULL);
+
+	g_mutex_lock (running->mutex);
+
+	expecting = !g_queue_is_empty (&running->responses);
 
 	g_mutex_unlock (running->mutex);
+
+	return expecting;
+}
+
+static gboolean
+on_idle_signal_cond (gpointer user_data)
+{
+	GCond *cond = user_data;
+	g_cond_signal (cond);
+	return FALSE; /* Don't run again */
 }
 
 static gpointer
@@ -618,13 +743,15 @@ mock_prompter_thread (gpointer data)
 	GDBusConnection *connection;
 	GMainContext *context;
 	GError *error = NULL;
+	GSource *idle;
 	gchar *address;
 
 	g_mutex_lock (thread_data->mutex);
 	context = g_main_context_new ();
 	g_main_context_push_thread_default (context);
 
-	thread_data->prompter = g_object_new (GCR_TYPE_MOCK_PROMPTER, NULL);
+	thread_data->prompter = gcr_system_prompter_new (GCR_SYSTEM_PROMPTER_SINGLE,
+	                                                 GCR_TYPE_MOCK_PROMPT);
 
 	address = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, NULL, &error);
 	if (error == NULL) {
@@ -636,6 +763,9 @@ mock_prompter_thread (gpointer data)
 			gcr_system_prompter_register (GCR_SYSTEM_PROMPTER (thread_data->prompter),
 			                              connection);
 			thread_data->bus_name = g_dbus_connection_get_unique_name (connection);
+		} else {
+			g_critical ("couldn't create connection: %s", error->message);
+			g_error_free (error);
 		}
 
 		g_free (address);
@@ -648,21 +778,35 @@ mock_prompter_thread (gpointer data)
 	}
 
 	thread_data->loop = g_main_loop_new (context, FALSE);
-	g_cond_signal (thread_data->start_cond);
 	g_mutex_unlock (thread_data->mutex);
 
+	idle = g_idle_source_new ();
+	g_source_set_callback (idle, on_idle_signal_cond, thread_data->start_cond, NULL);
+	g_source_attach (idle, context);
+	g_source_unref (idle);
+
 	g_main_loop_run (thread_data->loop);
 
 	g_mutex_lock (thread_data->mutex);
 	g_main_context_pop_thread_default (context);
-	g_main_context_unref (context);
 
-	if (connection) {
-		gcr_system_prompter_unregister (GCR_SYSTEM_PROMPTER (thread_data->prompter),
-		                                connection);
-		g_object_unref (connection);
+	gcr_system_prompter_unregister (thread_data->prompter, TRUE);
+	g_object_unref (thread_data->prompter);
+	thread_data->prompter = NULL;
+
+	if (!g_dbus_connection_flush_sync (connection, NULL, &error)) {
+		g_critical ("connection flush failed: %s", error->message);
+		g_error_free (error);
 	}
 
+	g_object_unref (connection);
+
+	while (g_main_context_iteration (context, FALSE));
+
+	g_main_context_unref (context);
+	g_main_loop_unref (thread_data->loop);
+	thread_data->loop = NULL;
+
 	g_mutex_unlock (thread_data->mutex);
 	return thread_data;
 }
@@ -677,6 +821,7 @@ gcr_mock_prompter_start (void)
 	running = g_new0 (ThreadData, 1);
 	running->mutex = g_mutex_new ();
 	running->start_cond = g_cond_new ();
+	g_queue_init (&running->responses);
 
 	g_mutex_lock (running->mutex);
 	running->thread = g_thread_create (mock_prompter_thread, running, TRUE, &error);
@@ -707,6 +852,9 @@ gcr_mock_prompter_stop (void)
 	check = g_thread_join (running->thread);
 	g_assert (check == running);
 
+	g_queue_foreach (&running->responses, (GFunc)mock_response_free, NULL);
+	g_queue_clear (&running->responses);
+
 	g_cond_free (running->start_cond);
 	g_mutex_free (running->mutex);
 	g_free (running);
diff --git a/gcr/gcr-mock-prompter.h b/gcr/gcr-mock-prompter.h
index f2068ec..3542250 100644
--- a/gcr/gcr-mock-prompter.h
+++ b/gcr/gcr-mock-prompter.h
@@ -38,7 +38,7 @@ const gchar *        gcr_mock_prompter_start                     (void);
 
 void                 gcr_mock_prompter_stop                      (void);
 
-gboolean             gcr_mock_prompter_get_showing               (void);
+gboolean             gcr_mock_prompter_is_prompting              (void);
 
 guint                gcr_mock_prompter_get_delay_msec            (void);
 
@@ -55,6 +55,8 @@ void                 gcr_mock_prompter_expect_password_ok        (const gchar *p
 
 void                 gcr_mock_prompter_expect_password_cancel    (void);
 
+gboolean             gcr_mock_prompter_is_expecting              (void);
+
 G_END_DECLS
 
 #endif /* __GCR_MOCK_PROMPTER_H__ */
diff --git a/gcr/gcr-prompt-dialog.c b/gcr/gcr-prompt-dialog.c
new file mode 100644
index 0000000..3a053b1
--- /dev/null
+++ b/gcr/gcr-prompt-dialog.c
@@ -0,0 +1,808 @@
+/*
+ * 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"
+
+#define DEBUG_FLAG GCR_DEBUG_PROMPT
+#include "gcr-debug.h"
+#include "gcr-prompt.h"
+#include "gcr-prompt-dialog.h"
+#include "gcr-secure-entry-buffer.h"
+
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <glib/gi18n.h>
+
+#define LOG_ERRORS 1
+#define GRAB_KEYBOARD 1
+
+typedef enum {
+	PROMPT_NONE,
+	PROMPT_CONFIRMING,
+	PROMPT_PASSWORDING
+} PromptMode;
+
+enum {
+	PROP_0,
+	PROP_TITLE,
+	PROP_MESSAGE,
+	PROP_DESCRIPTION,
+	PROP_WARNING,
+	PROP_CHOICE_LABEL,
+	PROP_CHOICE_CHOSEN,
+	PROP_PASSWORD_NEW,
+	PROP_PASSWORD_STRENGTH,
+	PROP_CALLER_WINDOW,
+
+	PROP_PASSWORD_VISIBLE,
+	PROP_CONFIRM_VISIBLE,
+	PROP_WARNING_VISIBLE,
+	PROP_CHOICE_VISIBLE,
+};
+
+struct _GcrPromptDialogPrivate {
+	gchar *title;
+	gchar *message;
+	gchar *description;
+	gchar *warning;
+	gchar *choice_label;
+	gboolean choice_chosen;
+	gboolean password_new;
+	guint password_strength;
+	gchar *caller_window;
+
+	GSimpleAsyncResult *async_result;
+	GcrPromptReply last_reply;
+	GtkWidget *spinner;
+	GtkWidget *image;
+	GtkEntryBuffer *password_buffer;
+	GtkEntryBuffer *confirm_buffer;
+	PromptMode mode;
+	GdkDevice *grabbed_device;
+	gulong grab_broken_id;
+};
+
+static void     gcr_prompt_dialog_prompt_iface       (GcrPromptIface *iface);
+
+static gboolean ungrab_keyboard                      (GtkWidget *win,
+                                                      GdkEvent *event,
+                                                      gpointer unused);
+
+G_DEFINE_TYPE_WITH_CODE (GcrPromptDialog, gcr_prompt_dialog, GTK_TYPE_DIALOG,
+                         G_IMPLEMENT_INTERFACE (GCR_TYPE_PROMPT, gcr_prompt_dialog_prompt_iface);
+);
+
+static void
+update_transient_for (GcrPromptDialog *self)
+{
+	GdkDisplay *display;
+	GdkWindow *transient_for;
+	GdkWindow *window;
+	gint64 handle;
+	gchar *end;
+
+	if (self->pv->caller_window == NULL || g_str_equal (self->pv->caller_window, "")) {
+		gtk_window_set_modal (GTK_WINDOW (self), FALSE);
+		return;
+	}
+
+	window = gtk_widget_get_window (GTK_WIDGET (self));
+	if (window == NULL)
+		return;
+
+	handle = g_ascii_strtoll (self->pv->caller_window, &end, 10);
+	if (!end || *end != '\0') {
+		g_warning ("couldn't parse caller-window property: %s", self->pv->caller_window);
+		return;
+	}
+
+	display = gtk_widget_get_display (GTK_WIDGET (self));
+	transient_for = gdk_x11_window_foreign_new_for_display (display, (Window)handle);
+	if (transient_for == NULL) {
+		g_warning ("caller-window property doesn't represent a window on current display: %s",
+		           self->pv->caller_window);
+	} else {
+		gdk_window_set_transient_for (window, transient_for);
+		g_object_unref (transient_for);
+	}
+
+	gtk_window_set_modal (GTK_WINDOW (self), TRUE);
+}
+
+static void
+gcr_prompt_dialog_init (GcrPromptDialog *self)
+{
+	self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_PROMPT_DIALOG,
+	                                        GcrPromptDialogPrivate);
+}
+
+static void
+gcr_prompt_dialog_set_property (GObject *obj,
+                                guint prop_id,
+                                const GValue *value,
+                                GParamSpec *pspec)
+{
+	GcrPromptDialog *self = GCR_PROMPT_DIALOG (obj);
+
+	switch (prop_id) {
+	case PROP_TITLE:
+		g_free (self->pv->title);
+		self->pv->title = g_value_dup_string (value);
+		g_object_notify (obj, "title");
+		break;
+	case PROP_MESSAGE:
+		g_free (self->pv->message);
+		self->pv->message = g_value_dup_string (value);
+		g_object_notify (obj, "message");
+		break;
+	case PROP_DESCRIPTION:
+		g_free (self->pv->description);
+		self->pv->description = g_value_dup_string (value);
+		g_object_notify (obj, "description");
+		break;
+	case PROP_WARNING:
+		g_free (self->pv->warning);
+		self->pv->warning = g_value_dup_string (value);
+		g_object_notify (obj, "warning");
+		break;
+	case PROP_CHOICE_LABEL:
+		g_free (self->pv->choice_label);
+		self->pv->choice_label = g_value_dup_string (value);
+		g_object_notify (obj, "choice-label");
+		break;
+	case PROP_PASSWORD_NEW:
+		self->pv->password_new = g_value_get_boolean (value);
+		break;
+	case PROP_CALLER_WINDOW:
+		g_free (self->pv->caller_window);
+		self->pv->caller_window = g_value_dup_string (value);
+		update_transient_for (self);
+		g_object_notify (obj, "caller-window");
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gcr_prompt_dialog_get_property (GObject *obj,
+                                guint prop_id,
+                                GValue *value,
+                                GParamSpec *pspec)
+{
+	GcrPromptDialog *self = GCR_PROMPT_DIALOG (obj);
+
+	switch (prop_id) {
+	case PROP_TITLE:
+		g_value_set_string (value, self->pv->title);
+		break;
+	case PROP_MESSAGE:
+		g_value_set_string (value, self->pv->message);
+		break;
+	case PROP_DESCRIPTION:
+		g_value_set_string (value, self->pv->description);
+		break;
+	case PROP_WARNING:
+		g_value_set_string (value, self->pv->warning);
+		break;
+	case PROP_CHOICE_LABEL:
+		g_value_set_string (value, self->pv->choice_label);
+		break;
+	case PROP_CHOICE_CHOSEN:
+		g_value_set_boolean (value, self->pv->choice_chosen);
+		break;
+	case PROP_PASSWORD_NEW:
+		g_value_set_boolean (value, self->pv->password_new);
+		break;
+	case PROP_PASSWORD_STRENGTH:
+		g_value_set_uint (value, self->pv->password_strength);
+		break;
+	case PROP_CALLER_WINDOW:
+		g_value_set_string (value, self->pv->caller_window);
+		break;
+	case PROP_PASSWORD_VISIBLE:
+		g_value_set_boolean (value, self->pv->mode == PROMPT_PASSWORDING);
+		break;
+	case PROP_CONFIRM_VISIBLE:
+		g_value_set_boolean (value, self->pv->password_new &&
+		                            self->pv->mode == PROMPT_PASSWORDING);
+		break;
+	case PROP_WARNING_VISIBLE:
+		g_value_set_boolean (value, self->pv->warning && self->pv->warning[0]);
+		break;
+	case PROP_CHOICE_VISIBLE:
+		g_value_set_boolean (value, self->pv->choice_label && self->pv->choice_label[0]);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+
+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 const gchar*
+grab_status_message (GdkGrabStatus status)
+{
+	switch (status) {
+	case GDK_GRAB_SUCCESS:
+		g_return_val_if_reached ("");
+		break;
+	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)
+{
+	ungrab_keyboard (widget, (GdkEvent *)event, NULL);
+	return TRUE;
+}
+
+static gboolean
+grab_keyboard (GtkWidget *widget,
+               GdkEvent *event,
+               gpointer user_data)
+{
+	GcrPromptDialog *self = GCR_PROMPT_DIALOG (user_data);
+	GdkGrabStatus status;
+	guint32 at;
+	GdkDevice *device = NULL;
+	GdkDeviceManager *manager;
+	GdkDisplay *display;
+	GList *devices, *l;
+
+	if (self->pv->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_APPLICATION, TRUE,
+	                          GDK_KEY_PRESS | GDK_KEY_RELEASE, NULL, at);
+	if (status == GDK_GRAB_SUCCESS) {
+		self->pv->grab_broken_id = g_signal_connect (widget, "grab-broken-event",
+		                                             G_CALLBACK (on_grab_broken), NULL);
+		gtk_device_grab_add (widget, device, TRUE);
+		self->pv->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;
+	GcrPromptDialog *self = GCR_PROMPT_DIALOG (user_data);
+
+	if (self->pv->grabbed_device) {
+		g_signal_handler_disconnect (widget, self->pv->grab_broken_id);
+		gdk_device_ungrab (self->pv->grabbed_device, at);
+		gtk_device_grab_remove (widget, self->pv->grabbed_device);
+		self->pv->grabbed_device = NULL;
+		self->pv->grab_broken_id = 0;
+	}
+
+	/* Always return false, so event is handled elsewhere */
+	return FALSE;
+}
+
+static gboolean
+window_state_changed (GtkWidget *win, GdkEventWindowState *event, gpointer data)
+{
+	GdkWindowState state = gdk_window_get_state (gtk_widget_get_window (win));
+
+	if (state & GDK_WINDOW_STATE_WITHDRAWN ||
+	    state & GDK_WINDOW_STATE_ICONIFIED ||
+	    state & GDK_WINDOW_STATE_FULLSCREEN ||
+	    state & GDK_WINDOW_STATE_MAXIMIZED)
+		ungrab_keyboard (win, (GdkEvent*)event, data);
+	else
+		grab_keyboard (win, (GdkEvent*)event, data);
+
+	return FALSE;
+}
+
+static void
+gcr_prompt_dialog_constructed (GObject *obj)
+{
+	GcrPromptDialog *self = GCR_PROMPT_DIALOG (obj);
+	GtkDialog *dialog;
+	PangoAttrList *attrs;
+	GtkWidget *widget;
+	GtkWidget *entry;
+	GtkWidget *content;
+	GtkGrid *grid;
+
+	G_OBJECT_CLASS (gcr_prompt_dialog_parent_class)->constructed (obj);
+
+	dialog = GTK_DIALOG (self);
+	gtk_dialog_add_buttons (dialog,
+	                        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+	                        _("Continue"), GTK_RESPONSE_OK,
+	                        NULL);
+
+	content = gtk_dialog_get_content_area (dialog);
+
+	grid = GTK_GRID (gtk_grid_new ());
+	gtk_container_set_border_width (GTK_CONTAINER (grid), 6);
+	gtk_widget_set_hexpand (GTK_WIDGET (grid), TRUE);
+	gtk_grid_set_column_spacing (grid, 12);
+	gtk_grid_set_row_spacing (grid, 6);
+
+	/* The prompt image */
+	self->pv->image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_AUTHENTICATION,
+	                                            GTK_ICON_SIZE_DIALOG);
+	gtk_widget_set_valign (self->pv->image, GTK_ALIGN_START);
+	gtk_grid_attach (grid, self->pv->image, -1, 0, 1, 4);
+	gtk_widget_show (self->pv->image);
+
+	/* The prompt spinner */
+	self->pv->spinner = gtk_spinner_new ();
+	gtk_widget_set_valign (self->pv->image, GTK_ALIGN_START);
+	gtk_grid_attach (grid, self->pv->spinner, -2, -1, 1, 4);
+	gtk_widget_show (self->pv->spinner);
+
+	/* The message 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);
+	gtk_widget_set_halign (widget, GTK_ALIGN_START);
+	gtk_widget_set_hexpand (widget, TRUE);
+	gtk_widget_set_margin_bottom (widget, 8);
+	g_object_bind_property (self, "message", 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 ("");
+	gtk_widget_set_halign (widget, GTK_ALIGN_START);
+	gtk_widget_set_hexpand (widget, TRUE);
+	gtk_widget_set_margin_bottom (widget, 4);
+	g_object_bind_property (self, "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:"));
+	gtk_widget_set_halign (widget, GTK_ALIGN_START);
+	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->pv->password_buffer = gcr_secure_entry_buffer_new ();
+	entry = gtk_entry_new_with_buffer (self->pv->password_buffer);
+	gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE);
+	gtk_widget_set_hexpand (widget, TRUE);
+	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:"));
+	gtk_widget_set_halign (widget, GTK_ALIGN_START);
+	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->pv->confirm_buffer = gcr_secure_entry_buffer_new ();
+	widget = gtk_entry_new_with_buffer (self->pv->password_buffer);
+	gtk_widget_set_hexpand (widget, TRUE);
+	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 ();
+	gtk_widget_set_hexpand (widget, TRUE);
+	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 (self, "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 (self, "choice-label", widget, "label", G_BINDING_DEFAULT);
+	g_object_bind_property (self, "choice-visible", widget, "visible", G_BINDING_DEFAULT);
+	g_object_bind_property (self, "choice-chosen", widget, "active", G_BINDING_BIDIRECTIONAL);
+	gtk_grid_attach (grid, widget, 0, 6, 2, 1);
+
+	gtk_container_add (GTK_CONTAINER (content), 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 gboolean
+handle_password_response (GcrPromptDialog *self)
+{
+	const gchar *password;
+	const gchar *confirm;
+	const gchar *env;
+	gint strength;
+
+	password = gtk_entry_buffer_get_text (self->pv->password_buffer);
+
+	/* Is it a new password? */
+	if (self->pv->password_new) {
+		confirm = gtk_entry_buffer_get_text (self->pv->confirm_buffer);
+
+		/* Do the passwords match? */
+		if (!g_str_equal (password, confirm)) {
+			gcr_prompt_set_warning (GCR_PROMPT (self), _("Passwords do not match."));
+			return FALSE;
+		}
+
+		/* Don't allow blank passwords if in paranoid mode */
+		env = g_getenv ("GNOME_KEYRING_PARANOID");
+		if (env && *env) {
+			gcr_prompt_set_warning (GCR_PROMPT (self), _("Password cannot be blank"));
+			return FALSE;
+		}
+	}
+
+	if (g_str_equal (password, ""))
+		strength = 0;
+	else
+		strength = 1;
+
+	self->pv->password_strength = strength;
+	g_object_notify (G_OBJECT (self), "password-strength");
+	return TRUE;
+}
+
+static void
+gcr_prompt_dialog_response (GtkDialog *dialog,
+                            gint response_id)
+{
+	GcrPromptDialog *self = GCR_PROMPT_DIALOG (dialog);
+	GSimpleAsyncResult *res;
+
+	g_return_if_fail (self->pv->mode != PROMPT_NONE);
+	g_return_if_fail (self->pv->async_result != NULL);
+
+	switch (response_id) {
+	case GTK_RESPONSE_OK:
+
+		switch (self->pv->mode) {
+		case PROMPT_PASSWORDING:
+			if (!handle_password_response (self))
+				return;
+			break;
+		default:
+			break;
+		}
+		self->pv->last_reply = GCR_PROMPT_REPLY_OK;
+		break;
+
+	default:
+		self->pv->last_reply = GCR_PROMPT_REPLY_CANCEL;
+		break;
+	}
+
+	gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
+	gtk_widget_hide (GTK_WIDGET (self->pv->image));
+	gtk_widget_show (GTK_WIDGET (self->pv->spinner));
+	self->pv->mode = PROMPT_NONE;
+
+	res = self->pv->async_result;
+	self->pv->async_result = NULL;
+
+	g_simple_async_result_complete (res);
+	g_object_unref (res);
+}
+
+static void
+gcr_prompt_dialog_dispose (GObject *obj)
+{
+	GcrPromptDialog *self = GCR_PROMPT_DIALOG (obj);
+
+	if (self->pv->async_result)
+		gcr_prompt_dialog_response (GTK_DIALOG (self), GTK_RESPONSE_DELETE_EVENT);
+	g_assert (self->pv->async_result == NULL);
+
+	ungrab_keyboard (GTK_WIDGET (self), NULL, self);
+	g_assert (self->pv->grabbed_device == NULL);
+
+	G_OBJECT_CLASS (gcr_prompt_dialog_parent_class)->dispose (obj);
+}
+
+static void
+gcr_prompt_dialog_finalize (GObject *obj)
+{
+	GcrPromptDialog *self = GCR_PROMPT_DIALOG (obj);
+
+	g_free (self->pv->title);
+	g_free (self->pv->message);
+	g_free (self->pv->description);
+	g_free (self->pv->warning);
+	g_free (self->pv->choice_label);
+	g_free (self->pv->caller_window);
+
+	g_object_unref (self->pv->password_buffer);
+	g_object_unref (self->pv->confirm_buffer);
+
+	G_OBJECT_CLASS (gcr_prompt_dialog_parent_class)->finalize (obj);
+}
+
+static void
+gcr_prompt_dialog_class_init (GcrPromptDialogClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	GtkDialogClass *dialog_class = GTK_DIALOG_CLASS (klass);
+
+	gobject_class->constructed = gcr_prompt_dialog_constructed;
+	gobject_class->get_property = gcr_prompt_dialog_get_property;
+	gobject_class->set_property = gcr_prompt_dialog_set_property;
+	gobject_class->dispose = gcr_prompt_dialog_dispose;
+	gobject_class->finalize = gcr_prompt_dialog_finalize;
+
+	dialog_class->response = gcr_prompt_dialog_response;
+
+	g_type_class_add_private (gobject_class, sizeof (GcrPromptDialogPrivate));
+
+	g_object_class_override_property (gobject_class, PROP_TITLE, "title");
+
+	g_object_class_override_property (gobject_class, PROP_MESSAGE, "message");
+
+	g_object_class_override_property (gobject_class, PROP_DESCRIPTION, "description");
+
+	g_object_class_override_property (gobject_class, PROP_WARNING, "warning");
+
+	g_object_class_override_property (gobject_class, PROP_PASSWORD_NEW, "password-new");
+
+	g_object_class_override_property (gobject_class, PROP_PASSWORD_STRENGTH, "password-strength");
+
+	g_object_class_override_property (gobject_class, PROP_CHOICE_LABEL, "choice-label");
+
+	g_object_class_override_property (gobject_class, PROP_CHOICE_CHOSEN, "choice-chosen");
+
+	g_object_class_override_property (gobject_class, PROP_CALLER_WINDOW, "caller-window");
+
+	g_object_class_install_property (gobject_class, PROP_PASSWORD_VISIBLE,
+	           g_param_spec_boolean ("password-visible", "Password visible", "Password field is visible",
+	                                 FALSE, G_PARAM_READABLE));
+
+	g_object_class_install_property (gobject_class, PROP_CONFIRM_VISIBLE,
+	           g_param_spec_boolean ("confirm-visible", "Confirm visible", "Confirm field is visible",
+	                                 FALSE, G_PARAM_READABLE));
+
+	g_object_class_install_property (gobject_class, PROP_WARNING_VISIBLE,
+	           g_param_spec_boolean ("warning-visible", "Warning visible", "Warning is visible",
+	                                 FALSE, G_PARAM_READABLE));
+
+	g_object_class_install_property (gobject_class, PROP_CHOICE_VISIBLE,
+	           g_param_spec_boolean ("choice-visible", "Choice visible", "Choice is visible",
+	                                 FALSE, G_PARAM_READABLE));
+}
+
+static void
+gcr_prompt_dialog_password_async (GcrPrompt *prompt,
+                                  GCancellable *cancellable,
+                                  GAsyncReadyCallback callback,
+                                  gpointer user_data)
+{
+	GcrPromptDialog *self = GCR_PROMPT_DIALOG (prompt);
+	GObject *obj;
+
+	if (self->pv->async_result != NULL) {
+		g_warning ("this prompt is already prompting");
+		return;
+	}
+
+	self->pv->mode = PROMPT_PASSWORDING;
+	self->pv->async_result = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
+	                                                    gcr_prompt_dialog_password_async);
+
+	gtk_image_set_from_stock (GTK_IMAGE (self->pv->image),
+	                          GTK_STOCK_DIALOG_AUTHENTICATION,
+	                          GTK_ICON_SIZE_DIALOG);
+	gtk_widget_set_sensitive (GTK_WIDGET (self), TRUE);
+	gtk_widget_show (self->pv->image);
+	gtk_widget_hide (self->pv->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");
+}
+
+static const gchar *
+gcr_prompt_dialog_password_finish (GcrPrompt *prompt,
+                                   GAsyncResult *result,
+                                   GError **error)
+{
+	GcrPromptDialog *self = GCR_PROMPT_DIALOG (prompt);
+
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (prompt),
+	                      gcr_prompt_dialog_password_async), NULL);
+
+	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+		return NULL;
+
+	if (self->pv->last_reply == GCR_PROMPT_REPLY_OK)
+		return gtk_entry_buffer_get_text (self->pv->password_buffer);
+	return NULL;
+}
+
+static void
+gcr_prompt_dialog_confirm_async (GcrPrompt *prompt,
+                                 GCancellable *cancellable,
+                                 GAsyncReadyCallback callback,
+                                 gpointer user_data)
+{
+	GcrPromptDialog *self = GCR_PROMPT_DIALOG (prompt);
+	GObject *obj;
+
+	if (self->pv->async_result != NULL) {
+		g_warning ("this prompt is already prompting");
+		return;
+	}
+
+	self->pv->mode = PROMPT_CONFIRMING;
+	self->pv->async_result = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
+	                                                    gcr_prompt_dialog_password_async);
+
+	gtk_image_set_from_stock (GTK_IMAGE (self->pv->image),
+	                          GTK_STOCK_DIALOG_QUESTION,
+	                          GTK_ICON_SIZE_DIALOG);
+	gtk_widget_set_sensitive (GTK_WIDGET (self), TRUE);
+	gtk_widget_show (self->pv->image);
+	gtk_widget_hide (self->pv->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");
+}
+
+static GcrPromptReply
+gcr_prompt_dialog_confirm_finish (GcrPrompt *prompt,
+                                  GAsyncResult *result,
+                                  GError **error)
+{
+	GcrPromptDialog *self = GCR_PROMPT_DIALOG (prompt);
+
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (prompt),
+	                      gcr_prompt_dialog_confirm_async), GCR_PROMPT_REPLY_CANCEL);
+
+	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+		return GCR_PROMPT_REPLY_CANCEL;
+
+	return self->pv->last_reply;
+}
+
+static void
+gcr_prompt_dialog_prompt_iface (GcrPromptIface *iface)
+{
+	iface->prompt_password_async = gcr_prompt_dialog_password_async;
+	iface->prompt_password_finish = gcr_prompt_dialog_password_finish;
+	iface->prompt_confirm_async = gcr_prompt_dialog_confirm_async;
+	iface->prompt_confirm_finish = gcr_prompt_dialog_confirm_finish;
+}
diff --git a/gcr/gcr-prompt-dialog.h b/gcr/gcr-prompt-dialog.h
new file mode 100644
index 0000000..7f04e59
--- /dev/null
+++ b/gcr/gcr-prompt-dialog.h
@@ -0,0 +1,61 @@
+/*
+ * 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_PROMPT_DIALOG_H__
+#define __GCR_PROMPT_DIALOG_H__
+
+#include <gtk/gtk.h>
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GCR_TYPE_PROMPT_DIALOG               (gcr_prompt_dialog_get_type ())
+#define GCR_PROMPT_DIALOG(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_PROMPT_DIALOG, GcrPromptDialog))
+#define GCR_PROMPT_DIALOG_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_PROMPT_DIALOG, GcrPromptDialogClass))
+#define GCR_IS_PROMPT_DIALOG(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_PROMPT_DIALOG))
+#define GCR_IS_PROMPT_DIALOG_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_PROMPT_DIALOG))
+#define GCR_PROMPT_DIALOG_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_PROMPT_DIALOG, GcrPromptDialogClass))
+
+typedef struct _GcrPromptDialog GcrPromptDialog;
+typedef struct _GcrPromptDialogClass GcrPromptDialogClass;
+typedef struct _GcrPromptDialogPrivate GcrPromptDialogPrivate;
+
+struct _GcrPromptDialog {
+	GtkDialog parent;
+	GcrPromptDialogPrivate *pv;
+};
+
+struct _GcrPromptDialogClass {
+	GtkDialogClass parent;
+};
+
+GType                gcr_prompt_dialog_get_type                  (void);
+
+G_END_DECLS
+
+#endif /* __GCR_PROMPT_DIALOG_H__ */
diff --git a/gcr/gcr-prompt.c b/gcr/gcr-prompt.c
new file mode 100644
index 0000000..7fa4c12
--- /dev/null
+++ b/gcr/gcr-prompt.c
@@ -0,0 +1,441 @@
+/*
+ * 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-prompt.h"
+
+typedef struct {
+	GAsyncResult *result;
+	GMainLoop *loop;
+	GMainContext *context;
+} RunClosure;
+
+typedef GcrPromptIface GcrPromptInterface;
+
+static void   gcr_prompt_default_init    (GcrPromptIface *iface);
+
+G_DEFINE_INTERFACE (GcrPrompt, gcr_prompt, G_TYPE_OBJECT);
+
+static void
+gcr_prompt_default_init (GcrPromptIface *iface)
+{
+	static gsize initialized = 0;
+
+	if (g_once_init_enter (&initialized)) {
+
+		g_object_interface_install_property (iface,
+		                g_param_spec_string ("title", "Title", "Prompt title",
+		                                     NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+		g_object_interface_install_property (iface,
+		                g_param_spec_string ("message", "Message", "Prompt message",
+		                                     NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+		g_object_interface_install_property (iface,
+		                g_param_spec_string ("description", "Description", "Prompt description",
+		                                     NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+		g_object_interface_install_property (iface,
+		                g_param_spec_string ("warning", "Warning", "Prompt warning",
+		                                     NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+		g_object_interface_install_property (iface,
+		               g_param_spec_boolean ("password-new", "Password new", "Whether prompting for a new password",
+		                                     FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+		g_object_interface_install_property (iface,
+		                   g_param_spec_int ("password-strength", "Password strength", "String of new password",
+		                                     0, G_MAXINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+		g_object_interface_install_property (iface,
+		                g_param_spec_string ("choice-label", "Choice label", "Label for prompt choice",
+		                                     NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+		g_object_interface_install_property (iface,
+		               g_param_spec_boolean ("choice-chosen", "Choice chosen", "Whether prompt choice is chosen",
+		                                     FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+		g_object_interface_install_property (iface,
+		                g_param_spec_string ("caller-window", "Caller window", "Window ID of application window requesting prompt",
+		                                     NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+		g_once_init_leave (&initialized, 1);
+	}
+}
+
+static void
+run_closure_end (gpointer data)
+{
+	RunClosure *closure = data;
+	g_clear_object (&closure->result);
+	g_main_loop_unref (closure->loop);
+	if (closure->context != NULL) {
+		g_main_context_pop_thread_default (closure->context);
+		g_main_context_unref (closure->context);
+	}
+	g_free (closure);
+}
+
+static RunClosure *
+run_closure_begin (GMainContext *context)
+{
+	RunClosure *closure = g_new0 (RunClosure, 1);
+	closure->loop = g_main_loop_new (context ? context : g_main_context_get_thread_default (), FALSE);
+	closure->result = NULL;
+
+	/* We assume ownership of context reference */
+	closure->context = context;
+	if (closure->context != NULL)
+		g_main_context_push_thread_default (closure->context);
+
+	return closure;
+}
+
+static void
+on_run_complete (GObject *source,
+                 GAsyncResult *result,
+                 gpointer user_data)
+{
+	RunClosure *closure = user_data;
+	g_return_if_fail (closure->result == NULL);
+	closure->result = g_object_ref (result);
+	g_main_loop_quit (closure->loop);
+}
+
+gchar *
+gcr_prompt_get_title (GcrPrompt *prompt)
+{
+	gchar *title = NULL;
+	g_object_get (prompt, "title", &title, NULL);
+	return title;
+}
+
+void
+gcr_prompt_set_title (GcrPrompt *prompt,
+                      const gchar *title)
+{
+	g_return_if_fail (GCR_IS_PROMPT (prompt));
+	g_object_set (prompt, "title", title, NULL);
+}
+
+gchar *
+gcr_prompt_get_message (GcrPrompt *prompt)
+{
+	gchar *message = NULL;
+	g_object_get (prompt, "message", &message, NULL);
+	return message;
+}
+
+void
+gcr_prompt_set_message (GcrPrompt *prompt,
+                        const gchar *message)
+{
+	g_return_if_fail (GCR_IS_PROMPT (prompt));
+	g_object_set (prompt, "message", message, NULL);
+}
+
+gchar *
+gcr_prompt_get_description (GcrPrompt *prompt)
+{
+	gchar *description = NULL;
+	g_object_get (prompt, "description", &description, NULL);
+	return description;
+}
+
+void
+gcr_prompt_set_description (GcrPrompt *prompt,
+                            const gchar *description)
+{
+	g_return_if_fail (GCR_IS_PROMPT (prompt));
+	g_object_set (prompt, "description", description, NULL);
+}
+
+gchar *
+gcr_prompt_get_warning (GcrPrompt *prompt)
+{
+	gchar *warning = NULL;
+	g_object_get (prompt, "warning", &warning, NULL);
+	return warning;
+}
+
+void
+gcr_prompt_set_warning (GcrPrompt *prompt,
+                        const gchar *warning)
+{
+	g_return_if_fail (GCR_IS_PROMPT (prompt));
+	g_object_set (prompt, "warning", warning, NULL);
+}
+
+gchar *
+gcr_prompt_get_choice_label (GcrPrompt *prompt)
+{
+	gchar *choice_label = NULL;
+	g_object_get (prompt, "choice-label", &choice_label, NULL);
+	return choice_label;
+}
+
+void
+gcr_prompt_set_choice_label (GcrPrompt *prompt,
+                             const gchar *choice_label)
+{
+	g_return_if_fail (GCR_IS_PROMPT (prompt));
+	g_object_set (prompt, "choice-label", choice_label, NULL);
+}
+
+gboolean
+gcr_prompt_get_choice_chosen (GcrPrompt *prompt)
+{
+	gboolean choice_chosen;
+	g_object_get (prompt, "choice-chosen", &choice_chosen, NULL);
+	return choice_chosen;
+}
+
+void
+gcr_prompt_set_choice_chosen (GcrPrompt *prompt,
+                              gboolean chosen)
+{
+	g_return_if_fail (GCR_IS_PROMPT (prompt));
+	g_object_set (prompt, "choice-chosen", chosen, NULL);
+}
+
+gboolean
+gcr_prompt_get_password_new (GcrPrompt *prompt)
+{
+	gboolean password_new;
+	g_object_get (prompt, "password-new", &password_new, NULL);
+	return password_new;
+}
+
+void
+gcr_prompt_set_password_new (GcrPrompt *prompt,
+                             gboolean new_password)
+{
+	g_return_if_fail (GCR_IS_PROMPT (prompt));
+	g_object_set (prompt, "password-new", new_password, NULL);
+}
+
+gint
+gcr_prompt_get_password_strength (GcrPrompt *prompt)
+{
+	gboolean password_strength;
+	g_object_get (prompt, "password-strength", &password_strength, NULL);
+	return password_strength;
+}
+
+gchar *
+gcr_prompt_get_caller_window (GcrPrompt *prompt)
+{
+	gchar *caller_window = NULL;
+	g_object_get (prompt, "caller-window", &caller_window, NULL);
+	return caller_window;
+}
+
+void
+gcr_prompt_set_caller_window (GcrPrompt *prompt,
+                              const gchar *window_id)
+{
+	g_return_if_fail (GCR_IS_PROMPT (prompt));
+	g_object_set (prompt, "caller-window", window_id, NULL);
+}
+
+void
+gcr_prompt_password_async (GcrPrompt *prompt,
+                           GCancellable *cancellable,
+                           GAsyncReadyCallback callback,
+                           gpointer user_data)
+{
+	GcrPromptIface *iface;
+
+	g_return_if_fail (GCR_IS_PROMPT (prompt));
+	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+	iface = GCR_PROMPT_GET_INTERFACE (prompt);
+	g_return_if_fail (iface->prompt_password_async);
+
+	(iface->prompt_password_async) (prompt, cancellable, callback, user_data);
+}
+
+const gchar *
+gcr_prompt_password_finish (GcrPrompt *prompt,
+                            GAsyncResult *result,
+                            GError **error)
+{
+	GcrPromptIface *iface;
+
+	g_return_val_if_fail (GCR_IS_PROMPT (prompt), NULL);
+	g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+	iface = GCR_PROMPT_GET_INTERFACE (prompt);
+	g_return_val_if_fail (iface->prompt_password_async, NULL);
+
+	return (iface->prompt_password_finish) (prompt, result, error);
+}
+
+const gchar *
+gcr_prompt_password (GcrPrompt *prompt,
+                     GCancellable *cancellable,
+                     GError **error)
+{
+	RunClosure *closure;
+	const gchar *reply;
+
+	g_return_val_if_fail (GCR_IS_PROMPT (prompt), NULL);
+	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
+	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+	closure = run_closure_begin (g_main_context_new ());
+
+	gcr_prompt_password_async (prompt, cancellable, on_run_complete, closure);
+
+	g_main_loop_run (closure->loop);
+
+	reply = gcr_prompt_password_finish (prompt, closure->result, error);
+	run_closure_end (closure);
+
+	return reply;
+}
+
+const gchar *
+gcr_prompt_password_run (GcrPrompt *prompt,
+                         GCancellable *cancellable,
+                         GError **error)
+{
+	RunClosure *closure;
+	const gchar *reply;
+
+	g_return_val_if_fail (GCR_IS_PROMPT (prompt), NULL);
+	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
+	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+	closure = run_closure_begin (NULL);
+
+	gcr_prompt_password_async (prompt, cancellable, on_run_complete, closure);
+
+	g_main_loop_run (closure->loop);
+
+	reply = gcr_prompt_password_finish (prompt, closure->result, error);
+	run_closure_end (closure);
+
+	return reply;
+}
+
+void
+gcr_prompt_confirm_async (GcrPrompt *prompt,
+                          GCancellable *cancellable,
+                          GAsyncReadyCallback callback,
+                          gpointer user_data)
+{
+	GcrPromptIface *iface;
+
+	g_return_if_fail (GCR_IS_PROMPT (prompt));
+	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+	iface = GCR_PROMPT_GET_INTERFACE (prompt);
+	g_return_if_fail (iface->prompt_confirm_async);
+
+	(iface->prompt_confirm_async) (prompt, cancellable, callback, user_data);
+}
+
+GcrPromptReply
+gcr_prompt_confirm_finish (GcrPrompt *prompt,
+                           GAsyncResult *result,
+                           GError **error)
+{
+	GcrPromptIface *iface;
+
+	g_return_val_if_fail (GCR_IS_PROMPT (prompt), GCR_PROMPT_REPLY_CANCEL);
+	g_return_val_if_fail (G_IS_ASYNC_RESULT (result), GCR_PROMPT_REPLY_CANCEL);
+	g_return_val_if_fail (error == NULL || *error == NULL, GCR_PROMPT_REPLY_CANCEL);
+
+	iface = GCR_PROMPT_GET_INTERFACE (prompt);
+	g_return_val_if_fail (iface->prompt_confirm_async, GCR_PROMPT_REPLY_CANCEL);
+
+	return (iface->prompt_confirm_finish) (prompt, result, error);
+}
+
+GcrPromptReply
+gcr_prompt_confirm (GcrPrompt *prompt,
+                    GCancellable *cancellable,
+                    GError **error)
+{
+	RunClosure *closure;
+	GcrPromptReply reply;
+
+	g_return_val_if_fail (GCR_IS_PROMPT (prompt), GCR_PROMPT_REPLY_CANCEL);
+	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), GCR_PROMPT_REPLY_CANCEL);
+	g_return_val_if_fail (error == NULL || *error == NULL, GCR_PROMPT_REPLY_CANCEL);
+
+	closure = run_closure_begin (g_main_context_new ());
+
+	gcr_prompt_confirm_async (prompt, cancellable, on_run_complete, closure);
+
+	g_main_loop_run (closure->loop);
+
+	reply = gcr_prompt_confirm_finish (prompt, closure->result, error);
+	run_closure_end (closure);
+
+	return reply;
+}
+
+/**
+ * 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
+ */
+GcrPromptReply
+gcr_prompt_confirm_run (GcrPrompt *prompt,
+                        GCancellable *cancellable,
+                        GError **error)
+{
+	RunClosure *closure;
+	GcrPromptReply reply;
+
+	g_return_val_if_fail (GCR_IS_PROMPT (prompt), GCR_PROMPT_REPLY_CANCEL);
+	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), GCR_PROMPT_REPLY_CANCEL);
+	g_return_val_if_fail (error == NULL || *error == NULL, GCR_PROMPT_REPLY_CANCEL);
+
+	closure = run_closure_begin (NULL);
+
+	gcr_prompt_confirm_async (prompt, cancellable, on_run_complete, closure);
+
+	g_main_loop_run (closure->loop);
+
+	reply = gcr_prompt_confirm_finish (prompt, closure->result, error);
+	run_closure_end (closure);
+
+	return reply;
+}
diff --git a/gcr/gcr-prompt.h b/gcr/gcr-prompt.h
new file mode 100644
index 0000000..a115704
--- /dev/null
+++ b/gcr/gcr-prompt.h
@@ -0,0 +1,152 @@
+/*
+ * 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_PROMPT_H__
+#define __GCR_PROMPT_H__
+
+#include "gcr-types.h"
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+	GCR_PROMPT_REPLY_CANCEL = 0,
+	GCR_PROMPT_REPLY_OK = 1,
+} GcrPromptReply;
+
+#define GCR_TYPE_PROMPT                 (gcr_prompt_get_type ())
+#define GCR_PROMPT(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_PROMPT, GcrPrompt))
+#define GCR_IS_PROMPT(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_PROMPT))
+#define GCR_PROMPT_GET_INTERFACE(inst)  (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GCR_TYPE_PROMPT, GcrPromptIface))
+
+typedef struct _GcrPrompt GcrPrompt;
+typedef struct _GcrPromptIface GcrPromptIface;
+
+struct _GcrPromptIface {
+	GTypeInterface parent_class;
+
+	void               (* prompt_password_async)    (GcrPrompt *prompt,
+	                                                 GCancellable *cancellable,
+	                                                 GAsyncReadyCallback callback,
+	                                                 gpointer user_data);
+
+	const gchar *      (* prompt_password_finish)   (GcrPrompt *prompt,
+	                                                 GAsyncResult *result,
+	                                                 GError **error);
+
+	void               (* prompt_confirm_async)     (GcrPrompt *prompt,
+	                                                 GCancellable *cancellable,
+	                                                 GAsyncReadyCallback callback,
+	                                                 gpointer user_data);
+
+	GcrPromptReply     (* prompt_confirm_finish)    (GcrPrompt *prompt,
+	                                                 GAsyncResult *result,
+	                                                 GError **error);
+};
+
+GType                gcr_prompt_get_type                  (void);
+
+gchar *              gcr_prompt_get_title                 (GcrPrompt *prompt);
+
+void                 gcr_prompt_set_title                 (GcrPrompt *prompt,
+                                                           const gchar *title);
+
+gchar *              gcr_prompt_get_message               (GcrPrompt *prompt);
+
+void                 gcr_prompt_set_message               (GcrPrompt *prompt,
+                                                           const gchar *message);
+
+gchar *              gcr_prompt_get_description           (GcrPrompt *prompt);
+
+void                 gcr_prompt_set_description           (GcrPrompt *prompt,
+                                                           const gchar *description);
+
+gchar *              gcr_prompt_get_warning               (GcrPrompt *prompt);
+
+void                 gcr_prompt_set_warning               (GcrPrompt *prompt,
+                                                           const gchar *warning);
+
+gchar *              gcr_prompt_get_choice_label          (GcrPrompt *prompt);
+
+void                 gcr_prompt_set_choice_label          (GcrPrompt *prompt,
+                                                           const gchar *warning);
+
+gboolean             gcr_prompt_get_choice_chosen         (GcrPrompt *prompt);
+
+void                 gcr_prompt_set_choice_chosen         (GcrPrompt *prompt,
+                                                                  gboolean chosen);
+
+gboolean             gcr_prompt_get_password_new          (GcrPrompt *prompt);
+
+void                 gcr_prompt_set_password_new          (GcrPrompt *prompt,
+                                                           gboolean new_password);
+
+gint                 gcr_prompt_get_password_strength     (GcrPrompt *prompt);
+
+gchar *              gcr_prompt_get_caller_window         (GcrPrompt *prompt);
+
+void                 gcr_prompt_set_caller_window         (GcrPrompt *prompt,
+                                                           const gchar *window_id);
+
+void                 gcr_prompt_password_async            (GcrPrompt *prompt,
+                                                           GCancellable *cancellable,
+                                                           GAsyncReadyCallback callback,
+                                                           gpointer user_data);
+
+const gchar *        gcr_prompt_password_finish           (GcrPrompt *prompt,
+                                                           GAsyncResult *result,
+                                                           GError **error);
+
+const gchar *        gcr_prompt_password                  (GcrPrompt *prompt,
+                                                           GCancellable *cancellable,
+                                                           GError **error);
+
+const gchar *        gcr_prompt_password_run              (GcrPrompt *prompt,
+                                                           GCancellable *cancellable,
+                                                           GError **error);
+
+void                 gcr_prompt_confirm_async             (GcrPrompt *prompt,
+                                                           GCancellable *cancellable,
+                                                           GAsyncReadyCallback callback,
+                                                           gpointer user_data);
+
+GcrPromptReply       gcr_prompt_confirm_finish            (GcrPrompt *prompt,
+                                                           GAsyncResult *result,
+                                                           GError **error);
+
+GcrPromptReply       gcr_prompt_confirm                   (GcrPrompt *prompt,
+                                                           GCancellable *cancellable,
+                                                           GError **error);
+
+GcrPromptReply       gcr_prompt_confirm_run               (GcrPrompt *prompt,
+                                                           GCancellable *cancellable,
+                                                           GError **error);
+
+G_END_DECLS
+
+#endif /* __GCR_PROMPT_H__ */
diff --git a/gcr/gcr-prompter-tool.c b/gcr/gcr-prompter-tool.c
index 86ca293..089dc65 100644
--- a/gcr/gcr-prompter-tool.c
+++ b/gcr/gcr-prompter-tool.c
@@ -38,51 +38,13 @@
 #include <stdlib.h>
 #include <string.h>
 
-#define LOG_ERRORS 1
-#define GRAB_KEYBOARD 1
 #define QUIT_TIMEOUT 10
 
 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;
-	guint quit_timeout;
-} GcrPrompterDialog;
-
-typedef struct {
-	GtkDialogClass parent;
-} GcrPrompterDialogClass;
-
-G_DEFINE_TYPE (GcrPrompterDialog, gcr_prompter_dialog, GTK_TYPE_DIALOG);
-
+#if 0
 static gboolean
-on_timeout_quit ()
+on_timeout_quit (gpointer unused)
 {
 	gtk_main_quit ();
 	return FALSE; /* Don't run again */
@@ -105,555 +67,13 @@ stop_timeout (GcrPrompterDialog *self)
 		g_source_remove (self->quit_timeout);
 	self->quit_timeout = 0;
 }
-
-static void
-on_open_prompt (GcrSystemPrompter *prompter,
-                gpointer user_data)
-{
-	GcrPrompterDialog *self = GCR_PROMPTER_DIALOG (user_data);
-	gtk_widget_show (GTK_WIDGET (self));
-	stop_timeout (self);
-}
-
-static void
-on_close_prompt (GcrSystemPrompter *prompter,
-                 gpointer user_data)
-{
-	GcrPrompterDialog *self = GCR_PROMPTER_DIALOG (user_data);
-	gtk_widget_hide (GTK_WIDGET (self));
-	start_timeout (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));
-	self->mode = PROMPT_NONE;
-}
-
-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_realize (GtkWidget *widget)
-{
-	gboolean modal = FALSE;
-	const gchar *value;
-	gulong caller_window_id;
-	GdkWindow *caller_window;
-	GdkWindow *self_window;
-	GdkDisplay *display;
-	gchar *end;
-
-	GTK_WIDGET_CLASS (gcr_prompter_dialog_parent_class)->realize (widget);
-
-	value= gcr_system_prompter_get_caller_window (the_prompter);
-	if (value) {
-		caller_window_id = strtoul (value, &end, 10);
-		if (caller_window_id && end && end[0] == '\0') {
-			display = gtk_widget_get_display (widget);
-			caller_window = gdk_x11_window_foreign_new_for_display (display, caller_window_id);
-			if (caller_window) {
-				self_window = gtk_widget_get_window (widget);
-				gdk_window_set_transient_for (self_window, caller_window);
-				g_object_unref (caller_window);
-				modal = TRUE;
-			}
-		}
-	}
-
-	gtk_window_set_modal (GTK_WINDOW (widget), modal);
-}
-
-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;
-	GtkWidget *content;
-	GtkGrid *grid;
-
-	g_assert (GCR_IS_SYSTEM_PROMPTER (the_prompter));
-
-	g_signal_connect (the_prompter, "open", G_CALLBACK (on_open_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, "close", G_CALLBACK (on_close_prompt), self);
-
-	dialog = GTK_DIALOG (self);
-	gtk_dialog_add_buttons (dialog,
-	                        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-	                        _("Continue"), GTK_RESPONSE_OK,
-	                        NULL);
-
-	content = gtk_dialog_get_content_area (dialog);
-
-	grid = GTK_GRID (gtk_grid_new ());
-	gtk_container_set_border_width (GTK_CONTAINER (grid), 6);
-	gtk_widget_set_hexpand (GTK_WIDGET (grid), TRUE);
-	gtk_grid_set_column_spacing (grid, 12);
-	gtk_grid_set_row_spacing (grid, 6);
-
-	/* The prompt image */
-	self->image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_AUTHENTICATION,
-	                                        GTK_ICON_SIZE_DIALOG);
-	gtk_widget_set_valign (self->image, GTK_ALIGN_START);
-	gtk_grid_attach (grid, self->image, -1, 0, 1, 4);
-	gtk_widget_show (self->image);
-
-	/* The prompt spinner */
-	self->spinner = gtk_spinner_new ();
-	gtk_widget_set_valign (self->image, GTK_ALIGN_START);
-	gtk_grid_attach (grid, self->spinner, -2, -1, 1, 4);
-	gtk_widget_show (self->spinner);
-
-	/* The message 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);
-	gtk_widget_set_halign (widget, GTK_ALIGN_START);
-	gtk_widget_set_hexpand (widget, TRUE);
-	gtk_widget_set_margin_bottom (widget, 8);
-	g_object_bind_property (the_prompter, "message", 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 ("");
-	gtk_widget_set_halign (widget, GTK_ALIGN_START);
-	gtk_widget_set_hexpand (widget, TRUE);
-	gtk_widget_set_margin_bottom (widget, 4);
-	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:"));
-	gtk_widget_set_halign (widget, GTK_ALIGN_START);
-	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);
-	gtk_widget_set_hexpand (widget, TRUE);
-	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:"));
-	gtk_widget_set_halign (widget, GTK_ALIGN_START);
-	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_widget_set_hexpand (widget, TRUE);
-	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 ();
-	gtk_widget_set_hexpand (widget, TRUE);
-	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 (content), 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);
-	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
-
-	widget_class->realize = gcr_prompter_dialog_realize;
-
-	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));
-}
+#endif
 
 static void
 on_bus_acquired (GDBusConnection *connection,
                  const gchar *name,
                  gpointer user_data)
 {
-	gcr_system_prompter_register (the_prompter, connection);
 }
 
 static void
@@ -661,7 +81,7 @@ on_name_acquired (GDBusConnection *connection,
                   const gchar *name,
                   gpointer user_data)
 {
-
+	gcr_system_prompter_register (the_prompter, connection);
 }
 
 static void
@@ -669,13 +89,13 @@ on_name_lost (GDBusConnection *connection,
               const gchar *name,
               gpointer user_data)
 {
+	gcr_system_prompter_unregister (the_prompter, FALSE);
 	gtk_main_quit ();
 }
 
 int
 main (int argc, char *argv[])
 {
-	GtkDialog *dialog;
 	guint owner_id;
 
 	g_type_init ();
@@ -692,8 +112,8 @@ main (int argc, char *argv[])
 	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
 #endif
 
-	the_prompter = gcr_system_prompter_new ();
-	dialog = g_object_new (GCR_TYPE_PROMPTER_DIALOG, NULL);
+	the_prompter = gcr_system_prompter_new (GCR_SYSTEM_PROMPTER_SINGLE,
+	                                        GCR_TYPE_PROMPT_DIALOG);
 	owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
 	                           GCR_DBUS_PROMPTER_BUS_NAME,
 	                           G_BUS_NAME_OWNER_FLAGS_NONE,
@@ -707,7 +127,6 @@ main (int argc, char *argv[])
 
 	g_bus_unown_name (owner_id);
 	g_object_unref (the_prompter);
-	gtk_widget_destroy (GTK_WIDGET (dialog));
 
 	return 0;
 }
diff --git a/gcr/gcr-secret-exchange.c b/gcr/gcr-secret-exchange.c
index 43672f1..cb3941f 100644
--- a/gcr/gcr-secret-exchange.c
+++ b/gcr/gcr-secret-exchange.c
@@ -378,7 +378,10 @@ perform_decrypt (GcrSecretExchange *self,
  * @exchange: the string received
  *
  * Receive a string from the other side of secret exchange. This string will
- * have been created by gcr_secret_exchange_begin() or gcr_secret_exchange_send()
+ * have been created by gcr_secret_exchange_begin() or gcr_secret_exchange_send().
+ *
+ * After this call completes successfully the value returned from
+ * gcr_secret_exchange_get_secret() will have changed.
  *
  * Returns: whether the string was successfully parsed and received
  */
@@ -387,9 +390,9 @@ gcr_secret_exchange_receive (GcrSecretExchange *self,
                              const gchar *exchange)
 {
 	GcrSecretExchangeClass *klass;
+	gchar *secret = NULL;
+	gsize n_secret = 0;
 	GKeyFile *input;
-	gchar *secret;
-	gsize n_secret;
 	gboolean ret;
 
 	g_return_val_if_fail (GCR_IS_SECRET_EXCHANGE (self), FALSE);
@@ -422,16 +425,13 @@ gcr_secret_exchange_receive (GcrSecretExchange *self,
 			ret = FALSE;
 	}
 
-	if (ret && g_key_file_has_key (input, GCR_SECRET_EXCHANGE_PROTOCOL_1, "secret", NULL)) {
+	if (ret && g_key_file_has_key (input, GCR_SECRET_EXCHANGE_PROTOCOL_1, "secret", NULL))
+		ret = perform_decrypt (self, input, (guchar **)&secret, &n_secret);
 
-		/* Remember that this can return a NULL secret */
-		if (!perform_decrypt (self, input, (guchar **)&secret, &n_secret)) {
-			ret = FALSE;
-		} else {
-			egg_secure_free (self->pv->secret);
-			self->pv->secret = secret;
-			self->pv->n_secret = n_secret;
-		}
+	if (ret) {
+		egg_secure_free (self->pv->secret);
+		self->pv->secret = secret;
+		self->pv->n_secret = n_secret;
 	}
 
 	g_key_file_free (input);
diff --git a/gcr/gcr-system-prompt.c b/gcr/gcr-system-prompt.c
index 165f3e3..fc6f6ba 100644
--- a/gcr/gcr-system-prompt.c
+++ b/gcr/gcr-system-prompt.c
@@ -29,11 +29,14 @@
 #include "gcr-debug.h"
 #include "gcr-internal.h"
 #include "gcr-library.h"
+#include "gcr-prompt.h"
 #include "gcr-secret-exchange.h"
 #include "gcr-system-prompt.h"
 
 #include "egg/egg-error.h"
 
+#include <glib/gi18n.h>
+
 /**
  * SECTION:gcr-system-prompt
  * @title: GcrSystemPrompt
@@ -72,17 +75,24 @@ enum {
 
 struct _GcrSystemPromptPrivate {
 	gchar *prompter_bus_name;
-	GDBusConnection *connection;
 	GcrSecretExchange *exchange;
-	GHashTable *properties_to_write;
-	GHashTable *property_cache;
-	GDBusProxy *prompt_proxy;
-	gchar *prompt_path;
-	gboolean exchanged;
-	gboolean begun_prompting;
+	gboolean received;
+	GHashTable *properties;
+	GHashTable *dirty_properties;
 	gint timeout_seconds;
+
+	GDBusConnection *connection;
+	gboolean begun_prompting;
+	gboolean closed;
+	guint prompt_registered;
+	gchar *prompt_path;
+
+	GSimpleAsyncResult *pending;
+	gchar *last_response;
 };
 
+static void     gcr_system_prompt_prompt_iface         (GcrPromptIface *iface);
+
 static void     gcr_system_prompt_initable_iface       (GInitableIface *iface);
 
 static void     gcr_system_prompt_async_initable_iface (GAsyncInitableIface *iface);
@@ -91,10 +101,30 @@ static void     perform_init_async                     (GcrSystemPrompt *self,
                                                         GSimpleAsyncResult *res);
 
 G_DEFINE_TYPE_WITH_CODE (GcrSystemPrompt, gcr_system_prompt, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (GCR_TYPE_PROMPT, gcr_system_prompt_prompt_iface);
                          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 gint unique_prompt_id = 0;
+
+typedef struct {
+	GSource *timeout;
+	GMainContext *context;
+	GCancellable *cancellable;
+	gboolean waiting;
+} CallClosure;
+
+static void
+call_closure_free (gpointer data)
+{
+	CallClosure *closure = data;
+	if (closure->timeout)
+		g_source_destroy (closure->timeout);
+	g_clear_object (&closure->cancellable);
+	g_free (data);
+}
+
 static void
 gcr_system_prompt_init (GcrSystemPrompt *self)
 {
@@ -102,9 +132,94 @@ gcr_system_prompt_init (GcrSystemPrompt *self)
 	                                        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);
+	self->pv->properties = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+	                                              NULL, (GDestroyNotify)g_variant_unref);
+	self->pv->dirty_properties = g_hash_table_new (g_direct_hash, g_direct_equal);
+}
+
+static const gchar *
+prompt_get_string_property (GcrSystemPrompt *self,
+                            const gchar *property_name)
+{
+	GVariant *variant;
+	gconstpointer key;
+
+	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPT (self), NULL);
+
+	key = g_intern_string (property_name);
+	variant = g_hash_table_lookup (self->pv->properties, key);
+	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->properties, key, variant);
+	g_hash_table_insert (self->pv->dirty_properties, key, key);
+	g_object_notify (G_OBJECT (self), property_name);
+}
+
+static gint
+prompt_get_int_property (GcrSystemPrompt *self,
+                         const gchar *property_name)
+{
+	GVariant *variant;
+	gconstpointer key;
+
+	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPT (self), 0);
+
+	key = g_intern_string (property_name);
+	variant = g_hash_table_lookup (self->pv->properties, key);
+	if (variant != NULL)
+		return g_variant_get_int32 (variant);
+
+	return 0;
+}
+
+static gboolean
+prompt_get_boolean_property (GcrSystemPrompt *self,
+                             const gchar *property_name)
+{
+	GVariant *variant;
+	gconstpointer key;
+
+	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPT (self), FALSE);
+
+	key = g_intern_string (property_name);
+	variant = g_hash_table_lookup (self->pv->properties, key);
+	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->properties, key, variant);
+	g_hash_table_insert (self->pv->dirty_properties, key, key);
+	g_object_notify (G_OBJECT (self), property_name);
 }
 
 static void
@@ -121,34 +236,39 @@ gcr_system_prompt_set_property (GObject *obj,
 		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));
+		if (self->pv->exchange) {
+			g_warning ("The secret exchange is already in use, and cannot be changed");
+			return;
+		}
+		self->pv->exchange = g_value_dup_object (value);
+		g_object_notify (G_OBJECT (self), "secret-exchange");
 		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));
+		prompt_set_string_property (self, "title", g_value_get_string (value));
 		break;
 	case PROP_MESSAGE:
-		gcr_system_prompt_set_message (self, g_value_get_string (value));
+		prompt_set_string_property (self, "message", g_value_get_string (value));
 		break;
 	case PROP_DESCRIPTION:
-		gcr_system_prompt_set_description (self, g_value_get_string (value));
+		prompt_set_string_property (self, "description", g_value_get_string (value));
 		break;
 	case PROP_WARNING:
-		gcr_system_prompt_set_warning (self, g_value_get_string (value));
+		prompt_set_string_property (self, "warning", g_value_get_string (value));
 		break;
 	case PROP_PASSWORD_NEW:
-		gcr_system_prompt_set_password_new (self, g_value_get_boolean (value));
+		prompt_set_boolean_property (self, "password-new", g_value_get_boolean (value));
 		break;
 	case PROP_CHOICE_LABEL:
-		gcr_system_prompt_set_choice_label (self, g_value_get_string (value));
+		prompt_set_string_property (self, "choice-label", g_value_get_string (value));
 		break;
 	case PROP_CHOICE_CHOSEN:
-		gcr_system_prompt_set_choice_chosen (self, g_value_get_boolean (value));
+		prompt_set_boolean_property (self, "choice-chosen", g_value_get_boolean (value));
 		break;
 	case PROP_CALLER_WINDOW:
-		gcr_system_prompt_set_caller_window (self, g_value_get_string (value));
+		prompt_set_string_property (self, "caller-window", g_value_get_string (value));
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
@@ -172,28 +292,31 @@ gcr_system_prompt_get_property (GObject *obj,
 		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));
+		g_value_set_string (value, prompt_get_string_property (self, "title"));
+		break;
+	case PROP_MESSAGE:
+		g_value_set_string (value, prompt_get_string_property (self, "message"));
 		break;
 	case PROP_DESCRIPTION:
-		g_value_set_string (value, gcr_system_prompt_get_description (self));
+		g_value_set_string (value, prompt_get_string_property (self, "description"));
 		break;
 	case PROP_WARNING:
-		g_value_set_string (value, gcr_system_prompt_get_warning (self));
+		g_value_set_string (value, prompt_get_string_property (self, "warning"));
 		break;
 	case PROP_PASSWORD_NEW:
-		g_value_set_boolean (value, gcr_system_prompt_get_password_new (self));
+		g_value_set_boolean (value, prompt_get_boolean_property (self, "password-new"));
 		break;
 	case PROP_PASSWORD_STRENGTH:
-		g_value_set_int (value, gcr_system_prompt_get_password_strength (self));
+		g_value_set_int (value, prompt_get_int_property (self, "password-strength"));
 		break;
 	case PROP_CHOICE_LABEL:
-		g_value_set_string (value, gcr_system_prompt_get_choice_label (self));
+		g_value_set_string (value, prompt_get_string_property (self, "choice-label"));
 		break;
 	case PROP_CHOICE_CHOSEN:
-		g_value_set_boolean (value, gcr_system_prompt_get_choice_chosen (self));
+		g_value_set_boolean (value, prompt_get_boolean_property (self, "choice-chosen"));
 		break;
 	case PROP_CALLER_WINDOW:
-		g_value_set_string (value, gcr_system_prompt_get_caller_window (self));
+		g_value_set_string (value, prompt_get_string_property (self, "caller-window"));
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
@@ -205,9 +328,18 @@ static void
 gcr_system_prompt_constructed (GObject *obj)
 {
 	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (obj);
+	gint seed;
 
 	G_OBJECT_CLASS (gcr_system_prompt_parent_class)->constructed (obj);
 
+#if GLIB_CHECK_VERSION (2,29,90)
+	seed = g_atomic_int_add (&unique_prompt_id, 1);
+#else
+	seed = g_atomic_int_exchange_and_add (&unique_prompt_id, 1);
+#endif
+
+	self->pv->prompt_path = g_strdup_printf ("%s/p%d", GCR_DBUS_PROMPT_OBJECT_PREFIX, seed);
+
 	if (self->pv->prompter_bus_name == NULL)
 		self->pv->prompter_bus_name = g_strdup (GCR_DBUS_PROMPTER_BUS_NAME);
 }
@@ -219,30 +351,12 @@ gcr_system_prompt_dispose (GObject *obj)
 
 	g_clear_object (&self->pv->exchange);
 
-	if (self->pv->prompt_path) {
-		_gcr_debug ("closing prompt asynchronously: %s", self->pv->prompt_path);
+	_gcr_debug ("closing prompt asynchronously: %s", self->pv->prompt_path);
+	if (self->pv->connection)
+		gcr_system_prompt_close_async (self, NULL, NULL, NULL);
 
-		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 ("(o)", self->pv->prompt_path),
-		                        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_object_unref (self->pv->prompt_proxy);
-		self->pv->prompt_proxy = NULL;
-	}
-
-	g_clear_object (&self->pv->connection);
+	g_hash_table_remove_all (self->pv->properties);
+	g_hash_table_remove_all (self->pv->dirty_properties);
 
 	G_OBJECT_CLASS (gcr_system_prompt_parent_class)->dispose (obj);
 }
@@ -253,8 +367,8 @@ gcr_system_prompt_finalize (GObject *obj)
 	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (obj);
 
 	g_free (self->pv->prompter_bus_name);
-	g_hash_table_destroy (self->pv->properties_to_write);
-	g_hash_table_destroy (self->pv->property_cache);
+	g_hash_table_destroy (self->pv->properties);
+	g_hash_table_destroy (self->pv->dirty_properties);
 
 	G_OBJECT_CLASS (gcr_system_prompt_parent_class)->finalize (obj);
 }
@@ -284,41 +398,15 @@ gcr_system_prompt_class_init (GcrSystemPromptClass *klass)
 	            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));
+	g_object_class_override_property (gobject_class, PROP_TITLE, "title");
+	g_object_class_override_property (gobject_class, PROP_MESSAGE, "message");
+	g_object_class_override_property (gobject_class, PROP_DESCRIPTION, "description");
+	g_object_class_override_property (gobject_class, PROP_WARNING, "warning");
+	g_object_class_override_property (gobject_class, PROP_PASSWORD_NEW, "password-new");
+	g_object_class_override_property (gobject_class, PROP_PASSWORD_STRENGTH, "password-strength");
+	g_object_class_override_property (gobject_class, PROP_CHOICE_LABEL, "choice-label");
+	g_object_class_override_property (gobject_class, PROP_CHOICE_CHOSEN, "choice-chosen");
+	g_object_class_override_property (gobject_class, PROP_CALLER_WINDOW, "caller-window");
 }
 
 /**
@@ -342,317 +430,168 @@ gcr_system_prompt_get_secret_exchange (GcrSystemPrompt *self)
 	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)g_intern_string (property_name),
-			                     variant);
-	}
-
-	return variant;
-}
-
-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)
+update_properties_from_iter (GcrSystemPrompt *self,
+                             GVariantIter *iter)
 {
+	GObject *obj = G_OBJECT (self);
 	GVariant *variant;
+	GVariant *value;
+	GVariant *already;
 	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;
+	gchar *name;
+
+	g_object_freeze_notify (obj);
+	while (g_variant_iter_loop (iter, "{sv}", &name, &variant)) {
+		key = (gpointer)g_intern_string (name);
+		value = g_variant_get_variant (variant);
+		already = g_hash_table_lookup (self->pv->properties, key);
+		if (!already || !g_variant_equal (already, value)) {
+			g_hash_table_replace (self->pv->properties, key, g_variant_ref (value));
+			g_object_notify (obj, name);
+		}
+	}
+	g_object_thaw_notify (obj);
 }
 
 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_method_ready (GcrSystemPrompt *self,
+                     GDBusMethodInvocation *invocation,
+                     GVariant *parameters)
 {
-	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;
+	GcrSecretExchange *exchange;
+	GSimpleAsyncResult *res;
+	GVariantIter *iter;
+	gchar *received;
 
-	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPT (self), 0);
+	g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (self->pv->pending));
 
-	variant = lookup_property_in_caches (self, GCR_DBUS_PROMPT_PROPERTY_PASSWORD_STRENGTH);
-	if (variant != NULL)
-		return g_variant_get_int32 (variant);
+	g_free (self->pv->last_response);
+	g_variant_get (parameters, "(sa{sv}s)",
+	               &self->pv->last_response,
+	               &iter, &received);
 
-	return 0;
-}
+	g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
 
+	update_properties_from_iter (self, iter);
+	g_variant_iter_free (iter);
 
-const gchar *
-gcr_system_prompt_get_choice_label (GcrSystemPrompt *self)
-{
-	return prompt_get_string_property (self, GCR_DBUS_PROMPT_PROPERTY_CHOICE_LABEL);
-}
+	exchange = gcr_system_prompt_get_secret_exchange (self);
+	if (gcr_secret_exchange_receive (exchange, received))
+		self->pv->received = TRUE;
+	else
+		g_warning ("received invalid secret exchange string");
+	g_free (received);
 
-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");
+	res = g_object_ref (self->pv->pending);
+	g_clear_object (&self->pv->pending);
+	g_simple_async_result_set_op_res_gpointer (res, NULL, NULL);
+	g_simple_async_result_complete (res);
+	g_object_unref (res);
 }
 
-gboolean
-gcr_system_prompt_get_choice_chosen (GcrSystemPrompt *self)
+static void
+prompt_method_done (GcrSystemPrompt *self,
+                    GDBusMethodInvocation *invocation,
+                    GVariant *parameters)
 {
-	return prompt_get_boolean_property (self, GCR_DBUS_PROMPT_PROPERTY_CHOICE_CHOSEN);
-}
+	GSimpleAsyncResult *res;
 
-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");
-}
+	g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (self->pv->pending));
 
-const gchar *
-gcr_system_prompt_get_caller_window (GcrSystemPrompt *self)
-{
-	return prompt_get_string_property (self, GCR_DBUS_PROMPT_PROPERTY_CALLER_WINDOW);
-}
+	g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
 
-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");
+	res = g_object_ref (self->pv->pending);
+	g_clear_object (&self->pv->pending);
+	g_simple_async_result_set_op_res_gpointer (res, NULL, NULL);
+	g_simple_async_result_complete (res);
+	g_object_unref (res);
 }
 
-static gboolean
-gcr_system_prompt_real_init (GInitable *initable,
-                             GCancellable *cancellable,
-                             GError **error)
+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)
 {
-	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (initable);
-
-	/* 1. Connect to the session bus */
-	if (!self->pv->connection) {
+	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (user_data);
 
-		self->pv->connection = g_bus_get_sync (G_BUS_TYPE_SESSION,
-		                                       cancellable, error);
-		if (self->pv->connection == NULL) {
-			_gcr_debug ("failed to connect to bus: %s",
-			            egg_error_result_message (error));
-			return FALSE;
-		}
+	g_return_if_fail (method_name != NULL);
 
-		_gcr_debug ("connected to bus");
-	}
+	if (g_str_equal (method_name, GCR_DBUS_CALLBACK_METHOD_READY)) {
+		prompt_method_ready (self, invocation, parameters);
 
-	/* 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) {
-			_gcr_debug ("failed to open prompt %s: %s", self->pv->prompter_bus_name,
-			            egg_error_result_message (error));
-			return FALSE;
-		}
+	} else if (g_str_equal (method_name, GCR_DBUS_CALLBACK_METHOD_DONE)) {
+		prompt_method_done (self, invocation, parameters);
 
-		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);
-
-		_gcr_debug ("opened prompt %s: %s", self->pv->prompter_bus_name, self->pv->prompt_path);
-	}
-
-	/* 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) {
-			_gcr_debug ("failed to create prompt proxy: %s",
-			            egg_error_result_message (error));
-			return FALSE;
-		}
-
-		g_dbus_proxy_set_default_timeout (self->pv->prompt_proxy, G_MAXINT);
-		_gcr_debug ("created prompt proxy");
+	} else {
+		g_return_if_reached ();
 	}
+}
 
-	return TRUE;
+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)
+{
+	g_return_val_if_reached (NULL);
 }
 
-static void
-gcr_system_prompt_initable_iface (GInitableIface *iface)
+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)
 {
-	iface->init = gcr_system_prompt_real_init;
+	g_return_val_if_reached (FALSE);
 }
 
-typedef struct {
-	GCancellable *cancellable;
-	guint subscribe_sig;
-} InitClosure;
+
+static GDBusInterfaceVTable prompt_dbus_vtable = {
+	prompt_method_call,
+	prompt_get_property,
+	prompt_set_property,
+};
 
 static void
-init_closure_free (gpointer data)
+register_prompt_object (GcrSystemPrompt *self,
+                        GError **error)
 {
-	InitClosure *closure = data;
-	g_clear_object (&closure->cancellable);
-	g_free (closure);
+	GError *lerror = NULL;
+	guint id;
+
+	/*
+	 * The unregister/reregister allows us to register this under a whatever
+	 * GMainContext has been pushed as the thread default context.
+	 */
+
+	if (self->pv->prompt_registered)
+		g_dbus_connection_unregister_object (self->pv->connection,
+		                                     self->pv->prompt_registered);
+
+	id = g_dbus_connection_register_object (self->pv->connection,
+	                                        self->pv->prompt_path,
+	                                        _gcr_dbus_prompter_callback_interface_info (),
+	                                        &prompt_dbus_vtable,
+	                                        self, NULL, &lerror);
+	self->pv->prompt_registered = id;
+
+	if (lerror != NULL) {
+		g_warning ("error registering prompter %s", egg_error_message (lerror));
+		g_propagate_error (error, lerror);
+	}
 }
 
 static void
@@ -662,19 +601,29 @@ on_bus_connected (GObject *source,
 {
 	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
 	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (g_async_result_get_source_object (user_data));
+	CallClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
 	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);
+	if (error != NULL) {
+		_gcr_debug ("failed to connect to bus: %s", egg_error_message (error));
 
+	} else {
+		g_return_if_fail (self->pv->connection != NULL);
 		_gcr_debug ("connected to bus");
-		perform_init_async (self, res);
 
+		g_main_context_push_thread_default (closure->context);
+
+		register_prompt_object (self, &error);
+
+		g_main_context_pop_thread_default (closure->context);
+	}
+
+	if (error == NULL) {
+		perform_init_async (self, res);
 	} else {
-		_gcr_debug ("failed to connect to bus: %s", egg_error_message (error));
 		g_simple_async_result_take_error (res, error);
 		g_simple_async_result_complete (res);
 	}
@@ -697,18 +646,16 @@ on_prompter_begin_prompting (GObject *source,
 
 	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);
 
-		_gcr_debug ("opened prompt %s: %s",
+		_gcr_debug ("registered prompt %s: %s",
 		            self->pv->prompter_bus_name, self->pv->prompt_path);
 
 		g_return_if_fail (self->pv->prompt_path != NULL);
 		perform_init_async (self, res);
 
 	} else {
-		_gcr_debug ("failed to open prompt %s: %s",
+		_gcr_debug ("failed to register prompt %s: %s",
 		            self->pv->prompter_bus_name, egg_error_message (error));
 
 		g_simple_async_result_take_error (res, error);
@@ -719,40 +666,35 @@ on_prompter_begin_prompting (GObject *source,
 	g_object_unref (res);
 }
 
-static void
-on_prompt_proxy_new (GObject *source,
-                     GAsyncResult *result,
-                     gpointer user_data)
+static gboolean
+on_call_timeout (gpointer user_data)
 {
 	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+	CallClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
 	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) {
-		_gcr_debug ("created prompt proxy");
-
-		g_return_if_fail (self->pv->prompt_proxy != NULL);
-		perform_init_async (self, res);
+	g_source_destroy (closure->timeout);
+	closure->timeout = NULL;
 
-	} else {
-		_gcr_debug ("failed to create prompt proxy: %s", egg_error_message (error));
+	/* Tell the prompter we're no longer interested */
+	gcr_system_prompt_close_async (self, NULL, NULL, NULL);
 
-		g_simple_async_result_take_error (res, error);
-		g_simple_async_result_complete (res);
-	}
+	g_simple_async_result_set_error (res, GCR_SYSTEM_PROMPT_ERROR,
+	                                 GCR_SYSTEM_PROMPT_IN_PROGRESS,
+	                                 _("Another prompt is already in progress"));
+	g_simple_async_result_complete (res);
 
 	g_object_unref (self);
-	g_object_unref (res);
+	return FALSE; /* Don't call this function again */
 }
 
 void
 perform_init_async (GcrSystemPrompt *self,
                     GSimpleAsyncResult *res)
 {
-	InitClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
+	CallClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
+
+	g_main_context_push_thread_default (closure->context);
 
 	/* 1. Connect to the session bus */
 	if (!self->pv->connection) {
@@ -760,39 +702,38 @@ perform_init_async (GcrSystemPrompt *self,
 		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) {
-		_gcr_debug ("opening prompt");
+	/* 2. Export our object, BeginPrompting on prompter */
+	} else if (!self->pv->begun_prompting) {
+		g_assert (self->pv->prompt_path != NULL);
+
 		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_variant_new ("(o)", self->pv->prompt_path),
+		                        G_VARIANT_TYPE ("()"),
 		                        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) {
-		_gcr_debug ("creating 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));
+	/* 3. Wait for iterate */
+	} else if (!self->pv->pending) {
+		self->pv->pending = g_object_ref (res);
+		if (self->pv->timeout_seconds > 0) {
+			g_assert (closure->timeout == NULL);
+			closure->timeout = g_timeout_source_new_seconds (self->pv->timeout_seconds);
+			g_source_set_callback (closure->timeout, on_call_timeout, res, NULL);
+			g_source_attach (closure->timeout, closure->context);
+		}
 
 	/* 4. All done */
 	} else {
-		_gcr_debug ("successfully initialized prompt");
-		g_simple_async_result_complete (res);
+		g_assert_not_reached ();
 	}
+
+	g_main_context_pop_thread_default (closure->context);
 }
 
 static void
@@ -804,14 +745,15 @@ gcr_system_prompt_real_init_async (GAsyncInitable *initable,
 {
 	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (initable);
 	GSimpleAsyncResult *res;
-	InitClosure *closure;
+	CallClosure *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);
+	closure = g_new0 (CallClosure, 1);
+	closure->context = g_main_context_get_thread_default ();
+	if (closure->context)
+		g_main_context_ref (closure->context);
+	g_simple_async_result_set_op_res_gpointer (res, closure, call_closure_free);
 
 	perform_init_async (self, res);
 
@@ -842,314 +784,253 @@ gcr_system_prompt_async_initable_iface (GAsyncInitableIface *iface)
 	iface->init_finish = gcr_system_prompt_real_init_finish;
 }
 
-static GVariant *
-parameter_properties (GcrSystemPrompt *self)
+typedef struct {
+	GAsyncResult *result;
+	GMainContext *context;
+	GMainLoop *loop;
+} SyncClosure;
+
+static SyncClosure *
+sync_closure_new (void)
 {
-	GHashTableIter iter;
-	GVariantBuilder builder;
-	const gchar *property_name;
-	GVariant *variant;
+	SyncClosure *closure;
 
-	g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ssv)"));
-
-	g_hash_table_iter_init (&iter, self->pv->properties_to_write);
-	while (g_hash_table_iter_next (&iter, (gpointer *)&property_name, NULL)) {
-		_gcr_debug ("sending prompt property: %s", property_name);
-		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
-			g_variant_builder_add (&builder, "(ssv)", GCR_DBUS_PROMPT_INTERFACE,
-			                       property_name, variant);
-	}
+	closure = g_new0 (SyncClosure, 1);
+
+	closure->context = g_main_context_new ();
+	closure->loop = g_main_loop_new (closure->context, FALSE);
 
-	g_hash_table_remove_all (self->pv->properties_to_write);
-	return g_variant_builder_end (&builder);
+	return closure;
 }
 
 static void
-return_properties (GcrSystemPrompt *self,
-                   GVariant *properties)
+sync_closure_free (gpointer data)
 {
-	GVariantIter *iter;
-	GVariant *value;
-	gchar *interface;
-	gchar *property_name;
-	gpointer key;
+	SyncClosure *closure = data;
 
-	g_variant_get (properties, "a(ssv)", &iter);
-	while (g_variant_iter_loop (iter, "(ssv)", &interface, &property_name, &value)) {
-		_gcr_debug ("received prompt property: %s", property_name);
-		key = (gpointer)g_intern_string (property_name);
-		if (!g_hash_table_lookup (self->pv->properties_to_write, key)) {
-			g_hash_table_insert (self->pv->property_cache,
-			                     key, g_variant_ref (value));
-		}
-	}
-	g_variant_iter_free (iter);
+	g_clear_object (&closure->result);
+	g_main_loop_unref (closure->loop);
+	g_main_context_unref (closure->context);
 }
 
-static GVariant *
-parameters_for_password (GcrSystemPrompt *self)
+static void
+on_sync_result (GObject *source,
+                GAsyncResult *result,
+                gpointer user_data)
 {
-	GcrSecretExchange *exchange;
-	GVariant *properties;
-	GVariant *params;
-	gchar *input;
+	SyncClosure *closure = user_data;
+	closure->result = g_object_ref (result);
+	g_main_loop_quit (closure->loop);
+}
 
-	exchange = gcr_system_prompt_get_secret_exchange (self);
+static gboolean
+gcr_system_prompt_real_init (GInitable *initable,
+                             GCancellable *cancellable,
+                             GError **error)
+{
+	SyncClosure *closure;
+	gboolean result;
 
-	if (self->pv->exchanged) {
-		_gcr_debug ("sending secret exchange");
-		input = gcr_secret_exchange_send (exchange, NULL, 0);
-	} else {
-		_gcr_debug ("beginning secret exchange");
-		input = gcr_secret_exchange_begin (exchange);
-	}
-	self->pv->exchanged = TRUE;
+	closure = sync_closure_new ();
+	g_main_context_push_thread_default (closure->context);
+
+	gcr_system_prompt_real_init_async (G_ASYNC_INITABLE (initable),
+	                                   G_PRIORITY_DEFAULT, cancellable,
+	                                   on_sync_result, closure);
+
+	g_main_loop_run (closure->loop);
 
-	properties = parameter_properties (self);
-	params = g_variant_new ("(@a(ssv)s)", properties, input);
-	g_free (input);
+	result = gcr_system_prompt_real_init_finish (G_ASYNC_INITABLE (initable),
+	                                             closure->result, error);
 
-	return params;
+	g_main_context_pop_thread_default (closure->context);
+	sync_closure_free (closure);
+
+	return result;
 }
 
-static const gchar *
-return_for_password (GcrSystemPrompt *self,
-                     GVariant *retval,
-                     GError **error)
+static void
+gcr_system_prompt_initable_iface (GInitableIface *iface)
 {
-	GcrSecretExchange *exchange;
-	GVariant *properties;
-	const gchar *ret = NULL;
-	gchar *output;
+	iface->init = gcr_system_prompt_real_init;
+}
 
-	exchange = gcr_system_prompt_get_secret_exchange (self);
-	g_variant_get (retval, "(@a(ssv)s)", &properties, &output);
-
-	return_properties (self, properties);
-
-	if (output && output[0]) {
-		if (!gcr_secret_exchange_receive (exchange, output)) {
-			_gcr_debug ("received invalid secret exchange");
-			g_set_error (error, GCR_SYSTEM_PROMPT_ERROR,
-			             GCR_SYSTEM_PROMPT_FAILED, "Invalid secret exchanged");
-		} else {
-			_gcr_debug ("received password in secret exchange");
-			ret = gcr_secret_exchange_get_secret (exchange, NULL);
-		}
-	} else {
-		_gcr_debug ("password prompt was cancelled");
-	}
+static GVariantBuilder *
+build_dirty_properties (GcrSystemPrompt *self)
+{
+	GVariantBuilder *builder;
+	GHashTableIter iter;
+	GVariant *variant;
+	gpointer key;
+
+	builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
 
-	g_variant_unref (properties);
-	g_free (output);
+	g_hash_table_iter_init (&iter, self->pv->dirty_properties);
+	while (g_hash_table_iter_next (&iter, &key, NULL)) {
+		variant = g_hash_table_lookup (self->pv->properties, key);
+		g_variant_builder_add (builder, "{sv}", (const gchar *)key, variant);
+	}
 
-	return ret;
+	g_hash_table_remove_all (self->pv->dirty_properties);
+	return builder;
 }
 
 static void
-on_prompt_requested_password (GObject *source,
-                              GAsyncResult *result,
-                              gpointer user_data)
+on_perform_prompt_complete (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;
+	CallClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
 	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);
-	}
-
+	retval = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
 	if (error != NULL) {
-		_gcr_debug ("failed to prompt for password: %s", egg_error_message (error));
 		g_simple_async_result_take_error (res, error);
+		g_simple_async_result_complete_in_idle (res);
+	} else {
+		closure->waiting = TRUE;
 	}
 
-	g_simple_async_result_complete (res);
+	if (retval)
+		g_variant_unref (retval);
+
 	g_object_unref (self);
 	g_object_unref (res);
 }
 
-void
-gcr_system_prompt_password_async (GcrSystemPrompt *self,
-                                  GCancellable *cancellable,
-                                  GAsyncReadyCallback callback,
-                                  gpointer user_data)
+static void
+perform_prompt_async (GcrSystemPrompt *self,
+                      const gchar *type,
+                      gpointer source_tag,
+                      GCancellable *cancellable,
+                      GAsyncReadyCallback callback,
+                      gpointer user_data)
 {
 	GSimpleAsyncResult *res;
+	GcrSecretExchange *exchange;
+	GVariantBuilder *builder;
+	CallClosure *closure;
+	gchar *sent;
 
 	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);
+	if (self->pv->pending != NULL) {
+		g_warning ("another operation is already pending on this prompt");
+		return;
+	}
 
 	_gcr_debug ("prompting for password");
 
-	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));
+	res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, source_tag);
+	closure = g_new0 (CallClosure, 1);
+	closure->cancellable = cancellable ? g_object_ref (cancellable) : cancellable;
+	g_simple_async_result_set_op_res_gpointer (res, closure, call_closure_free);
 
-	g_object_unref (res);
-}
+	exchange = gcr_system_prompt_get_secret_exchange (self);
+	if (self->pv->received)
+		sent = gcr_secret_exchange_send (exchange, NULL, 0);
+	else
+		sent = gcr_secret_exchange_begin (exchange);
 
-const gchar *
-gcr_system_prompt_password_finish (GcrSystemPrompt *self,
-                                   GAsyncResult *result,
-                                   GError **error)
-{
-	GSimpleAsyncResult *res;
+	builder = build_dirty_properties (self);
 
-	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);
+	/* Reregister the prompt object in the current GMainContext */
+	register_prompt_object (self, NULL);
 
-	res = G_SIMPLE_ASYNC_RESULT (result);
-	if (g_simple_async_result_propagate_error (res, error))
-		return FALSE;
+	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_PERFORM,
+	                        g_variant_new ("(osa{sv}s)", self->pv->prompt_path,
+	                                       type, builder, sent),
+	                        G_VARIANT_TYPE ("()"),
+	                        G_DBUS_CALL_FLAGS_NO_AUTO_START,
+	                        -1, cancellable,
+	                        on_perform_prompt_complete,
+	                        g_object_ref (res));
 
-	return g_simple_async_result_get_op_res_gpointer (res);
+	self->pv->pending = res;
+	g_free (sent);
 }
 
-const gchar *
-gcr_system_prompt_password (GcrSystemPrompt *self,
-                            GCancellable *cancellable,
-                            GError **error)
+static GcrSystemPromptResponse
+handle_last_response (GcrSystemPrompt *self)
 {
-	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);
+	GcrSystemPromptResponse response;
 
-	if (!self->pv->prompt_proxy) {
-		g_warning ("GcrSystemPrompt was not successfully opened");
-		return NULL;
-	}
+	g_return_val_if_fail (self->pv->last_response != NULL,
+	                      GCR_SYSTEM_PROMPT_CANCEL);
 
-	_gcr_debug ("prompting for password");
+	if (g_str_equal (self->pv->last_response, GCR_DBUS_PROMPT_REPLY_YES)) {
+		response = GCR_SYSTEM_PROMPT_OK;
 
-	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);
+	} else if (g_str_equal (self->pv->last_response, GCR_DBUS_PROMPT_REPLY_NO)) {
+		response = GCR_SYSTEM_PROMPT_CANCEL;
 
-	if (retval != NULL) {
-		ret = return_for_password (self, retval, error);
-		g_variant_unref (retval);
+	} else {
+		g_warning ("unknown response from prompter: %s", self->pv->last_response);
+		response = GCR_SYSTEM_PROMPT_CANCEL;
 	}
 
-	return ret;
+	return response;
 }
 
-static GVariant *
-parameters_for_confirm (GcrSystemPrompt *self)
+static void
+gcr_system_prompt_password_async (GcrPrompt *prompt,
+                                  GCancellable *cancellable,
+                                  GAsyncReadyCallback callback,
+                                  gpointer user_data)
 {
-	GVariant *properties;
-
-	properties = parameter_properties (self);
-	return g_variant_new ("(@a(ssv))", properties);
+	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (prompt);
+	perform_prompt_async (self, GCR_DBUS_PROMPT_TYPE_PASSWORD,
+	                      gcr_system_prompt_password_async,
+	                      cancellable, callback, user_data);
 }
 
-static gboolean
-return_for_confirm (GcrSystemPrompt *self,
-                    GVariant *retval,
-                    GError **error)
+static const gchar *
+gcr_system_prompt_password_finish (GcrPrompt *prompt,
+                                   GAsyncResult *result,
+                                   GError **error)
 {
-	GVariant *properties;
-	gboolean confirm = FALSE;
-
-	g_variant_get (retval, "(@a(ssv)b)", &properties, &confirm);
+	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (prompt);
+	GSimpleAsyncResult *res;
 
-	return_properties (self, properties);
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
+	                      gcr_system_prompt_password_async), FALSE);
 
-	g_variant_unref (properties);
+	res = G_SIMPLE_ASYNC_RESULT (result);
+	if (g_simple_async_result_propagate_error (res, error))
+		return FALSE;
 
-	if (confirm)
-		_gcr_debug ("prompt confirmed");
-	else
-		_gcr_debug ("prompt was cancelled");
+	if (handle_last_response (self) == GCR_SYSTEM_PROMPT_OK)
+		return gcr_secret_exchange_get_secret (self->pv->exchange, NULL);
 
-	return confirm;
+	return NULL;
 }
 
 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;
-	gboolean ret;
-
-	retval = g_dbus_proxy_call_finish (self->pv->prompt_proxy, result, &error);
-
-	if (retval != NULL) {
-		ret = return_for_confirm (self, retval, &error);
-		g_simple_async_result_set_op_res_gboolean (res, ret);
-	}
-
-	if (error != NULL) {
-		_gcr_debug ("failed to prompt for confirm: %s", egg_error_message (error));
-		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,
+gcr_system_prompt_confirm_async (GcrPrompt *prompt,
                                  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);
-
-	_gcr_debug ("prompting for confirm");
-
-	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);
+	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (prompt);
+	perform_prompt_async (self, GCR_DBUS_PROMPT_TYPE_CONFIRM,
+	                      gcr_system_prompt_confirm_async,
+	                      cancellable, callback, user_data);
 }
 
-gboolean
-gcr_system_prompt_confirm_finish (GcrSystemPrompt *self,
+static GcrPromptReply
+gcr_system_prompt_confirm_finish (GcrPrompt *prompt,
                                   GAsyncResult *result,
                                   GError **error)
 {
+	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (prompt);
 	GSimpleAsyncResult *res;
 
 	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPT (self), FALSE);
@@ -1160,57 +1041,16 @@ gcr_system_prompt_confirm_finish (GcrSystemPrompt *self,
 	if (g_simple_async_result_propagate_error (res, error))
 		return FALSE;
 
-	return g_simple_async_result_get_op_res_gboolean (res);
+	return handle_last_response (self);
 }
 
-/**
- * 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)
+static void
+gcr_system_prompt_prompt_iface (GcrPromptIface *iface)
 {
-	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;
-	}
-
-	_gcr_debug ("prompting for confirm");
-
-	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) {
-		ret = return_for_confirm (self, retval, error);
-		g_variant_unref (retval);
-	}
-
-	return ret;
+	iface->prompt_password_async = gcr_system_prompt_password_async;
+	iface->prompt_password_finish = gcr_system_prompt_password_finish;
+	iface->prompt_confirm_async = gcr_system_prompt_confirm_async;
+	iface->prompt_confirm_finish = gcr_system_prompt_confirm_finish;
 }
 
 /**
@@ -1404,51 +1244,44 @@ gcr_system_prompt_close (GcrSystemPrompt *self,
                          GCancellable *cancellable,
                          GError **error)
 {
-	GVariant *retval;
+	SyncClosure *closure;
+	gboolean result;
 
-	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);
-	g_return_val_if_fail (self->pv->prompt_path != NULL, FALSE);
+	closure = sync_closure_new ();
+	g_main_context_push_thread_default (closure->context);
 
-	_gcr_debug ("closing prompt");
+	gcr_system_prompt_close_async (self, cancellable,
+	                               on_sync_result, closure);
 
-	retval = 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_FINISH,
-	                                      g_variant_new ("(o)", self->pv->prompt_path),
-	                                      NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START,
-	                                      -1, cancellable, error);
-	if (retval == NULL) {
-		_gcr_debug ("failed to close prompt: %s",
-		            egg_error_result_message (error));
-		return FALSE;
-	}
+	g_main_loop_run (closure->loop);
 
-	g_variant_unref (retval);
-	g_free (self->pv->prompt_path);
-	self->pv->prompt_path = NULL;
-	return TRUE;
+	result = gcr_system_prompt_close_finish (self, closure->result, error);
+
+	g_main_context_pop_thread_default (closure->context);
+	sync_closure_free (closure);
+
+	return result;
 }
 
 static void
-on_prompt_close_async (GObject *source,
-                       GAsyncResult *result,
-                       gpointer user_data)
+on_prompter_stop_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 *retval;
 
-	if (!g_dbus_connection_call_finish (self->pv->connection, result, &error)) {
-		_gcr_debug ("failed to close prompt: %s", egg_error_message (error));
-		g_simple_async_result_take_error (res, error);
+	retval = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
+	if (error != NULL) {
+		_gcr_debug ("failed to stop prompting: %s", egg_error_message (error));
+		g_clear_error (&error);
 	}
 
+	if (retval)
+		g_variant_unref (retval);
+
 	g_simple_async_result_complete (res);
-	g_object_unref (self);
 	g_object_unref (res);
 }
 
@@ -1459,25 +1292,66 @@ gcr_system_prompt_close_async (GcrSystemPrompt *self,
                                gpointer user_data)
 {
 	GSimpleAsyncResult *res;
+	CallClosure *closure;
+	gboolean called = FALSE;
 
 	g_return_if_fail (GCR_SYSTEM_PROMPT (self));
 	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+	g_return_if_fail (self->pv->connection != NULL);
 	g_return_if_fail (self->pv->prompt_path != NULL);
+	g_return_if_fail (self->pv->closed == FALSE);
 
-	res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
+	_gcr_debug ("closing prompt");
+	self->pv->closed = TRUE;
+
+	if (self->pv->pending) {
+		res = g_object_ref (self->pv->pending);
+		g_clear_object (&self->pv->pending);
+		closure = g_simple_async_result_get_op_res_gpointer (res);
+		g_cancellable_cancel (closure->cancellable);
+		g_simple_async_result_complete_in_idle (res);
+		g_object_unref (res);
+	}
+
+	res = g_simple_async_result_new (NULL, callback, user_data,
 	                                 gcr_system_prompt_close_async);
+	closure = g_new0 (CallClosure, 1);
+	closure->cancellable = cancellable ? g_object_ref (cancellable) : g_cancellable_new ();
+	closure->context = g_main_context_get_thread_default ();
+	if (closure->context != NULL)
+		g_main_context_ref (closure->context);
+	g_simple_async_result_set_op_res_gpointer (res, closure, call_closure_free);
+
+	if (self->pv->prompt_registered) {
+		g_dbus_connection_unregister_object (self->pv->connection,
+		                                     self->pv->prompt_registered);
+		self->pv->prompt_registered = 0;
+	}
 
-	_gcr_debug ("closing prompt");
+	if (self->pv->begun_prompting) {
+		if (self->pv->connection) {
+			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_STOP,
+			                        g_variant_new ("(o)", self->pv->prompt_path),
+			                        G_VARIANT_TYPE ("()"),
+			                        G_DBUS_CALL_FLAGS_NO_AUTO_START,
+			                        -1, closure->cancellable,
+			                        on_prompter_stop_prompting,
+			                        g_object_ref (res));
+			called = TRUE;
+		}
+	}
 
-	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 ("(o)", self->pv->prompt_path),
-	                        NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START,
-	                        -1, NULL, on_prompt_close_async, g_object_ref (res));
+	g_free (self->pv->prompt_path);
+	self->pv->prompt_path = NULL;
 
+	g_clear_object (&self->pv->connection);
+
+	if (!called)
+		g_simple_async_result_complete_in_idle (res);
 	g_object_unref (res);
 }
 
@@ -1489,10 +1363,10 @@ gcr_system_prompt_close_finish (GcrSystemPrompt *self,
 	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPT (self), FALSE);
 	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL,
 	                      gcr_system_prompt_close_async), FALSE);
 
-	if (!g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
 		return FALSE;
 
 	return TRUE;
@@ -1500,7 +1374,6 @@ gcr_system_prompt_close_finish (GcrSystemPrompt *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 },
 };
 
diff --git a/gcr/gcr-system-prompt.h b/gcr/gcr-system-prompt.h
index 3fe2b33..4bd154b 100644
--- a/gcr/gcr-system-prompt.h
+++ b/gcr/gcr-system-prompt.h
@@ -35,10 +35,13 @@
 
 G_BEGIN_DECLS
 
+typedef enum {
+	GCR_SYSTEM_PROMPT_CANCEL = 0,
+	GCR_SYSTEM_PROMPT_OK = 1,
+} GcrSystemPromptResponse;
 
 typedef enum {
 	GCR_SYSTEM_PROMPT_IN_PROGRESS = 1,
-	GCR_SYSTEM_PROMPT_NOT_HAPPENING,
 	GCR_SYSTEM_PROMPT_FAILED,
 	GCR_SYSTEM_PROMPT_CLOSED
 } GcrSystemPromptError;
@@ -73,51 +76,6 @@ 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,
@@ -142,32 +100,6 @@ GcrSystemPrompt *    gcr_system_prompt_open                      (gint timeout_s
                                                                   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);
-
 gboolean             gcr_system_prompt_close                     (GcrSystemPrompt *self,
                                                                   GCancellable *cancellable,
                                                                   GError **error);
diff --git a/gcr/gcr-system-prompter.c b/gcr/gcr-system-prompter.c
index 3f8546b..278c41f 100644
--- a/gcr/gcr-system-prompter.c
+++ b/gcr/gcr-system-prompter.c
@@ -27,8 +27,10 @@
 #include "gcr-dbus-generated.h"
 #define DEBUG_FLAG GCR_DEBUG_PROMPT
 #include "gcr-debug.h"
+#include "gcr-enum-types-base.h"
 #include "gcr-internal.h"
 #include "gcr-library.h"
+#include "gcr-prompt.h"
 #include "gcr-secret-exchange.h"
 #include "gcr-system-prompter.h"
 #include "gcr-system-prompt.h"
@@ -59,415 +61,125 @@
 
 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 {
-	OPEN,
-	PROMPT_PASSWORD,
-	PROMPT_CONFIRM,
-	RESPONDED,
-	CLOSE,
-	LAST_SIGNAL
+	PROP_MODE,
+	PROP_PROMPT_TYPE
 };
 
 struct _GcrSystemPrompterPrivate {
+	GcrSystemPrompterMode mode;
+	GType prompt_type;
+
 	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 opened;
-
-	/* Properties */
-	GHashTable *properties;
-	GQueue *properties_changed;
+	GHashTable *pending;          /* callback path (string) -> sender (string) */
+	GHashTable *active;           /* callback path (string) -> active (ActivePrompt) */
 };
 
-static gint signals[LAST_SIGNAL] = { 0, };
-
 G_DEFINE_TYPE (GcrSystemPrompter, gcr_system_prompter, G_TYPE_OBJECT);
 
-static GVariant *
-build_changed_properties (GcrSystemPrompter *self)
-{
-	const gchar *property_name;
-	GVariantBuilder builder;
-	GVariant *value;
-
-	g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ssv)"));
-
-	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, "(ssv)", GCR_DBUS_PROMPT_INTERFACE, property_name, value);
-	}
-
-	return g_variant_builder_end (&builder);
-}
-
-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 (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)
-{
-	GObject *obj = G_OBJECT (self);
-	GVariantIter *iter;
-	GVariant *variant;
-	gchar *property_name;
-	gchar *interface;
-
-	g_object_freeze_notify (obj);
+typedef struct {
+	gint refs;
+	gchar *callback_path;
+	gchar *callback_name;
+	GcrSystemPrompter *prompter;
+	GCancellable *cancellable;
+	GcrPrompt *prompt;
+	gboolean ready;
+	guint notify_sig;
+	GHashTable *changed;
+	GcrSecretExchange *exchange;
+	gboolean received;
+} ActivePrompt;
 
-	g_variant_get (properties, "a(ssv)", &iter);
-	while (g_variant_iter_loop (iter, "(ssv)", &interface, &property_name, &variant)) {
-		if (g_strcmp0 (interface, GCR_DBUS_PROMPT_INTERFACE) != 0)
-			continue;
-		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);
-		}
-	}
+static void    prompt_send_ready               (ActivePrompt *active,
+                                                const gchar *response,
+                                                const gchar *secret);
 
-	g_variant_iter_free (iter);
+static void    prompt_possibly_ready           (GcrSystemPrompter *self,
+                                                const gchar *callback);
 
-	g_object_thaw_notify (obj);
-}
+static void    prompt_stop_prompting           (ActivePrompt *active,
+                                                gboolean send_done_message,
+                                                gboolean wait_for_reply);
 
-static void
-prompt_clear_property (GcrSystemPrompter *self,
-                       const gchar *property_name,
-                       GVariant *value)
+static ActivePrompt *
+active_prompt_ref (ActivePrompt *active)
 {
-	g_hash_table_insert (self->pv->properties,
-	                     (gpointer)g_intern_string (property_name),
-	                     g_variant_ref (value));
+	g_atomic_int_inc (&active->refs);
+	return active;
 }
 
 static void
-prompt_clear_properties (GcrSystemPrompter *self)
+on_prompt_notify (GObject *object,
+                  GParamSpec *param,
+                  gpointer user_data)
 {
-	GVariant *variant;
-
-	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);
+	ActivePrompt *active = user_data;
+	gpointer key = (gpointer)g_intern_string (param->name);
+	g_hash_table_replace (active->changed, key, key);
 }
 
 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->opened) {
-		self->pv->opened = TRUE;
-		_gcr_debug ("prompter opening prompt");
-		g_signal_emit (self, signals[OPEN], 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.");
+active_prompt_unref (gpointer data)
+{
+	ActivePrompt *active = data;
+
+	if (g_atomic_int_dec_and_test (&active->refs)) {
+		g_free (active->callback_path);
+		g_free (active->callback_name);
+		g_object_unref (active->prompter);
+		g_object_unref (active->cancellable);
+		g_signal_handlers_disconnect_by_func (active->prompt, on_prompt_notify, active);
+		g_object_unref (active->prompt);
+		g_hash_table_destroy (active->changed);
+		if (active->exchange)
+			g_object_unref (active->exchange);
+		g_slice_free (ActivePrompt, active);
 	}
 }
 
-static void
-prompt_method_request_confirm (GcrSystemPrompter *self,
-                               GDBusMethodInvocation *invocation,
-                               GVariant *properties)
+static GcrSecretExchange *
+active_prompt_get_secret_exchange (ActivePrompt *active)
 {
-	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->opened) {
-		self->pv->opened = TRUE;
-		_gcr_debug ("prompter opening prompt");
-		g_signal_emit (self, signals[OPEN], 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.");
-	}
+	if (active->exchange == NULL)
+		active->exchange = gcr_secret_exchange_new (NULL);
+	return active->exchange;
 }
 
-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)
+static ActivePrompt *
+active_prompt_create (GcrSystemPrompter *self,
+                      const gchar *callback)
 {
-	GcrSystemPrompter *self = GCR_SYSTEM_PROMPTER (user_data);
-	GVariant *properties = NULL;
-	gchar *string = NULL;
-
-	g_return_if_fail (method_name != NULL);
+	ActivePrompt *active;
 
-	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.");
+	active = g_slice_new0 (ActivePrompt);
+	if (!g_hash_table_lookup_extended (self->pv->pending, callback,
+	                                   (gpointer *)&active->callback_path,
+	                                   (gpointer *)&active->callback_name))
+		g_return_val_if_reached (NULL);
+	if (!g_hash_table_steal (self->pv->pending, callback))
+		g_return_val_if_reached (NULL);
 
-	} else if (g_str_equal (method_name, "RequestPassword")) {
-		g_variant_get (parameters, "(@a(ssv)s)", &properties, &string);
-		prompt_method_request_password (self, invocation, properties, string);
+	active->refs = 1;
+	active->prompter = g_object_ref (self);
+	active->cancellable = g_cancellable_new ();
+	active->prompt = g_object_new (self->pv->prompt_type, NULL);
+	active->notify_sig = g_signal_connect (active->prompt, "notify", G_CALLBACK (on_prompt_notify), active);
+	active->changed = g_hash_table_new (g_direct_hash, g_direct_equal);
 
-	} else if (g_str_equal (method_name, "RequestConfirm")) {
-		g_variant_get (parameters, "(@a(ssv))", &properties);
-		prompt_method_request_confirm (self, invocation, properties);
-
-	} else {
-		g_return_if_reached ();
-	}
-
-	if (properties)
-		g_variant_unref (properties);
-	g_free (string);
+	/* Insert us into the active hash table */
+	g_hash_table_replace (self->pv->active, active->callback_path, active);
+	return active;
 }
 
-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");
-	_gcr_debug ("prompter registering prompt: %s", self->pv->prompt_path);
-	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->opened) {
-		_gcr_debug ("prompter closing prompt");
-		self->pv->opened = FALSE;
-		g_signal_emit (self, signals[CLOSE], 0);
-	}
-
-	if (self->pv->owner_watching_id) {
-		_gcr_debug ("prompter stopping watch of client: %s", self->pv->owner_name);
-		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) {
-		_gcr_debug ("prompter unregistering prompt: %s", self->pv->prompt_path);
-		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);
+	self->pv->pending = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+	self->pv->active = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, active_prompt_unref);
 }
 
 static void
@@ -479,11 +191,11 @@ gcr_system_prompter_set_property (GObject *obj,
 	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));
+	case PROP_MODE:
+		self->pv->mode = g_value_get_enum (value);
 		break;
-	case PROP_PASSWORD_STRENGTH:
-		gcr_system_prompter_set_password_strength (self, g_value_get_int (value));
+	case PROP_PROMPT_TYPE:
+		self->pv->prompt_type = g_value_get_gtype (value);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
@@ -500,32 +212,11 @@ gcr_system_prompter_get_property (GObject *obj,
 	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));
+	case PROP_MODE:
+		g_value_set_enum (value, gcr_system_prompter_get_mode (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));
+	case PROP_PROMPT_TYPE:
+		g_value_set_gtype (value, gcr_system_prompter_get_prompt_type (self));
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
@@ -540,13 +231,11 @@ gcr_system_prompter_dispose (GObject *obj)
 
 	_gcr_debug ("disposing prompter");
 
-	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);
+		gcr_system_prompter_unregister (self, FALSE);
+
+	g_hash_table_remove_all (self->pv->active);
+	g_hash_table_remove_all (self->pv->pending);
 
 	G_OBJECT_CLASS (gcr_system_prompter_parent_class)->dispose (obj);
 }
@@ -558,143 +247,249 @@ gcr_system_prompter_finalize (GObject *obj)
 
 	_gcr_debug ("finalizing prompter");
 
-	g_hash_table_destroy (self->pv->properties);
-	g_queue_free (self->pv->properties_changed);
+	g_assert (self->pv->connection == NULL);
+	g_assert (self->pv->prompter_registered == 0);
+
+	g_hash_table_destroy (self->pv->active);
+	g_hash_table_destroy (self->pv->pending);
 
 	G_OBJECT_CLASS (gcr_system_prompter_parent_class)->finalize (obj);
 }
 
 static void
-gcr_system_prompter_real_show_prompt (GcrSystemPrompter *self)
+gcr_system_prompter_class_init (GcrSystemPrompterClass *klass)
 {
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
-}
-
-static gboolean
-gcr_system_prompter_real_prompt_confirm (GcrSystemPrompter *self)
-{
-	return FALSE;
-}
+	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;
 
-static gboolean
-gcr_system_prompter_real_prompt_password (GcrSystemPrompter *self)
-{
-	return FALSE;
-}
+	g_type_class_add_private (gobject_class, sizeof (GcrSystemPrompterPrivate));
 
-static void
-gcr_system_prompter_real_hide_prompt (GcrSystemPrompter *self)
-{
+	g_object_class_install_property (gobject_class, PROP_MODE,
+	              g_param_spec_enum ("mode", "Mode", "Prompting mode",
+	                                 GCR_TYPE_SYSTEM_PROMPTER_MODE, GCR_SYSTEM_PROMPTER_SINGLE,
+	                                 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
 
+	g_object_class_install_property (gobject_class, PROP_PROMPT_TYPE,
+	             g_param_spec_gtype ("prompt-type", "Prompt GType", "GObject type of prompts",
+	                                 GCR_TYPE_PROMPT,
+	                                 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
 }
 
-static void
-gcr_system_prompter_class_init (GcrSystemPrompterClass *klass)
+static GVariantBuilder *
+prompt_build_properties (GcrPrompt *prompt,
+                         GHashTable *changed)
 {
-	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	GObject *obj = G_OBJECT (prompt);
+	GVariantBuilder *builder;
+	const gchar *property_name;
+	GParamSpec *pspec;
+	GHashTableIter iter;
+	const GVariantType *type;
+	GVariant *variant;
+	GValue value;
 
-	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;
+	builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
+	g_hash_table_iter_init (&iter, changed);
+	while (g_hash_table_iter_next (&iter, (gpointer *)&property_name, NULL)) {
 
-	klass->open = 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->close = gcr_system_prompter_real_hide_prompt;
+		/* Make sure this property is on the prompt interface */
+		pspec = g_object_interface_find_property (GCR_PROMPT_GET_INTERFACE (obj),
+		                                          property_name);
+		if (pspec == NULL)
+			continue;
 
-	g_type_class_add_private (gobject_class, sizeof (GcrSystemPrompterPrivate));
+		memset (&value, 0, sizeof (GValue));
+		g_value_init (&value, pspec->value_type);
+		g_object_get_property (obj, property_name, &value);
+
+		switch (pspec->value_type) {
+		case G_TYPE_STRING:
+			type = G_VARIANT_TYPE ("s");
+			break;
+		case G_TYPE_INT:
+			type = G_VARIANT_TYPE ("i");
+			break;
+		case G_TYPE_BOOLEAN:
+			type = G_VARIANT_TYPE ("b");
+			break;
+		default:
+			g_critical ("encountered unsupported property type on GcrPrompt: %s",
+			            g_type_name (pspec->value_type));
+			continue;
+		}
 
-	g_object_class_install_property (gobject_class, PROP_TITLE,
-	            g_param_spec_string ("title", "Title", "Prompt title",
-	                                 "", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+		variant = g_dbus_gvalue_to_gvariant (&value, type);
+		g_variant_builder_add (builder, "{sv}", property_name,
+		                       g_variant_new_variant (variant));
+		g_value_unset (&value);
+		g_variant_unref (variant);
+	}
+	g_hash_table_remove_all (changed);
+	return builder;
+}
 
-	g_object_class_install_property (gobject_class, PROP_MESSAGE,
-	            g_param_spec_string ("message", "Message", "Prompt message",
-	                                 "", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+static void
+prompt_stop_prompting (ActivePrompt *active,
+                       gboolean send_done_message,
+                       gboolean wait_for_reply)
+{
+	GcrSystemPrompter *self = g_object_ref (active->prompter);
+	GVariant *retval;
+
+	if (!active->ready)
+		g_cancellable_cancel (active->cancellable);
+
+	if (send_done_message && wait_for_reply) {
+		retval = g_dbus_connection_call_sync (self->pv->connection,
+		                                      active->callback_name,
+		                                      active->callback_path,
+		                                      GCR_DBUS_CALLBACK_INTERFACE,
+		                                      GCR_DBUS_CALLBACK_METHOD_DONE,
+		                                      g_variant_new ("()"),
+		                                      G_VARIANT_TYPE ("()"),
+		                                      G_DBUS_CALL_FLAGS_NO_AUTO_START,
+		                                      -1, NULL, NULL);
+		if (retval)
+			g_variant_unref (retval);
+	} else if (send_done_message) {
+		g_dbus_connection_call (self->pv->connection,
+		                        active->callback_name,
+		                        active->callback_path,
+		                        GCR_DBUS_CALLBACK_INTERFACE,
+		                        GCR_DBUS_CALLBACK_METHOD_DONE,
+		                        g_variant_new ("()"),
+		                        G_VARIANT_TYPE ("()"),
+		                        G_DBUS_CALL_FLAGS_NO_AUTO_START,
+		                        -1, NULL, NULL, NULL);
+	}
 
-	g_object_class_install_property (gobject_class, PROP_DESCRIPTION,
-	            g_param_spec_string ("description", "Description", "Prompt description",
-	                                 "", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+	g_object_run_dispose (G_OBJECT (active->prompt));
+	if (!g_hash_table_remove (self->pv->active, active->callback_path))
+		g_assert_not_reached ();
 
-	g_object_class_install_property (gobject_class, PROP_WARNING,
-	            g_param_spec_string ("warning", "Warning", "Prompt warning",
-	                                 "", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+	g_object_unref (self);
+}
 
-	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_PARAM_STATIC_STRINGS));
+static void
+on_prompt_ready_complete (GObject *source,
+                          GAsyncResult *result,
+                          gpointer user_data)
+{
+	ActivePrompt *active = user_data;
+	GcrSystemPrompter *self = g_object_ref (active->prompter);
+	GError *error = NULL;
+	GVariant *retval;
 
-	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_PARAM_STATIC_STRINGS));
+	active->ready = TRUE;
+	retval = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
 
-	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_PARAM_STATIC_STRINGS));
+	/* The ready call failed,  */
+	if (error != NULL) {
+		if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD))
+			_gcr_debug ("prompt disappeared or does not exist: %s: %s",
+			            active->callback_name, active->callback_path);
+		else
+			g_message ("received an error from the prompt callback: %s", error->message);
+		g_error_free (error);
+		prompt_stop_prompting (active, FALSE, FALSE);
 
-	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_PARAM_STATIC_STRINGS));
+		/* Another new prompt may be ready to go active? */
+		prompt_possibly_ready (self, NULL);
+	}
 
-	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 | G_PARAM_STATIC_STRINGS));
+	if (retval != NULL)
+		g_variant_unref (retval);
 
-	signals[OPEN] = g_signal_new ("open", GCR_TYPE_SYSTEM_PROMPTER, G_SIGNAL_RUN_LAST,
-	                              G_STRUCT_OFFSET (GcrSystemPrompterClass, open),
-	                              NULL, NULL, NULL, G_TYPE_NONE, 0);
+	active_prompt_unref (active);
+	g_object_unref (self);
+}
 
-	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);
+static void
+prompt_send_ready (ActivePrompt *active,
+                   const gchar *response,
+                   const gchar *secret)
+{
+	GcrSystemPrompter *self;
+	GVariantBuilder *builder;
+	GcrSecretExchange *exchange;
+	gchar *sent;
 
-	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);
+	exchange = active_prompt_get_secret_exchange (active);
+	if (!active->received) {
+		g_return_if_fail (secret == NULL);
+		sent = gcr_secret_exchange_begin (exchange);
+	} else {
+		sent = gcr_secret_exchange_send (exchange, secret, -1);
+	}
+
+	self = active->prompter;
+	builder = prompt_build_properties (active->prompt, active->changed);
 
-	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);
+	g_dbus_connection_call (self->pv->connection,
+	                        active->callback_name,
+	                        active->callback_path,
+	                        GCR_DBUS_CALLBACK_INTERFACE,
+	                        GCR_DBUS_CALLBACK_METHOD_READY,
+	                        g_variant_new ("(sa{sv}s)", response, builder, sent),
+	                        G_VARIANT_TYPE ("()"),
+	                        G_DBUS_CALL_FLAGS_NO_AUTO_START,
+	                        -1, active->cancellable,
+	                        on_prompt_ready_complete,
+	                        active_prompt_ref (active));
 
-	signals[CLOSE] = g_signal_new ("close", GCR_TYPE_SYSTEM_PROMPTER, G_SIGNAL_RUN_LAST,
-	                               G_STRUCT_OFFSET (GcrSystemPrompterClass, close),
-	                               NULL, NULL, NULL, G_TYPE_NONE, 0);
+	g_variant_builder_unref (builder);
+	g_free (sent);
 }
 
 static void
-emit_prompter_ready (GcrSystemPrompter *self)
+prompt_possibly_ready (GcrSystemPrompter *self,
+                       const gchar *callback)
 {
-	GError *error = NULL;
+	ActivePrompt *active;
+	GHashTableIter iter;
 
-	/* Now let everyone else know, we're ready! */
-	g_dbus_connection_emit_signal (self->pv->connection, NULL,
-	                               GCR_DBUS_PROMPTER_OBJECT_PATH,
-	                               GCR_DBUS_PROMPTER_INTERFACE,
-	                               GCR_DBUS_PROMPTER_SIGNAL_READY,
-	                               g_variant_new ("()"),
-	                               &error);
+	if (callback == NULL) {
+		g_hash_table_iter_init (&iter, self->pv->pending);
+		if (!g_hash_table_iter_next (&iter, (gpointer *)&callback, NULL))
+			return;
+		g_assert (callback != NULL);
+	}
 
-	if (error != NULL) {
-		g_warning ("couldn't emit prompter ready signal: %s", egg_error_message (error));
-		g_error_free (error);
+	active = g_hash_table_lookup (self->pv->active, callback);
+
+	/* Only one prompt at a time, and only one active */
+	if (active == NULL) {
+		if (self->pv->mode == GCR_SYSTEM_PROMPTER_SINGLE &&
+		    g_hash_table_size (self->pv->active) > 0)
+			return;
+
+		active = active_prompt_create (self, callback);
 	}
+
+	prompt_send_ready (active, GCR_DBUS_PROMPT_REPLY_YES, NULL);
 }
 
 static void
-on_owner_vanished (GDBusConnection *connection,
-                   const gchar *name,
-                   gpointer user_data)
+prompt_update_properties (GcrPrompt *prompt,
+                          GVariantIter *iter)
 {
-	GcrSystemPrompter *self = GCR_SYSTEM_PROMPTER (user_data);
-
-	if (self->pv->invocation != NULL)
-		gcr_system_prompter_respond_cancelled (self);
+	GObject *obj = G_OBJECT (prompt);
+	gchar *property_name;
+	GVariant *variant;
+	GValue value;
 
-	finish_prompting (self);
-	emit_prompter_ready (self);
+	g_object_freeze_notify (obj);
+	while (g_variant_iter_loop (iter, "{&sv}", &property_name, &variant)) {
+		memset (&value, 0, sizeof (GValue));
+		g_dbus_gvariant_to_gvalue (variant, &value);
+		g_object_set_property (obj, property_name, &value);
+		g_value_unset (&value);
+	}
+	g_object_thaw_notify (obj);
 }
 
 static GVariant *
@@ -724,70 +519,173 @@ prompter_set_property (GDBusConnection *connection,
 
 static void
 prompter_method_begin_prompting (GcrSystemPrompter *self,
-                                 GDBusMethodInvocation *invocation)
+                                 GDBusMethodInvocation *invocation,
+                                 GVariant *parameters)
 {
-	GDBusConnection *connection;
-	const gchar *path;
+	gchar *callback;
+	const gchar *sender;
+
+	g_variant_get (parameters, "(o)", &callback);
 
-	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");
+	if (g_hash_table_lookup (self->pv->pending, callback) ||
+	    g_hash_table_lookup (self->pv->active, callback)) {
+		g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+		                                               "Already begun prompting for this prompt callback");
 		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);
-	_gcr_debug ("prompter starting watch of dbus client: %s", self->pv->owner_name);
-	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));
+	sender = g_dbus_method_invocation_get_sender (invocation);
+	g_hash_table_insert (self->pv->pending, g_strdup (callback), g_strdup (sender));
+
+	g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
+	prompt_possibly_ready (self, callback);
+	g_free (callback);
 }
 
 static void
-prompter_method_finish_prompting (GcrSystemPrompter *self,
-                                  GDBusMethodInvocation *invocation,
-                                  const gchar *prompt)
+on_prompt_password (GObject *source,
+                    GAsyncResult *result,
+                    gpointer user_data)
 {
-	if (g_strcmp0 (prompt, self->pv->prompt_path) != 0) {
-		_gcr_debug ("caller passed invalid prompt: %s != %s", prompt, self->pv->prompt_path);
-		g_dbus_method_invocation_return_error_literal (invocation,
-		                                               G_DBUS_ERROR,
-		                                               G_DBUS_ERROR_INVALID_ARGS,
-		                                               "The prompt argument is not valid");
+	ActivePrompt *active = user_data;
+	const gchar *reply;
+	GError *error = NULL;
+	const gchar *response;
+
+	g_assert (active->ready == FALSE);
+
+	reply = gcr_prompt_password_finish (GCR_PROMPT (source), result, &error);
+	if (error != NULL) {
+		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+			g_warning ("prompting failed: %s", error->message);
+		g_clear_error (&error);
+	}
+
+	if (reply == NULL)
+		response = "no";
+	else
+		response = "yes";
+
+	prompt_send_ready (active, response, reply);
+	active_prompt_unref (active);
+}
+
+static void
+on_prompt_confirm (GObject *source,
+                   GAsyncResult *result,
+                   gpointer user_data)
+{
+	ActivePrompt *active = user_data;
+	GcrPromptReply reply;
+	GError *error = NULL;
+	const gchar *response;
+
+	g_assert (active->ready == FALSE);
+
+	reply = gcr_prompt_confirm_finish (GCR_PROMPT (source), result, &error);
+	if (error != NULL) {
+		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+			g_warning ("prompting failed: %s", error->message);
+		g_clear_error (&error);
+	}
+
+	switch (reply) {
+	case GCR_PROMPT_REPLY_OK:
+		response = GCR_DBUS_PROMPT_REPLY_YES;
+		break;
+	case GCR_PROMPT_REPLY_CANCEL:
+		response = GCR_DBUS_PROMPT_REPLY_NO;
+		break;
+	}
+
+	prompt_send_ready (active, response, NULL);
+	active_prompt_unref (active);
+}
+
+static void
+prompter_method_perform_prompt (GcrSystemPrompter *self,
+                                GDBusMethodInvocation *invocation,
+                                GVariant *parameters)
+{
+	GcrSecretExchange *exchange;
+	GError *error = NULL;
+	ActivePrompt *active;
+	const gchar *callback;
+	const gchar *type;
+	GVariantIter *iter;
+	const gchar *received;
+
+	g_variant_get (parameters, "(&o&sa{sv}&s)",
+	               &callback, &type, &iter, &received);
+
+	active = g_hash_table_lookup (self->pv->active, callback);
+	if (active == NULL) {
+		error = g_error_new (G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+		                     "Not begun prompting for this prompt callback");
+
+	} else if (!g_str_equal (active->callback_name, g_dbus_method_invocation_get_sender (invocation))) {
+		error = g_error_new (G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
+		                     "This prompt is not owned by this application");
+
+	} else if (!active->ready) {
+		error = g_error_new (G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+		                     "Already performing a prompt for this prompt callback");
+	}
+
+	if (error != NULL) {
+		g_dbus_method_invocation_take_error (invocation, error);
+		g_variant_iter_free (iter);
 		return;
 	}
 
-	if (self->pv->owner_name == NULL) {
-		_gcr_debug ("prompting is not in progress");
-		g_dbus_method_invocation_return_error_literal (invocation,
-		                                               GCR_SYSTEM_PROMPT_ERROR,
-		                                               GCR_SYSTEM_PROMPT_NOT_HAPPENING,
-		                                               "The prompt is not in progress");
+	g_assert (active != NULL);
+	prompt_update_properties (active->prompt, iter);
+	g_variant_iter_free (iter);
+
+	exchange = active_prompt_get_secret_exchange (active);
+	if (!gcr_secret_exchange_receive (exchange, received)) {
+		g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+		                                       "Invalid secret exchange received");
 		return;
 	}
 
-	_gcr_debug ("finishing prompting owned by caller %s", self->pv->owner_name);
+	active->received = TRUE;
 
-	/* Close a prompt that's prompting */
-	if (self->pv->invocation != NULL)
-		gcr_system_prompter_respond_cancelled (self);
+	if (g_strcmp0 (type, GCR_DBUS_PROMPT_TYPE_CONFIRM) == 0) {
+		active->ready = FALSE;
+		gcr_prompt_confirm_async (active->prompt, active->cancellable,
+		                          on_prompt_confirm, active_prompt_ref (active));
 
-	finish_prompting (self);
+	} else if (g_strcmp0 (type, GCR_DBUS_PROMPT_TYPE_PASSWORD) == 0) {
+		active->ready = FALSE;
+		gcr_prompt_password_async (active->prompt, active->cancellable,
+		                           on_prompt_password, active_prompt_ref (active));
+
+	} else {
+		g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+		                                               "Invalid type argument");
+		return;
+	}
 
 	g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
+}
 
-	emit_prompter_ready (self);
+static void
+prompter_method_stop_prompting (GcrSystemPrompter *self,
+                                GDBusMethodInvocation *invocation,
+                                GVariant *parameters)
+{
+	const gchar *callback;
+	ActivePrompt *active;
+
+	g_variant_get (parameters, "(&o)", &callback);
+
+	active = g_hash_table_lookup (self->pv->active, callback);
+	if (active != NULL)
+		prompt_stop_prompting (active, TRUE, FALSE);
+
+	g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
+	prompt_possibly_ready (self, NULL);
 }
 
 static void
@@ -801,22 +699,21 @@ prompter_method_call (GDBusConnection *connection,
                       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);
+		prompter_method_begin_prompting (self, invocation, parameters);
+
+	} else if (g_str_equal (method_name, GCR_DBUS_PROMPTER_METHOD_PERFORM)) {
+		prompter_method_perform_prompt (self, invocation, parameters);
 
-	} 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 if (g_str_equal (method_name, GCR_DBUS_PROMPTER_METHOD_STOP)) {
+		prompter_method_stop_prompting (self, invocation, parameters);
 
 	} else {
 		g_return_if_reached ();
 	}
-
-	g_free (path);
 }
 
 static GDBusInterfaceVTable prompter_dbus_vtable = {
@@ -838,16 +735,13 @@ gcr_system_prompter_register (GcrSystemPrompter *self,
 
 	_gcr_debug ("registering prompter");
 
-	self->pv->connection = connection;
-	g_object_add_weak_pointer (G_OBJECT (connection), (gpointer *)&self->pv->connection);
+	self->pv->connection = g_object_ref (connection);
 
 	self->pv->prompter_registered = g_dbus_connection_register_object (connection,
 	                                                                   GCR_DBUS_PROMPTER_OBJECT_PATH,
-	                                                                   _gcr_prompter_interface_info (),
+	                                                                   _gcr_dbus_prompter_interface_info (),
 	                                                                   &prompter_dbus_vtable,
-	                                                                   g_object_ref (self),
-	                                                                   g_object_unref,
-	                                                                   &error);
+	                                                                   self, NULL, &error);
 	if (error != NULL) {
 		g_warning ("error registering prompter %s", egg_error_message (error));
 		g_clear_error (&error);
@@ -856,339 +750,97 @@ gcr_system_prompter_register (GcrSystemPrompter *self,
 
 void
 gcr_system_prompter_unregister (GcrSystemPrompter *self,
-                                GDBusConnection *connection)
+                                gboolean wait)
 {
+	ActivePrompt *active;
+	GVariantBuilder builder;
+	const gchar *sender;
+	GVariant *retval;
+	GList *paths;
+	GList *l;
+
 	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);
 
 	_gcr_debug ("unregistering prompter");
 
-	if (self->pv->invocation)
-		gcr_system_prompter_respond_cancelled (self);
+	paths = g_hash_table_get_keys (self->pv->active);
+	for (l = paths; l != NULL; l = g_list_next (l)) {
+		active = g_hash_table_lookup (self->pv->active, l->data);
+		prompt_stop_prompting (active, TRUE, wait);
+	}
+	g_assert (g_hash_table_size (self->pv->active) == 0);
+	g_list_free (paths);
+
+	paths = g_hash_table_get_keys (self->pv->pending);
+	for (l = paths; l != NULL; l = g_list_next (l)) {
+		sender = g_hash_table_lookup (self->pv->pending, l->data);
+		g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+
+		if (wait) {
+			retval = g_dbus_connection_call_sync (self->pv->connection,
+			                                      sender, l->data,
+			                                      GCR_DBUS_CALLBACK_INTERFACE,
+			                                      GCR_DBUS_CALLBACK_METHOD_DONE,
+			                                      g_variant_new ("()"),
+			                                      G_VARIANT_TYPE ("()"),
+			                                      G_DBUS_CALL_FLAGS_NO_AUTO_START,
+			                                      -1, NULL, NULL);
+			if (retval)
+				g_variant_unref (retval);
+
+		} else {
+			g_dbus_connection_call (self->pv->connection,
+			                        sender, l->data,
+			                        GCR_DBUS_CALLBACK_INTERFACE,
+			                        GCR_DBUS_CALLBACK_METHOD_DONE,
+			                        g_variant_new ("()"),
+			                        G_VARIANT_TYPE ("()"),
+			                        G_DBUS_CALL_FLAGS_NO_AUTO_START,
+			                        -1, NULL, NULL, NULL);
+		}
 
-	finish_prompting (self);
+		if (!g_hash_table_remove (self->pv->pending, l->data))
+			g_assert_not_reached ();
+	}
+	g_assert (g_hash_table_size (self->pv->pending) == 0);
+	g_list_free (paths);
 
-	g_dbus_connection_unregister_object (connection, self->pv->prompter_registered);
+	if (!g_dbus_connection_unregister_object (self->pv->connection, self->pv->prompter_registered))
+		g_assert_not_reached ();
 	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);
+	g_clear_object (&self->pv->connection);
 }
 
 /**
- * gcr_system_prompter_get_caller_window:
- * @self: a prompter
- *
- * Get a string containing the callers window identifier. If the prompter
- * supports making its prompts transient for
- */
-const gchar *
-gcr_system_prompter_get_caller_window (GcrSystemPrompter *self)
-{
-	GVariant *variant;
-	const gchar *window;
-
-	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);
-	window = g_variant_get_string (variant, NULL);
-	if (!window || !window[0])
-		return NULL;
-	return window;
-}
-
-/**
- * gcr_system_prompter_respond_cancelled:
- * @self: a prompter
+ * gcr_system_prompter_new:
  *
- * 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;
-	GVariant *properties;
-
-	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;
-
-	/* Don't send back any changed properties on cancel */
-	properties = g_variant_new_array (G_VARIANT_TYPE ("(ssv)"), NULL, 0);
-
-	_gcr_debug ("responding to with cancelled");
-
-	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 ("(@a(ssv)s)",
-		                                                      properties, ""));
-
-	else if (method && g_str_equal (method, GCR_DBUS_PROMPT_METHOD_CONFIRM))
-		g_dbus_method_invocation_return_value (invocation, g_variant_new ("(@a(ssv)b)",
-		                                                                  properties, FALSE));
-
-	else
-		g_return_if_reached ();
-
-	g_signal_emit (self, signals[RESPONDED], 0);
-}
-
-/**
- * gcr_system_prompter_respond_confirmed:
- * @self: a prompter
- * @password: the password
+ * Create a new system prompter service. This prompter won't do anything unless
+ * you connect to its signals and show appropriate prompts.
  *
- * Used by prompter implementations to let the prompter know when the user
- * has entered a password and clicked 'OK'.
+ * Returns: (transfer full): a new prompter service
  */
-void
-gcr_system_prompter_respond_with_password (GcrSystemPrompter *self,
-                                           const gchar *password)
+GcrSystemPrompter *
+gcr_system_prompter_new (GcrSystemPrompterMode mode,
+                         GType prompt_type)
 {
-	GDBusMethodInvocation *invocation;
-	const gchar *method;
-	GVariant *properties;
-	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;
-
-	method = g_dbus_method_invocation_get_method_name (invocation);
-	g_return_if_fail (method != NULL && g_str_equal (method, GCR_DBUS_PROMPT_METHOD_PASSWORD));
-
-	/* Send back all the properties before we respond */
-	properties = build_changed_properties (self);
-
-	_gcr_debug ("responding to prompt with password");
-
-	exchange = gcr_secret_exchange_send (self->pv->exchange, password, -1);
-	g_dbus_method_invocation_return_value (invocation, g_variant_new ("(@a(ssv)s)",
-	                                                                  properties, exchange));
-	g_free (exchange);
-
-	g_signal_emit (self, signals[RESPONDED], 0);
+	return g_object_new (GCR_TYPE_SYSTEM_PROMPTER,
+	                     "mode", mode,
+	                     "prompt-type", prompt_type,
+	                     NULL);
 }
 
-/**
- * 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)
+GcrSystemPrompterMode
+gcr_system_prompter_get_mode (GcrSystemPrompter *self)
 {
-	GDBusMethodInvocation *invocation;
-	GVariant *properties;
-	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 */
-	properties = build_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));
-
-	_gcr_debug ("responding to prompt with confirm");
-
-	g_dbus_method_invocation_return_value (invocation, g_variant_new ("(@a(ssv)b)",
-	                                                                  properties, TRUE));
-
-	g_signal_emit (self, signals[RESPONDED], 0);
+	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPTER (self), GCR_SYSTEM_PROMPTER_SINGLE);
+	return self->pv->mode;
 }
 
-/**
- * gcr_system_prompter_new:
- *
- * Create a new system prompter service. This prompter won't do anything unless
- * you connect to its signals and show appropriate prompts.
- *
- * Returns: (transfer full): a new prompter service
- */
-GcrSystemPrompter *
-gcr_system_prompter_new (void)
+GType
+gcr_system_prompter_get_prompt_type (GcrSystemPrompter *self)
 {
-	return g_object_new (GCR_TYPE_SYSTEM_PROMPTER, NULL);
+	g_return_val_if_fail (GCR_IS_SYSTEM_PROMPTER (self), 0);
+	return self->pv->prompt_type;
 }
diff --git a/gcr/gcr-system-prompter.h b/gcr/gcr-system-prompter.h
index b697473..8549924 100644
--- a/gcr/gcr-system-prompter.h
+++ b/gcr/gcr-system-prompter.h
@@ -28,12 +28,18 @@
 #ifndef __GCR_SYSTEM_PROMPTER_H__
 #define __GCR_SYSTEM_PROMPTER_H__
 
+#include "gcr-secret-exchange.h"
 #include "gcr-types.h"
 
 #include <glib-object.h>
 
 G_BEGIN_DECLS
 
+typedef enum {
+	GCR_SYSTEM_PROMPTER_SINGLE,
+	GCR_SYSTEM_PROMPTER_MULTIPLE
+} GcrSystemPrompterMode;
+
 #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))
@@ -52,61 +58,26 @@ struct _GcrSystemPrompter {
 
 struct _GcrSystemPrompterClass {
 	GObjectClass parent_class;
-
-	void         (*open)                    (GcrSystemPrompter *self);
-
-	gboolean     (*prompt_password)         (GcrSystemPrompter *self);
-
-	gboolean     (*prompt_confirm)          (GcrSystemPrompter *self);
-
-	void         (*responded)               (GcrSystemPrompter *self);
-
-	void         (*close)                   (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);
+GType                  gcr_system_prompter_get_type                (void) G_GNUC_CONST;
 
-void                 gcr_system_prompter_set_warning               (GcrSystemPrompter *self,
-                                                                    const gchar *warning);
+GcrSystemPrompter *    gcr_system_prompter_new                     (GcrSystemPrompterMode mode,
+                                                                    GType prompt_type);
 
-gboolean             gcr_system_prompter_get_password_new          (GcrSystemPrompter *self);
+GcrSystemPrompterMode  gcr_system_prompter_get_mode                (GcrSystemPrompter *self);
 
-gint                 gcr_system_prompter_get_password_strength     (GcrSystemPrompter *self);
+GType                  gcr_system_prompter_get_prompt_type         (GcrSystemPrompter *self);
 
-void                 gcr_system_prompter_set_password_strength     (GcrSystemPrompter *self,
-                                                                    gint strength);
+gint                   gcr_system_prompter_get_showing             (GcrSystemPrompter *self);
 
-const gchar *        gcr_system_prompter_get_choice_label          (GcrSystemPrompter *self);
+void                   gcr_system_prompter_stop_all                (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_register                (GcrSystemPrompter *self,
+                                                                    GDBusConnection *connection);
 
-void                 gcr_system_prompter_respond_confirmed         (GcrSystemPrompter *self);
+void                   gcr_system_prompter_unregister              (GcrSystemPrompter *self,
+                                                                    gboolean wait);
 
 G_END_DECLS
 
diff --git a/gcr/gcr.h b/gcr/gcr.h
index 6c09364..a7c9f35 100644
--- a/gcr/gcr.h
+++ b/gcr/gcr.h
@@ -48,6 +48,7 @@
 #include "gcr-key-widget.h"
 #include "gcr-import-button.h"
 #include "gcr-list-selector.h"
+#include "gcr-prompt-dialog.h"
 #include "gcr-renderer.h"
 #include "gcr-secure-entry-buffer.h"
 #include "gcr-tree-selector.h"
diff --git a/gcr/gcr.symbols b/gcr/gcr.symbols
index 4119e4c..07128d0 100644
--- a/gcr/gcr.symbols
+++ b/gcr/gcr.symbols
@@ -63,6 +63,7 @@ gcr_list_selector_get_selected
 gcr_list_selector_get_type
 gcr_list_selector_new
 gcr_list_selector_set_selected
+gcr_prompt_dialog_get_type
 gcr_renderer_create
 gcr_renderer_emit_data_changed
 gcr_renderer_get_type
diff --git a/gcr/org.gnome.keyring.Prompt.xml b/gcr/org.gnome.keyring.Prompt.xml
index c29635b..1b76647 100644
--- a/gcr/org.gnome.keyring.Prompt.xml
+++ b/gcr/org.gnome.keyring.Prompt.xml
@@ -2,41 +2,5 @@
  "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"/>
-
-		<!--
-		 * In addition to PropertiesChanged, we send back changed
-		 * properties with the method response. To avoid race conditions
-		-->
-
-		<method name="RequestPassword">
-			<arg name="properties" type="a(ssv)" direction="in"/>
-			<arg name="input" type="s" direction="in"/>
-			<arg name="changed" type="a(ssv)" direction="out"/>
-			<arg name="output" type="s" direction="out"/>
-		</method>
-
-		<method name="RequestConfirm">
-			<arg name="properties" type="a(ssv)" direction="in"/>
-			<arg name="changed" type="a(ssv)" 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
index 8fdda02..189b0f6 100644
--- a/gcr/org.gnome.keyring.Prompter.xml
+++ b/gcr/org.gnome.keyring.Prompter.xml
@@ -1,15 +1,30 @@
 <!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">
+<node>
+	<interface name="org.gnome.keyring.internal.Prompter">
 		<method name="BeginPrompting">
-			<arg name="prompt" type="o" direction="out"/>
+			<arg name="callback" type="o" direction="in"/>
 		</method>
-		<method name="FinishPrompting">
-			<arg name="prompt" type="o" direction="in"/>
+		<method name="PerformPrompt">
+			<arg name="callback" type="o" direction="in"/>
+			<arg name="type" type="s" direction="in"/>
+			<arg name="properties" type="a{sv}" direction="in"/>
+			<arg name="exchange" type="s" direction="in"/>
+		</method>
+		<method name="StopPrompting">
+			<arg name="callback" type="o" direction="in"/>
+		</method>
+	</interface>
+
+	<!-- Called when ready to prompt or a prompt completes -->
+	<interface name="org.gnome.keyring.internal.Prompter.Callback">
+		<method name="PromptReady">
+			<arg name="reply" type="s" direction="in"/>
+			<arg name="properties" type="a{sv}" direction="in"/>
+			<arg name="exchange" type="s" direction="in"/>
+		</method>
+		<method name="PromptClose">
 		</method>
-		<signal name="PrompterReady">
-		</signal>
 	</interface>
 </node>
diff --git a/gcr/tests/frob-system-prompt.c b/gcr/tests/frob-system-prompt.c
index ef01139..6834c56 100644
--- a/gcr/tests/frob-system-prompt.c
+++ b/gcr/tests/frob-system-prompt.c
@@ -49,15 +49,15 @@ on_prompt_clicked (GtkToolButton *button,
 		return;
 	}
 
-	gcr_system_prompt_set_title (prompt, "This is the title");
-	gcr_system_prompt_set_message (prompt, "This is the message");
-	gcr_system_prompt_set_description (prompt, "This is the description");
+	gcr_prompt_set_title (GCR_PROMPT (prompt), "This is the title");
+	gcr_prompt_set_message (GCR_PROMPT (prompt), "This is the message");
+	gcr_prompt_set_description (GCR_PROMPT (prompt), "This is the description");
 
 	caller_id = g_strdup_printf ("%lu", (gulong)GDK_WINDOW_XID (gtk_widget_get_window (parent)));
-	gcr_system_prompt_set_caller_window (prompt, caller_id);
+	gcr_prompt_set_caller_window (GCR_PROMPT (prompt), caller_id);
 	g_free (caller_id);
 
-	password = gcr_system_prompt_password (prompt, NULL, &error);
+	password = gcr_prompt_password_run (GCR_PROMPT (prompt), NULL, &error);
 	if (error != NULL) {
 		g_warning ("couldn't prompt for password: %s", error->message);
 		g_error_free (error);
diff --git a/gcr/tests/test-system-prompt.c b/gcr/tests/test-system-prompt.c
index aec1ce6..bb6d841 100644
--- a/gcr/tests/test-system-prompt.c
+++ b/gcr/tests/test-system-prompt.c
@@ -48,6 +48,51 @@ teardown (Test *test,
 }
 
 static void
+test_open_prompt (Test *test,
+                  gconstpointer unused)
+{
+	GcrSystemPrompt *prompt;
+	GError *error = NULL;
+	gboolean ret;
+	gchar *bus_name;
+
+	prompt = gcr_system_prompt_open_for_prompter (test->prompter_name, 0, NULL, &error);
+	g_assert_no_error (error);
+	g_assert (GCR_IS_SYSTEM_PROMPT (prompt));
+
+	g_object_get (prompt, "bus-name", &bus_name, NULL);
+	g_assert_cmpstr (bus_name, ==, test->prompter_name);
+
+	ret = gcr_system_prompt_close (prompt, NULL, &error);
+	g_assert_no_error (error);
+	g_assert (ret == TRUE);
+
+	g_free (bus_name);
+	g_object_unref (prompt);
+}
+
+static void
+test_open_failure (Test *test,
+                   gconstpointer unused)
+{
+	GcrSystemPrompt *prompt;
+	GDBusConnection *connection;
+	GError *error = NULL;
+
+	connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+	g_assert_no_error (error);
+
+	/* Try to open a prompt where no prompter is running */
+
+	prompt = gcr_system_prompt_open_for_prompter (g_dbus_connection_get_unique_name (connection),
+	                                              0, NULL, &error);
+	g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD);
+	g_assert (prompt == NULL);
+
+	g_object_unref (connection);
+}
+
+static void
 test_prompt_password (Test *test,
                       gconstpointer unused)
 {
@@ -61,12 +106,12 @@ test_prompt_password (Test *test,
 	g_assert_no_error (error);
 	g_assert (GCR_IS_SYSTEM_PROMPT (prompt));
 
-	password = gcr_system_prompt_password (prompt, NULL, &error);
+	password = gcr_prompt_password_run (GCR_PROMPT (prompt), NULL, &error);
 	g_assert_no_error (error);
 	g_assert_cmpstr (password, ==, "booo");
 
 	g_object_unref (prompt);
-	g_assert (!G_IS_OBJECT (prompt));
+	egg_assert_not_object (prompt);
 }
 
 static void
@@ -83,7 +128,7 @@ test_password_in_exchange (Test *test,
 	g_assert_no_error (error);
 	g_assert (GCR_IS_SYSTEM_PROMPT (prompt));
 
-	gcr_system_prompt_password (prompt, NULL, &error);
+	gcr_prompt_password_run (GCR_PROMPT (prompt), NULL, &error);
 	g_assert_no_error (error);
 
 	g_object_get (prompt, "secret-exchange", &exchange, NULL);
@@ -92,7 +137,39 @@ test_password_in_exchange (Test *test,
 
 	g_object_unref (exchange);
 	g_object_unref (prompt);
-	g_assert (!G_IS_OBJECT (prompt));
+	egg_assert_not_object (prompt);
+}
+
+static void
+test_password_custom_exchange (Test *test,
+                               gconstpointer unused)
+{
+	GcrSystemPrompt *prompt;
+	GError *error = NULL;
+	GcrSecretExchange *exchange;
+	const gchar *password;
+
+	exchange = gcr_secret_exchange_new (NULL);
+	gcr_mock_prompter_expect_password_ok ("booo", NULL);
+
+	prompt = g_initable_new (GCR_TYPE_SYSTEM_PROMPT, NULL, &error,
+	                         "timeout-seconds", 0,
+	                         "bus-name", test->prompter_name,
+	                         "secret-exchange", exchange,
+	                         NULL);
+	g_assert_no_error (error);
+	g_assert (GCR_IS_SYSTEM_PROMPT (prompt));
+
+	password = gcr_prompt_password_run (GCR_PROMPT (prompt), NULL, &error);
+	g_assert_cmpstr (password, ==, "booo");
+	g_assert_no_error (error);
+
+	password = gcr_secret_exchange_get_secret (exchange, NULL);
+	g_assert_cmpstr (password, ==, "booo");
+
+	g_object_unref (exchange);
+	g_object_unref (prompt);
+	egg_assert_not_object (prompt);
 }
 
 static void
@@ -127,12 +204,12 @@ test_async_password (Test *test,
 	g_assert (GCR_IS_SYSTEM_PROMPT (prompt));
 	g_clear_object (&result);
 
-	gcr_system_prompt_password_async (prompt, NULL,
-	                                  on_async_result, &result);
+	gcr_prompt_password_async (GCR_PROMPT (prompt), NULL,
+	                           on_async_result, &result);
 	g_assert (result == NULL);
 	egg_test_wait ();
 
-	password = gcr_system_prompt_password_finish (prompt, result, &error);
+	password = gcr_prompt_password_finish (GCR_PROMPT (prompt), result, &error);
 	g_assert_no_error (error);
 	g_assert_cmpstr (password, ==, "booo");
 	g_clear_object (&result);
@@ -154,12 +231,12 @@ test_prompt_confirm (Test *test,
 	g_assert_no_error (error);
 	g_assert (GCR_IS_SYSTEM_PROMPT (prompt));
 
-	ret = gcr_system_prompt_confirm (prompt, NULL, &error);
+	ret = gcr_prompt_confirm_run (GCR_PROMPT (prompt), NULL, &error);
 	g_assert_no_error (error);
 	g_assert (ret == TRUE);
 
 	g_object_unref (prompt);
-	g_assert (!G_IS_OBJECT (prompt));
+	egg_assert_not_object (prompt);
 }
 
 static void
@@ -184,18 +261,18 @@ test_async_confirm (Test *test,
 	g_assert (GCR_IS_SYSTEM_PROMPT (prompt));
 	g_clear_object (&result);
 
-	gcr_system_prompt_confirm_async (prompt, NULL,
-	                                 on_async_result, &result);
+	gcr_prompt_confirm_async (GCR_PROMPT (prompt), NULL,
+	                          on_async_result, &result);
 	g_assert (result == NULL);
 	egg_test_wait ();
 
-	confirm = gcr_system_prompt_confirm_finish (prompt, result, &error);
+	confirm = gcr_prompt_confirm_finish (GCR_PROMPT (prompt), result, &error);
 	g_assert_no_error (error);
 	g_assert (confirm == TRUE);
 	g_clear_object (&result);
 
 	g_object_unref (prompt);
-	g_assert (!G_IS_OBJECT (prompt));
+	egg_assert_not_object (prompt);
 }
 
 static void
@@ -212,12 +289,12 @@ test_cancel_password (Test *test,
 	g_assert_no_error (error);
 	g_assert (GCR_IS_SYSTEM_PROMPT (prompt));
 
-	password = gcr_system_prompt_password (prompt, NULL, &error);
+	password = gcr_prompt_password_run (GCR_PROMPT (prompt), NULL, &error);
 	g_assert_no_error (error);
 	g_assert_cmpstr (password, ==, NULL);
 
 	g_object_unref (prompt);
-	g_assert (!G_IS_OBJECT (prompt));
+	egg_assert_not_object (prompt);
 }
 
 static void
@@ -234,19 +311,20 @@ test_cancel_confirm (Test *test,
 	g_assert_no_error (error);
 	g_assert (GCR_IS_SYSTEM_PROMPT (prompt));
 
-	ret = gcr_system_prompt_confirm (prompt, NULL, &error);
+	ret = gcr_prompt_confirm_run (GCR_PROMPT (prompt), NULL, &error);
 	g_assert_no_error (error);
 	g_assert (ret == FALSE);
 
 	g_object_unref (prompt);
-	g_assert (!G_IS_OBJECT (prompt));
+	egg_assert_not_object (prompt);
 }
 
 static void
 test_prompt_properties (Test *test,
                         gconstpointer unused)
 {
-	GcrSystemPrompt *prompt;
+	GcrSystemPrompt *sprompt;
+	GcrPrompt *prompt;
 	GError *error = NULL;
 	gboolean ret;
 
@@ -258,13 +336,14 @@ test_prompt_properties (Test *test,
 	                                     "choice-label", "My Choice",
 	                                     "choice-chosen", TRUE,
 	                                     "password-new", TRUE,
-	                                     "password-strength", 2,
+	                                     "password-strength", 0,
 	                                     NULL);
 
-	prompt = gcr_system_prompt_open_for_prompter (test->prompter_name, 0, NULL, &error);
+	sprompt = gcr_system_prompt_open_for_prompter (test->prompter_name, 0, NULL, &error);
 	g_assert_no_error (error);
-	g_assert (GCR_IS_SYSTEM_PROMPT (prompt));
+	g_assert (GCR_IS_SYSTEM_PROMPT (sprompt));
 
+	prompt = GCR_PROMPT (sprompt);
 	g_object_set (prompt,
 	              "title", "Other Title",
 	              "choice-label", "Other Choice",
@@ -276,33 +355,60 @@ test_prompt_properties (Test *test,
 	              "choice-chosen", TRUE,
 	              NULL);
 
-	g_assert_cmpstr (gcr_system_prompt_get_title (prompt), ==, "Other Title");
-	g_assert_cmpstr (gcr_system_prompt_get_choice_label (prompt), ==, "Other Choice");
-	g_assert_cmpstr (gcr_system_prompt_get_description (prompt), ==, "Other Description");
-	g_assert_cmpstr (gcr_system_prompt_get_message (prompt), ==, "Other Message");
-	g_assert_cmpstr (gcr_system_prompt_get_caller_window (prompt), ==, "01012");
-	g_assert_cmpstr (gcr_system_prompt_get_warning (prompt), ==, "Other Warning");
-	g_assert (gcr_system_prompt_get_password_new (prompt) == FALSE);
-	g_assert (gcr_system_prompt_get_choice_chosen (prompt) == TRUE);
-
-	gcr_system_prompt_set_title (prompt, "My Title");
-	gcr_system_prompt_set_choice_label (prompt, "My Choice");
-	gcr_system_prompt_set_description (prompt, "My Description");
-	gcr_system_prompt_set_message (prompt, "My Message");
-	gcr_system_prompt_set_caller_window (prompt, "01010");
-	gcr_system_prompt_set_warning (prompt, "My Warning");
-	gcr_system_prompt_set_password_new (prompt, TRUE);
-	gcr_system_prompt_set_choice_chosen (prompt, FALSE);
-
-	ret = gcr_system_prompt_confirm (prompt, NULL, &error);
+	g_assert_cmpstr (gcr_prompt_get_title (prompt), ==, "Other Title");
+	g_assert_cmpstr (gcr_prompt_get_choice_label (prompt), ==, "Other Choice");
+	g_assert_cmpstr (gcr_prompt_get_description (prompt), ==, "Other Description");
+	g_assert_cmpstr (gcr_prompt_get_message (prompt), ==, "Other Message");
+	g_assert_cmpstr (gcr_prompt_get_caller_window (prompt), ==, "01012");
+	g_assert_cmpstr (gcr_prompt_get_warning (prompt), ==, "Other Warning");
+	g_assert (gcr_prompt_get_password_new (prompt) == FALSE);
+	g_assert (gcr_prompt_get_choice_chosen (prompt) == TRUE);
+
+	gcr_prompt_set_title (prompt, "My Title");
+	gcr_prompt_set_choice_label (prompt, "My Choice");
+	gcr_prompt_set_description (prompt, "My Description");
+	gcr_prompt_set_message (prompt, "My Message");
+	gcr_prompt_set_caller_window (prompt, "01010");
+	gcr_prompt_set_warning (prompt, "My Warning");
+	gcr_prompt_set_password_new (prompt, TRUE);
+	gcr_prompt_set_choice_chosen (prompt, TRUE);
+
+	ret = gcr_prompt_confirm_run (prompt, NULL, &error);
 	g_assert_no_error (error);
 	g_assert (ret == TRUE);
 
-	g_assert (gcr_system_prompt_get_choice_chosen (prompt) == TRUE);
-	g_assert_cmpint (gcr_system_prompt_get_password_strength (prompt), ==, 2);
+	g_assert (gcr_prompt_get_choice_chosen (prompt) == TRUE);
+	g_assert_cmpint (gcr_prompt_get_password_strength (prompt), ==, 0);
 
 	g_object_unref (prompt);
-	g_assert (!G_IS_OBJECT (prompt));
+	egg_assert_not_object (prompt);
+}
+
+static void
+test_prompt_properties_unset (Test *test,
+                              gconstpointer unused)
+{
+	GcrSystemPrompt *sprompt;
+	GcrPrompt *prompt;
+	GError *error = NULL;
+
+	sprompt = gcr_system_prompt_open_for_prompter (test->prompter_name, 0, NULL, &error);
+	g_assert_no_error (error);
+	g_assert (GCR_IS_SYSTEM_PROMPT (sprompt));
+	prompt = GCR_PROMPT (sprompt);
+
+	g_assert_cmpstr (gcr_prompt_get_title (prompt), ==, NULL);
+	g_assert_cmpstr (gcr_prompt_get_choice_label (prompt), ==, NULL);
+	g_assert_cmpstr (gcr_prompt_get_description (prompt), ==, NULL);
+	g_assert_cmpstr (gcr_prompt_get_message (prompt), ==, NULL);
+	g_assert_cmpstr (gcr_prompt_get_caller_window (prompt), ==, NULL);
+	g_assert_cmpstr (gcr_prompt_get_warning (prompt), ==, NULL);
+	g_assert (gcr_prompt_get_password_new (prompt) == FALSE);
+	g_assert (gcr_prompt_get_choice_chosen (prompt) == FALSE);
+	g_assert_cmpint (gcr_prompt_get_password_strength (prompt), ==, 0);
+
+	g_object_unref (prompt);
+	egg_assert_not_object (prompt);
 }
 
 static void
@@ -312,39 +418,32 @@ test_prompt_close (Test *test,
 	GcrSystemPrompt *prompt;
 	GcrSystemPrompt *prompt2;
 	GError *error = NULL;
-	gboolean showing;
 	gboolean ret;
 
 	gcr_mock_prompter_expect_confirm_ok (NULL);
 
-	prompt = gcr_system_prompt_open_for_prompter (test->prompter_name, 0, NULL, &error);
+	prompt = gcr_system_prompt_open_for_prompter (test->prompter_name, 1, NULL, &error);
 	g_assert_no_error (error);
 	g_assert (GCR_IS_SYSTEM_PROMPT (prompt));
 
-	prompt2 = gcr_system_prompt_open_for_prompter (test->prompter_name, 0, NULL, &error);
+	prompt2 = gcr_system_prompt_open_for_prompter (test->prompter_name, 1, NULL, &error);
 	g_assert_error (error, GCR_SYSTEM_PROMPT_ERROR, GCR_SYSTEM_PROMPT_IN_PROGRESS);
 	g_clear_error (&error);
 	g_assert (prompt2 == NULL);
 
-	ret = gcr_system_prompt_confirm (prompt, NULL, &error);
+	ret = gcr_prompt_confirm_run (GCR_PROMPT (prompt), NULL, &error);
 	g_assert_no_error (error);
 	g_assert (ret == TRUE);
 
-	prompt2 = gcr_system_prompt_open_for_prompter (test->prompter_name, 0, NULL, &error);
+	prompt2 = gcr_system_prompt_open_for_prompter (test->prompter_name, 1, NULL, &error);
 	g_assert_error (error, GCR_SYSTEM_PROMPT_ERROR, GCR_SYSTEM_PROMPT_IN_PROGRESS);
 	g_clear_error (&error);
 	g_assert (prompt2 == NULL);
 
-	showing = gcr_mock_prompter_get_showing ();
-	g_assert (showing == TRUE);
-
 	gcr_system_prompt_close (prompt, NULL, &error);
 	g_assert_no_error (error);
 
-	showing = gcr_mock_prompter_get_showing ();
-	g_assert (showing == FALSE);
-
-	prompt2 = gcr_system_prompt_open_for_prompter (test->prompter_name, 0, NULL, &error);
+	prompt2 = gcr_system_prompt_open_for_prompter (test->prompter_name, 1, NULL, &error);
 	g_assert_no_error (error);
 	g_assert (GCR_IS_SYSTEM_PROMPT (prompt2));
 
@@ -352,12 +451,12 @@ test_prompt_close (Test *test,
 	g_assert (!G_IS_OBJECT (prompt));
 
 	g_object_unref (prompt2);
-	g_assert (!G_IS_OBJECT (prompt2));
+	egg_assert_not_object (prompt);
 }
 
 static void
-test_finish_cancels (Test *test,
-                     gconstpointer unused)
+test_close_cancels (Test *test,
+                    gconstpointer unused)
 {
 	GcrSystemPrompt *prompt;
 	GError *error = NULL;
@@ -371,20 +470,20 @@ test_finish_cancels (Test *test,
 	g_assert_no_error (error);
 	g_assert (GCR_IS_SYSTEM_PROMPT (prompt));
 
-	gcr_system_prompt_password_async (prompt, NULL, on_async_result, &result);
+	gcr_prompt_password_async (GCR_PROMPT (prompt), NULL, on_async_result, &result);
 
 	gcr_system_prompt_close (prompt, NULL, &error);
 	g_assert_no_error (error);
 
 	egg_test_wait ();
 
-	password = gcr_system_prompt_password_finish (prompt, result, &error);
+	password = gcr_prompt_password_finish (GCR_PROMPT (prompt), result, &error);
 	g_assert_no_error (error);
 	g_assert (password == NULL);
 	g_clear_object (&result);
 
 	g_object_unref (prompt);
-	g_assert (!G_IS_OBJECT (prompt));
+	egg_assert_not_object (prompt);
 }
 
 int
@@ -394,16 +493,20 @@ main (int argc, char **argv)
 	g_test_init (&argc, &argv, NULL);
 	g_set_prgname ("test-system-prompt");
 
+	g_test_add ("/gcr/system-prompt/open", Test, NULL, setup, test_open_prompt, teardown);
+	g_test_add ("/gcr/system-prompt/open-failure", Test, NULL, setup, test_open_failure, teardown);
 	g_test_add ("/gcr/system-prompt/password", Test, NULL, setup, test_prompt_password, teardown);
 	g_test_add ("/gcr/system-prompt/password-async", Test, NULL, setup, test_async_password, teardown);
 	g_test_add ("/gcr/system-prompt/password-cancel", Test, NULL, setup, test_cancel_password, teardown);
 	g_test_add ("/gcr/system-prompt/password-in-exchange", Test, NULL, setup, test_password_in_exchange, teardown);
+	g_test_add ("/gcr/system-prompt/password-custom-exchange", Test, NULL, setup, test_password_custom_exchange, teardown);
 	g_test_add ("/gcr/system-prompt/confirm", Test, NULL, setup, test_prompt_confirm, teardown);
 	g_test_add ("/gcr/system-prompt/confirm-async", Test, NULL, setup, test_async_confirm, teardown);
 	g_test_add ("/gcr/system-prompt/confirm-cancel", Test, NULL, setup, test_cancel_confirm, teardown);
 	g_test_add ("/gcr/system-prompt/properties", Test, NULL, setup, test_prompt_properties, teardown);
+	g_test_add ("/gcr/system-prompt/properties-unset", Test, NULL, setup, test_prompt_properties_unset, teardown);
 	g_test_add ("/gcr/system-prompt/close", Test, NULL, setup, test_prompt_close, teardown);
-	g_test_add ("/gcr/system-prompt/finish-cancel", Test, NULL, setup, test_finish_cancels, teardown);
+	g_test_add ("/gcr/system-prompt/close-cancels", Test, NULL, setup, test_close_cancels, teardown);
 
 	return egg_tests_run_with_loop ();
 }
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 29609a8..ec60cdd 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -25,7 +25,9 @@ gcr/gcr-parser.c
 gcr/gcr-pkcs11-import-dialog.c
 [type: gettext/glade]gcr/gcr-pkcs11-import-dialog.ui
 gcr/gcr-pkcs11-import-interaction.c
+gcr/gcr-prompt-dialog.c
 gcr/gcr-subject-public-key.c
+gcr/gcr-system-prompt.c
 gcr/gcr-trust.c
 [type: gettext/glade]gcr/gcr-unlock-options-widget.ui
 gcr/gcr-unlock-renderer.c



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