diff options
author | Stef Walter <stefw@collabora.co.uk> | 2011-10-28 12:06:03 +0200 |
---|---|---|
committer | Stef Walter <stefw@collabora.co.uk> | 2011-12-13 21:45:09 +0100 |
commit | 316bb56cadbdd56ee0e897dcc571dec7ea8ac77a (patch) | |
tree | 83916f468d2c13b0d6e3e5e842d53b91ccb2e608 | |
parent | 19f5cd44bbcb5771c1519a0f7d55b46043c5d858 (diff) | |
download | gcr-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.ac | 2 | ||||
-rw-r--r-- | docs/reference/gcr/Makefile.am | 2 | ||||
-rw-r--r-- | docs/reference/gcr/gcr-sections.txt | 5 | ||||
-rw-r--r-- | gcr/Makefile.am | 40 | ||||
-rw-r--r-- | gcr/gcr-base.h | 3 | ||||
-rw-r--r-- | gcr/gcr-base.symbols | 59 | ||||
-rw-r--r-- | gcr/gcr-dbus-constants.h | 62 | ||||
-rw-r--r-- | gcr/gcr-debug.c | 1 | ||||
-rw-r--r-- | gcr/gcr-debug.h | 1 | ||||
-rw-r--r-- | gcr/gcr-mock-prompter.c | 456 | ||||
-rw-r--r-- | gcr/gcr-mock-prompter.h | 62 | ||||
-rw-r--r-- | gcr/gcr-prompter-tool.c | 630 | ||||
-rw-r--r-- | gcr/gcr-system-prompt.c | 1330 | ||||
-rw-r--r-- | gcr/gcr-system-prompt.h | 174 | ||||
-rw-r--r-- | gcr/gcr-system-prompter.c | 1162 | ||||
-rw-r--r-- | gcr/gcr-system-prompter.h | 113 | ||||
-rw-r--r-- | gcr/org.gnome.keyring.Prompt.xml | 39 | ||||
-rw-r--r-- | gcr/org.gnome.keyring.Prompter.xml | 15 | ||||
-rw-r--r-- | gcr/tests/Makefile.am | 3 |
19 files changed, 4154 insertions, 5 deletions
diff --git a/configure.ac b/configure.ac index e21581e..eff8f86 100644 --- a/configure.ac +++ b/configure.ac @@ -70,7 +70,7 @@ PKG_CHECK_MODULES(GLIB, gmodule-no-export-2.0 gthread-2.0 gobject-2.0 - gio-2.0) + gio-2.0 gio-unix-2.0) AC_SUBST(GLIB_CFLAGS) AC_SUBST(GLIB_LIBS) diff --git a/docs/reference/gcr/Makefile.am b/docs/reference/gcr/Makefile.am index 6dc143f..a984601 100644 --- a/docs/reference/gcr/Makefile.am +++ b/docs/reference/gcr/Makefile.am @@ -64,6 +64,8 @@ IGNORE_HFILES= \ gcr-certificate-basics-widget.h \ gcr-certificate-details-widget.h \ gcr-certificate-request-renderer.h \ + gcr-dbus-constants.h \ + gcr-dbus-generated.h \ gcr-deprecated.h \ gcr-deprecated-base.h \ gcr-display-scrolled.h \ diff --git a/docs/reference/gcr/gcr-sections.txt b/docs/reference/gcr/gcr-sections.txt index 6fa835b..45840d8 100644 --- a/docs/reference/gcr/gcr-sections.txt +++ b/docs/reference/gcr/gcr-sections.txt @@ -706,3 +706,8 @@ GcrOpensshPubCallback GCR_TYPE_IMPORTER_PROMPT_BEHAVIOR gcr_importer_prompt_behavior_get_type </SECTION> + +<SECTION> +<FILE>GcrPrompter1</FILE> +<SUBSECTION Private> +</SECTION> diff --git a/gcr/Makefile.am b/gcr/Makefile.am index 92af49f..adeb3e6 100644 --- a/gcr/Makefile.am +++ b/gcr/Makefile.am @@ -27,10 +27,13 @@ HEADER_BASE_FILES = \ gcr-importer.h \ gcr-import-interaction.h \ gcr-library.h \ + gcr-mock-prompter.h \ gcr-parser.h \ gcr-pkcs11-certificate.h \ gcr-secret-exchange.h \ gcr-simple-certificate.h \ + gcr-system-prompt.h \ + gcr-system-prompter.h \ gcr-trust.h \ gcr-types.h \ gcr-union-collection.h \ @@ -76,7 +79,9 @@ INCLUDES = \ $(GLIB_CFLAGS) \ $(LIBGCRYPT_CFLAGS) \ $(P11_KIT_CFLAGS) \ - -DG_LOG_DOMAIN=\"Gcr\" + -DG_LOG_DOMAIN=\"Gcr\" \ + -DGCR_API_SUBJECT_TO_CHANGE \ + -DLOCALEDIR=\""$(datadir)/locale"\" lib_LTLIBRARIES = \ libgcr-base-@GCR_MAJOR@.la \ @@ -85,7 +90,8 @@ lib_LTLIBRARIES = \ BUILT_BASE_FILES = \ gcr-marshal.c gcr-marshal.h \ gcr-enum-types-base.c gcr-enum-types-base.h \ - gcr-oids.c gcr-oids.h + gcr-oids.c gcr-oids.h \ + gcr-dbus-generated.c gcr-dbus-generated.h BUILT_UI_FILES = \ gcr-marshal.c gcr-marshal.h \ @@ -104,6 +110,7 @@ libgcr_base_@GCR_MAJOR@_la_SOURCES = \ gcr-certificate-request.c gcr-certificate-request.h \ gcr-collection.c gcr-collection.h \ gcr-comparable.c gcr-comparable.h \ + gcr-dbus-constants.h \ gcr-debug.c gcr-debug.h \ gcr-filter-collection.c gcr-filter-collection.h \ gcr-fingerprint.c gcr-fingerprint.h \ @@ -121,6 +128,7 @@ libgcr_base_@GCR_MAJOR@_la_SOURCES = \ gcr-library.c gcr-library.h \ gcr-memory.c \ gcr-memory-icon.c gcr-memory-icon.h \ + gcr-mock-prompter.c gcr-mock-prompter.h \ gcr-openpgp.c gcr-openpgp.h \ gcr-openssh.c gcr-openssh.h \ gcr-parser.c gcr-parser.h \ @@ -132,6 +140,8 @@ libgcr_base_@GCR_MAJOR@_la_SOURCES = \ gcr-simple-collection.c gcr-simple-collection.h \ gcr-single-collection.c gcr-single-collection.h \ gcr-subject-public-key.c gcr-subject-public-key.h \ + gcr-system-prompt.c gcr-system-prompt.h \ + gcr-system-prompter.c gcr-system-prompter.h \ gcr-trust.c gcr-trust.h \ gcr-types.h \ gcr-union-collection.c gcr-union-collection.h \ @@ -180,7 +190,6 @@ libgcr_@GCR_MAJOR@_la_SOURCES = \ libgcr_base_@GCR_MAJOR@_la_CFLAGS = \ -DGCK_API_SUBJECT_TO_CHANGE \ - -DGCR_API_SUBJECT_TO_CHANGE \ -DP11_KIT_API_SUBJECT_TO_CHANGE \ -DGCR_COMPILATION @@ -247,6 +256,18 @@ gcr-oids.c: gcr-oids.list gcr-mkoids gcr-oids.h: gcr-oids.c +DBUS_XML_DEFINITIONS = \ + org.gnome.keyring.Prompter.xml \ + org.gnome.keyring.Prompt.xml + +gcr-dbus-generated.c: $(DBUS_XML_DEFINITIONS) + $(AM_V_GEN) gdbus-codegen --interface-prefix org.gnome.keyring. \ + --generate-c-code gcr-dbus-generated --c-namespace Gcr \ + $(DBUS_XML_DEFINITIONS) + $(AM_V_GEN) sed -i -e 's/gcr_/_gcr_/g' gcr-dbus-generated.[ch] + +gcr-dbus-generated.h: gcr-dbus-generated.c + pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = gcr-$(GCR_MAJOR).pc gcr-base-$(GCR_MAJOR).pc @@ -304,6 +325,18 @@ gcr_viewer_LDADD = \ $(GLIB_LIBS) \ $(GTK_LIBS) +libexec_PROGRAMS = gcr-prompter + +gcr_prompter_SOURCES = \ + gcr-prompter-tool.c + +gcr_prompter_CFLAGS = \ + $(GTK_CFLAGS) + +gcr_prompter_LDADD = \ + $(builddir)/libgcr-$(GCR_MAJOR).la \ + $(GTK_LIBS) + # ------------------------------------------------------------------ # INTROSPECTION @@ -372,6 +405,7 @@ EXTRA_DIST = \ gcr-mkoids \ $(ui_DATA) \ $(conf_DATA) \ + org.gnome.keyring.Prompter.xml \ gcr-enum-types.h.template \ gcr-enum-types.c.template \ gcr.symbols \ diff --git a/gcr/gcr-base.h b/gcr/gcr-base.h index ff871cb..ff34ddc 100644 --- a/gcr/gcr-base.h +++ b/gcr/gcr-base.h @@ -43,11 +43,14 @@ #include "gcr-icons.h" #include "gcr-importer.h" #include "gcr-library.h" +#include "gcr-mock-prompter.h" #include "gcr-parser.h" #include "gcr-pkcs11-certificate.h" #include "gcr-secret-exchange.h" #include "gcr-simple-certificate.h" #include "gcr-simple-collection.h" +#include "gcr-system-prompt.h" +#include "gcr-system-prompter.h" #include "gcr-trust.h" #include "gcr-union-collection.h" #include "gcr-unlock-options.h" diff --git a/gcr/gcr-base.symbols b/gcr/gcr-base.symbols index 4061745..9256194 100644 --- a/gcr/gcr-base.symbols +++ b/gcr/gcr-base.symbols @@ -100,6 +100,12 @@ gcr_import_interaction_supplement gcr_import_interaction_supplement_async gcr_import_interaction_supplement_finish gcr_import_interaction_supplement_prep +gcr_mock_prompter_expect_confirm_cancel +gcr_mock_prompter_expect_confirm_ok +gcr_mock_prompter_expect_password_cancel +gcr_mock_prompter_expect_password_ok +gcr_mock_prompter_get_type +gcr_mock_prompter_new gcr_parsed_get_attributes gcr_parsed_get_data gcr_parsed_get_description @@ -157,6 +163,59 @@ gcr_simple_collection_contains gcr_simple_collection_get_type gcr_simple_collection_new gcr_simple_collection_remove +gcr_system_prompt_close +gcr_system_prompt_confirm +gcr_system_prompt_confirm_async +gcr_system_prompt_confirm_finish +gcr_system_prompter_get_caller_window +gcr_system_prompter_get_choice_chosen +gcr_system_prompter_get_choice_label +gcr_system_prompter_get_description +gcr_system_prompter_get_message +gcr_system_prompter_get_password_new +gcr_system_prompter_get_password_strength +gcr_system_prompter_get_title +gcr_system_prompter_get_type +gcr_system_prompter_get_warning +gcr_system_prompter_new +gcr_system_prompter_register +gcr_system_prompter_respond_cancelled +gcr_system_prompter_respond_confirmed +gcr_system_prompter_respond_with_password +gcr_system_prompt_error_get_domain +gcr_system_prompt_error_get_type +gcr_system_prompter_set_choice_chosen +gcr_system_prompter_set_password_strength +gcr_system_prompter_set_warning +gcr_system_prompter_unregister +gcr_system_prompt_get_caller_window +gcr_system_prompt_get_choice_chosen +gcr_system_prompt_get_choice_label +gcr_system_prompt_get_description +gcr_system_prompt_get_message +gcr_system_prompt_get_password_new +gcr_system_prompt_get_password_strength +gcr_system_prompt_get_secret_exchange +gcr_system_prompt_get_title +gcr_system_prompt_get_type +gcr_system_prompt_get_warning +gcr_system_prompt_open +gcr_system_prompt_open_async +gcr_system_prompt_open_finish +gcr_system_prompt_open_for_prompter +gcr_system_prompt_open_for_prompter_async +gcr_system_prompt_password +gcr_system_prompt_password_async +gcr_system_prompt_password_finish +gcr_system_prompt_set_caller_window +gcr_system_prompt_set_choice_chosen +gcr_system_prompt_set_choice_label +gcr_system_prompt_set_description +gcr_system_prompt_set_message +gcr_system_prompt_set_password_new +gcr_system_prompt_set_secret_exchange +gcr_system_prompt_set_title +gcr_system_prompt_set_warning gcr_trust_add_pinned_certificate gcr_trust_add_pinned_certificate_async gcr_trust_add_pinned_certificate_finish diff --git a/gcr/gcr-dbus-constants.h b/gcr/gcr-dbus-constants.h new file mode 100644 index 0000000..d47c6f3 --- /dev/null +++ b/gcr/gcr-dbus-constants.h @@ -0,0 +1,62 @@ +/* + * gnome-keyring + * + * Copyright (C) 2011 Stefan Walter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Author: Stef Walter <stef@thewalter.net> + */ + +#ifndef __GCR_DBUS_CONSTANTS_H__ +#define __GCR_DBUS_CONSTANTS_H__ + +#include <glib.h> + +G_BEGIN_DECLS + +#define GCR_DBUS_PROMPTER_BUS_NAME "org.gnome.keyring.Prompter" +#define GCR_DBUS_PROMPTER_MOCK_BUS_NAME "org.gnome.keyring.MockPrompter" + +#define GCR_DBUS_PROMPTER_OBJECT_PATH "/org/gnome/keyring/Prompter" + +#define GCR_DBUS_PROMPTER_INTERFACE "org.gnome.keyring.Prompter" + +#define GCR_DBUS_PROMPTER_METHOD_BEGIN "BeginPrompting" +#define GCR_DBUS_PROMPTER_METHOD_FINISH "FinishPrompting" + +#define GCR_DBUS_PROMPT_INTERFACE "org.gnome.keyring.Prompter.Prompt" + +#define GCR_DBUS_PROMPT_ERROR_IN_PROGRESS "org.gnome.keyring.Prompter.InProgress" +#define GCR_DBUS_PROMPT_ERROR_NOT_HAPPENING "org.gnome.keyring.Prompter.NotHappening" +#define GCR_DBUS_PROMPT_ERROR_FAILED "org.gnome.keyring.Prompter.Failed" + +#define GCR_DBUS_PROMPT_PROPERTY_TITLE "Title" +#define GCR_DBUS_PROMPT_PROPERTY_MESSAGE "Message" +#define GCR_DBUS_PROMPT_PROPERTY_DESCRIPTION "Description" +#define GCR_DBUS_PROMPT_PROPERTY_WARNING "Warning" +#define GCR_DBUS_PROMPT_PROPERTY_CHOICE_LABEL "ChoiceLabel" +#define GCR_DBUS_PROMPT_PROPERTY_CHOICE_CHOSEN "ChoiceChosen" +#define GCR_DBUS_PROMPT_PROPERTY_PASSWORD_NEW "PasswordNew" +#define GCR_DBUS_PROMPT_PROPERTY_PASSWORD_STRENGTH "PasswordStrength" +#define GCR_DBUS_PROMPT_PROPERTY_CALLER_WINDOW "CallerWindow" + +#define GCR_DBUS_PROMPT_METHOD_PASSWORD "RequestPassword" +#define GCR_DBUS_PROMPT_METHOD_CONFIRM "RequestConfirm" + +G_END_DECLS + +#endif /* __GCR_DBUS_CONSTANTS_H__ */ diff --git a/gcr/gcr-debug.c b/gcr/gcr-debug.c index 9177e58..1f74250 100644 --- a/gcr/gcr-debug.c +++ b/gcr/gcr-debug.c @@ -43,6 +43,7 @@ static GDebugKey keys[] = { { "trust", GCR_DEBUG_TRUST }, { "import", GCR_DEBUG_IMPORT }, { "key", GCR_DEBUG_KEY }, + { "prompt", GCR_DEBUG_PROMPT }, { 0, } }; diff --git a/gcr/gcr-debug.h b/gcr/gcr-debug.h index 5063b77..54c13d5 100644 --- a/gcr/gcr-debug.h +++ b/gcr/gcr-debug.h @@ -35,6 +35,7 @@ typedef enum { GCR_DEBUG_TRUST = 1 << 5, GCR_DEBUG_IMPORT = 1 << 6, GCR_DEBUG_KEY = 1 << 7, + GCR_DEBUG_PROMPT = 1 << 8, } GcrDebugFlags; gboolean _gcr_debug_flag_is_set (GcrDebugFlags flag); diff --git a/gcr/gcr-mock-prompter.c b/gcr/gcr-mock-prompter.c new file mode 100644 index 0000000..412c101 --- /dev/null +++ b/gcr/gcr-mock-prompter.c @@ -0,0 +1,456 @@ +/* + * gnome-keyring + * + * Copyright (C) 2011 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Author: Stef Walter <stfew@collabora.co.uk> + */ + +#include "config.h" + +#include "gcr-mock-prompter.h" + +#include "egg/egg-error.h" + +#include <gobject/gvaluecollector.h> + +#include <string.h> + +/** + * SECTION:gcr-mock-prompter + * @title: GcrMockPrompter + * @short_description: XXX + * + * XXXX + */ + +/** + * GcrMockPrompter: + * + * XXX + */ + +/** + * GcrMockPrompterClass: + * + * The class for #GcrMockPrompter. + */ + +#define GCR_IS_MOCK_PROMPTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_MOCK_PROMPTER)) +#define GCR_MOCK_PROMPTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_MOCK_PROMPTER, GcrMockPromptClass)) +#define GCR_MOCK_PROMPTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_MOCK_PROMPTER, GcrMockPromptClass)) + +typedef struct _GcrMockPrompterClass GcrMockPrompterClass; +typedef struct _GcrMockPrompterPrivate GcrMockPrompterPrivate; + +enum { + PROP_0, + PROP_CONNECTION, +}; + +typedef struct { + gboolean proceed; + gchar *password; + GList *properties; +} MockResponse; + +struct _GcrMockPrompter { + GcrSystemPrompter parent; + GDBusConnection *connection; + GQueue *responses; +}; + +struct _GcrMockPrompterClass { + GcrSystemPrompterClass parent_class; +}; + +G_DEFINE_TYPE (GcrMockPrompter, gcr_mock_prompter, GCR_TYPE_SYSTEM_PROMPTER); + +static void +mock_property_free (gpointer data) +{ + GParameter *param = data; + g_value_unset (¶m->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, ¶m->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, ¶m->value)) { + gchar *expected = g_strdup_value_contents (¶m->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 (¶meter->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) |