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