From f2e275f25eebd5456967d431580668fcbbcf0601 Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Wed, 11 Jan 2012 07:44:32 +0100 Subject: [PATCH] Prompting and item deletion --- build/glib.supp | 8 + build/unknown.supp | 25 + egg/egg-testing.c | 11 + egg/egg-testing.h | 2 + library/Makefile.am | 2 + library/gsecret-collection.h | 1 - library/gsecret-item.c | 75 +-- library/gsecret-item.h | 2 +- library/gsecret-password.c | 440 +++++++++++++++++ library/gsecret-password.h | 87 ++++ library/gsecret-private.h | 24 +- library/gsecret-prompt.c | 433 ++++++++++++++++ library/gsecret-prompt.h | 35 +- library/gsecret-service.c | 710 ++++++++++++++++++++++++++- library/gsecret-service.h | 145 +++++- library/gsecret-types.h | 30 ++ library/gsecret-util.c | 75 +++ library/gsecret-value.h | 4 +- library/org.freedesktop.Secrets.xml | 142 +++--- library/tests/Makefile.am | 2 + library/tests/mock-service-delete.py | 17 + library/tests/mock-service-prompt.py | 42 ++ library/tests/mock/service.py | 72 ++- library/tests/test-password.c | 137 ++++++ library/tests/test-prompt.c | 429 ++++++++++++++++ library/tests/test-service.c | 214 +++++++- library/tests/test-session.c | 4 +- 27 files changed, 2982 insertions(+), 186 deletions(-) create mode 100644 library/gsecret-password.c create mode 100644 library/gsecret-password.h create mode 100644 library/gsecret-prompt.c create mode 100644 library/tests/mock-service-delete.py create mode 100644 library/tests/mock-service-prompt.py create mode 100644 library/tests/test-password.c create mode 100644 library/tests/test-prompt.c diff --git a/build/glib.supp b/build/glib.supp index 2da79f1..e62121d 100644 --- a/build/glib.supp +++ b/build/glib.supp @@ -317,3 +317,11 @@ fun:g_hash_table_remove fun:g_variant_type_info_unref } +{ + g_rw_lock_reader_lock + Memcheck:Leak + ... + fun:g_rw_lock_impl_new + fun:g_rw_lock_get_impl + fun:g_rw_lock_reader_lock +} diff --git a/build/unknown.supp b/build/unknown.supp index b57beb3..6938016 100644 --- a/build/unknown.supp +++ b/build/unknown.supp @@ -364,3 +364,28 @@ fun:g_main_loop_run fun:gdbus_shared_thread_func } +{ + + Memcheck:Leak + ... + fun:g_source_new + fun:g_idle_source_new + fun:call_destroy_notify + fun:g_dbus_connection_signal_unsubscribe +} +{ + + Memcheck:Leak + ... + fun:g_source_set_callback + fun:call_destroy_notify + fun:g_dbus_connection_signal_unsubscribe +} +{ + + Memcheck:Leak + ... + fun:g_malloc0_n + fun:call_destroy_notify + fun:g_dbus_connection_signal_unsubscribe +} diff --git a/egg/egg-testing.c b/egg/egg-testing.c index 0fed4a1..7fa2a60 100644 --- a/egg/egg-testing.c +++ b/egg/egg-testing.c @@ -114,6 +114,17 @@ egg_test_wait_until (int timeout) return (wait_until_impl) (timeout); } +void +egg_test_wait_idle (void) +{ + GMainContext *context; + + g_assert (wait_until_impl != NULL); + + context = g_main_context_get_thread_default (); + while (g_main_context_iteration (context, FALSE)); +} + static GMainLoop *wait_loop = NULL; static void diff --git a/egg/egg-testing.h b/egg/egg-testing.h index 7ac3149..3ed8081 100644 --- a/egg/egg-testing.h +++ b/egg/egg-testing.h @@ -59,6 +59,8 @@ void egg_test_wait_stop (void); gboolean egg_test_wait_until (int timeout); +void egg_test_wait_idle (void); + gint egg_tests_run_with_loop (void); #endif /* EGG_DH_H_ */ diff --git a/library/Makefile.am b/library/Makefile.am index 44c872f..09ca8e9 100644 --- a/library/Makefile.am +++ b/library/Makefile.am @@ -17,6 +17,8 @@ BUILT_SOURCES = \ libgsecret_la_SOURCES = \ gsecret-value.h gsecret-value.c \ gsecret-item.h gsecret-item.c \ + gsecret-password.h gsecret-password.c \ + gsecret-prompt.h gsecret-prompt.c \ gsecret-service.h gsecret-service.c \ gsecret-util.c \ $(BUILT_SOURCES) \ diff --git a/library/gsecret-collection.h b/library/gsecret-collection.h index 61f03a5..341618b 100644 --- a/library/gsecret-collection.h +++ b/library/gsecret-collection.h @@ -24,7 +24,6 @@ G_BEGIN_DECLS #define GSECRET_IS_SERVICE_CLASS(class) (GSECRET_TYPE_CHECK_CLASS_TYPE ((class), GSECRET_TYPE_SERVICE)) #define GSECRET_SERVICE_GET_CLASS(inst) (GSECRET_TYPE_INSTANCE_GET_CLASS ((inst), GSECRET_TYPE_SERVICE, GSecretServiceClass)) -typedef struct _GSecretService GSecretService; typedef struct _GSecretServiceClass GSecretServiceClass; typedef struct _GSecretServicePrivate GSecretServicePrivate; diff --git a/library/gsecret-item.c b/library/gsecret-item.c index 0318bff..1408b53 100644 --- a/library/gsecret-item.c +++ b/library/gsecret-item.c @@ -38,96 +38,47 @@ gsecret_item_class_init (GSecretItemClass *klass) } -static void -on_item_delete_ready (GObject *source, GAsyncResult *result, gpointer user_data) -{ - GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); - GError *error = NULL; - GVariant *ret; - - ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), - result, &error); - if (ret == NULL) - g_simple_async_result_take_error (res, error); - else - g_variant_unref (ret); - - g_simple_async_result_complete (res); - g_object_unref (res); -} - void -gsecret_item_delete (GSecretItem *self, GCancellable *cancellable, - GAsyncReadyCallback callback, gpointer user_data) +gsecret_item_delete (GSecretItem *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { const gchar *object_path; - gchar *collection_path; - GSimpleAsyncResult *res; g_return_if_fail (GSECRET_IS_ITEM (self)); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); - res = g_simple_async_result_new (G_OBJECT (self), callback, - user_data, gsecret_item_delete); object_path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (self)); - collection_path = _gsecret_util_parent_path (object_path); - - g_dbus_connection_call (g_dbus_proxy_get_connection (G_DBUS_PROXY (self)), - g_dbus_proxy_get_name (G_DBUS_PROXY (self)), - collection_path, GSECRET_COLLECTION_INTERFACE, - "Delete", NULL, NULL, - G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, - cancellable, on_item_delete_ready, res); - - g_free (collection_path); + gsecret_service_delete_path (self->pv->service, object_path, + cancellable, callback, user_data); } gboolean -gsecret_item_delete_finish (GSecretItem *self, GAsyncResult *result, +gsecret_item_delete_finish (GSecretItem *self, + GAsyncResult *result, GError **error) { g_return_val_if_fail (GSECRET_IS_ITEM (self), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - g_return_val_if_fail (g_simple_async_result_is_valid (result, - G_OBJECT (self), gsecret_item_delete), FALSE); - if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), - error)) - return FALSE; - - return TRUE; + return gsecret_service_delete_path_finish (self->pv->service, result, error); } gboolean -gsecret_item_delete_sync (GSecretItem *self, GCancellable *cancellable, +gsecret_item_delete_sync (GSecretItem *self, + GCancellable *cancellable, GError **error) { const gchar *object_path; - gchar *collection_path; - GVariant *ret; g_return_val_if_fail (GSECRET_IS_ITEM (self), FALSE); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); object_path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (self)); - collection_path = _gsecret_util_parent_path (object_path); - - ret = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (G_DBUS_PROXY (self)), - g_dbus_proxy_get_name (G_DBUS_PROXY (self)), - collection_path, GSECRET_COLLECTION_INTERFACE, - "Delete", NULL, NULL, - G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, - cancellable, error); - - g_free (collection_path); - - if (ret != NULL) { - g_variant_unref (ret); - return TRUE; - } - - return FALSE; + return gsecret_service_delete_path_sync (self->pv->service, + object_path, cancellable, error); } static void diff --git a/library/gsecret-item.h b/library/gsecret-item.h index 8ec1e00..b29c29e 100644 --- a/library/gsecret-item.h +++ b/library/gsecret-item.h @@ -16,6 +16,7 @@ #include #include "gsecret-item.h" +#include "gsecret-service.h" #include "gsecret-value.h" G_BEGIN_DECLS @@ -27,7 +28,6 @@ G_BEGIN_DECLS #define GSECRET_IS_ITEM_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), GSECRET_TYPE_ITEM)) #define GSECRET_ITEM_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), GSECRET_TYPE_ITEM, GSecretItemClass)) -typedef struct _GSecretItem GSecretItem; typedef struct _GSecretItemClass GSecretItemClass; typedef struct _GSecretItemPrivate GSecretItemPrivate; diff --git a/library/gsecret-password.c b/library/gsecret-password.c new file mode 100644 index 0000000..d504461 --- /dev/null +++ b/library/gsecret-password.c @@ -0,0 +1,440 @@ +/* GSecret - GLib wrapper for Secret Service + * + * Copyright 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 of the licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + */ + +#include "config.h" + +#include "gsecret-password.h" +#include "gsecret-private.h" +#include "gsecret-value.h" + +#include + +typedef struct { + GAsyncResult *result; + GMainContext *context; + GMainLoop *loop; +} SyncClosure; + +static SyncClosure * +sync_closure_new (void) +{ + SyncClosure *closure; + + closure = g_new0 (SyncClosure, 1); + + closure->context = g_main_context_new (); + closure->loop = g_main_loop_new (closure->context, FALSE); + + return closure; +} + +static void +sync_closure_free (gpointer data) +{ + SyncClosure *closure = data; + + g_clear_object (&closure->result); + g_main_loop_unref (closure->loop); + g_main_context_unref (closure->context); +} + +static void +on_sync_result (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + SyncClosure *closure = user_data; + closure->result = g_object_ref (result); + g_main_loop_quit (closure->loop); +} + +#if 0 + +typedef struct { + GVariant *properties; + gchar *collection_path; + GSecretValue *secret; + GCancellable *cancellable; +} StoreClosure; + +static void +store_closure_free (gpointer data) +{ + StoreClosure *closure = data; + g_variant_unref (closure->properties); + g_free (closure->collection_path); + gsecret_value_unref (closure->secret); + g_clear_object (closure->cancellable); + g_free (closure); +} + +static void +on_create_item_reply (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + GError *error = NULL; + + retval = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error); + if (error == NULL) { + g_variant_get (retval, "(&o&o)", &item_path, &prompt_path); + if (prompt_path xxx) + gsecret_prompt_perform (self, "", closure->cancellable, + on_store_prompt_complete, NULL); + + if (g_strcmp0 (item_path, "/") != 0) + xxx complete! + } + + g_object_unref (res); +} + +static void +on_store_service_connected (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + StoreClosure *closure = g_simple_async_result_get_op_res_gpointer (res); + GSecretService *service; + GError *error = NULL; + GDBusProxy *proxy; + GVariant *params; + + service = _gsecret_service_bare_connect_finish (result, &error); + if (error == NULL) { + params = g_variant_new ("(&a{sv}&(oayays)b)", + closure->properties, + _gsecret_service_encode_secret (service, closure->secret), + TRUE); + + proxy = G_DBUS_PROXY (service); + g_dbus_connection_call (g_dbus_proxy_get_connection (proxy), + g_dbus_proxy_get_name (proxy), + closure->collection_path, + GSECRET_COLLECTION_INTERFACE, + "CreateItem", params, G_VARIANT_TYPE ("(oo)"), + G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, + closure->cancellable, on_create_item_reply, + g_object_ref (res)); + } else { + g_simple_async_result_take_error (res, error); + g_simple_async_result_complete (res); + + + + } + + g_object_unref (res); +} + +void +gsecret_password_store (const GSecretSchema *schema, + const gchar *collection_path, + const gchar *label, + const gchar *password, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data, + ...) +{ + GSimpleAsyncResult *res; + GVariant *attributes; + StoreClosure *closure; + GVariantBuilder builder; + va_list va; + + g_return_if_fail (schema != NULL); + g_return_if_fail (label != NULL); + g_return_if_fail (password != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + + /* Build up the attributes */ + va_start (va, user_data); + attributes = build_attributes (schema, va); + va_end (va); + g_return_if_fail (attributes != NULL); + + /* Build up the various properties */ + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (&builder, "{sv}", GSECRET_SERVICE_INTERFACE "Attributes", attributes); + g_variant_builder_add (&builder, "{sv}", GSECRET_SERVICE_INTERFACE "Label", g_variant_new_string ("label")); + g_variant_builder_add (&builder, "{sv}", GSECRET_SERVICE_INTERFACE "Schema", g_variant_new_string (schema->schema_name)); + + res = g_simple_async_result_new (NULL, callback, user_data, + gsecret_password_store_finish); + closure = g_new0 (StoreClosure, 1); + closure->properties = g_variant_ref_sink (g_variant_builder_end (&builder)); + closure->collection_path = g_strdup (collection_path); + closure->secret = gsecret_value_new (password, -1, "text/plain"); + closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL; + g_simple_async_result_set_op_res_gpointer (res, closure, store_closure_free); + + _gsecret_service_bare_connect_with_session (cancellable, on_store_service_connected, + g_object_ref (res)); + + g_object_unref (res); +} + +#if 0 +gboolean +gsecret_password_store_finish (GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + +} + +gboolean +gsecret_password_store_sync (const GSecretPasswordSchema *schema, + const gchar *collection, + const gchar *label, + const gchar *password, + GCancellable *cancellable, + GError **error, + const gchar *attribute_name, + ...) +{ + g_return_val_if_fail (schema != NULL, FALSE); + g_return_val_if_fail (display_name != NULL, FALSE); + g_return_val_if_fail (password != NULL, FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + +} + +void +gsecret_password_lookup (const GSecretPasswordSchema *schema, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data, + const gchar *attribute_name, + ...) +{ + g_return_if_fail (schema != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + +} + +gchar * +gsecret_password_lookup_finish (GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + +} + +gchar * +gsecret_password_lookup_sync (const GSecretPasswordSchema *schema, + GCancellable *cancellable, + GError **error, + const gchar *attribute_name, + ...) +{ + g_return_val_if_fail (schema != NULL, NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + +} + +#endif +#endif + +typedef struct { + GCancellable *cancellable; + GHashTable *attributes; + gboolean deleted; +} DeleteClosure; + +static void +delete_closure_free (gpointer data) +{ + DeleteClosure *closure = data; + g_clear_object (&closure->cancellable); + g_hash_table_unref (closure->attributes); + g_slice_free (DeleteClosure, closure); +} + +void +gsecret_password_delete (const GSecretSchema *schema, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data, + ...) +{ + GHashTable *attributes; + va_list va; + + g_return_if_fail (schema != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + + va_start (va, user_data); + attributes = _gsecret_util_attributes_for_varargs (schema, va); + va_end (va); + + gsecret_password_deletev (attributes, cancellable, + callback, user_data); + + g_hash_table_unref (attributes); +} + +static void +on_delete_complete (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + DeleteClosure *closure = g_simple_async_result_get_op_res_gpointer (res); + GError *error = NULL; + + closure->deleted = gsecret_service_delete_password_finish (GSECRET_SERVICE (source), + result, &error); + if (error != NULL) + g_simple_async_result_take_error (res, error); + g_simple_async_result_complete (res); + + g_object_unref (res); +} + +static void +on_delete_connect (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + DeleteClosure *closure = g_simple_async_result_get_op_res_gpointer (res); + GSecretService *service; + GError *error = NULL; + + service = _gsecret_service_bare_connect_finish (result, &error); + if (error == NULL) { + gsecret_service_delete_passwordv (service, closure->attributes, + closure->cancellable, on_delete_complete, + g_object_ref (res)); + g_object_unref (service); + + } else { + g_simple_async_result_take_error (res, error); + g_simple_async_result_complete (res); + } + + g_object_unref (res); +} + +void +gsecret_password_deletev (GHashTable *attributes, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *res; + DeleteClosure *closure; + + g_return_if_fail (attributes != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + + res = g_simple_async_result_new (NULL, callback, user_data, + gsecret_password_deletev); + closure = g_slice_new0 (DeleteClosure); + closure->attributes = g_hash_table_ref (attributes); + closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL; + g_simple_async_result_set_op_res_gpointer (res, closure, delete_closure_free); + + _gsecret_service_bare_connect (NULL, FALSE, cancellable, + on_delete_connect, + g_object_ref (res)); + + g_object_unref (res); +} + +gboolean +gsecret_password_delete_finish (GAsyncResult *result, + GError **error) +{ + DeleteClosure *closure; + GSimpleAsyncResult *res; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL, + gsecret_password_deletev), FALSE); + + res = G_SIMPLE_ASYNC_RESULT (result); + if (g_simple_async_result_propagate_error (res, error)) + return FALSE; + + closure = g_simple_async_result_get_op_res_gpointer (res); + return closure->deleted; +} + +gboolean +gsecret_password_delete_sync (const GSecretSchema* schema, + GCancellable *cancellable, + GError **error, + ...) +{ + GHashTable *attributes; + gboolean result; + va_list va; + + g_return_val_if_fail (schema != NULL, FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + va_start (va, error); + attributes = _gsecret_util_attributes_for_varargs (schema, va); + va_end (va); + + result = gsecret_password_deletev_sync (attributes, cancellable, error); + + g_hash_table_unref (attributes); + + return result; +} + +gboolean +gsecret_password_deletev_sync (GHashTable *attributes, + GCancellable *cancellable, + GError **error) +{ + SyncClosure *closure; + gboolean result; + + g_return_val_if_fail (attributes != NULL, FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + closure = sync_closure_new (); + g_main_context_push_thread_default (closure->context); + + gsecret_password_deletev (attributes, cancellable, + on_sync_result, closure); + + g_main_loop_run (closure->loop); + + result = gsecret_password_delete_finish (closure->result, error); + + g_main_context_pop_thread_default (closure->context); + sync_closure_free (closure); + + return result; +} + +void +gsecret_password_free (gpointer password) +{ + if (password == NULL) + return; + + egg_secure_strfree (password); +} + diff --git a/library/gsecret-password.h b/library/gsecret-password.h new file mode 100644 index 0000000..9ab2250 --- /dev/null +++ b/library/gsecret-password.h @@ -0,0 +1,87 @@ +/* GSecret - GLib wrapper for Secret Service + * + * Copyright 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 of the licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + */ + +#ifndef __GSECRET_PASSWORD_H__ +#define __GSECRET_PASSWORD_H__ + +#include + +G_BEGIN_DECLS + +#include "gsecret-types.h" + +#if 0 + +void gsecret_password_store (const GSecretSchema *schema, + const gchar *collection_path, + const gchar *label, + const gchar *password, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data, + ...) G_GNUC_NULL_TERMINATED; + +gboolean gsecret_password_store_finish (GAsyncResult *result, + GError **error); + +void gsecret_password_store_sync (const GSecretSchema *schema, + const gchar *collection, + const gchar *display_name, + const gchar *password, + GCancellable *cancellable, + GError **error, + ...) G_GNUC_NULL_TERMINATED; + +void gsecret_password_lookup (const GSecretSchema *schema, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data, + ...) G_GNUC_NULL_TERMINATED; + +gchar * gsecret_password_lookup_finish (GAsyncResult *result, + GError **error); + +gchar * gsecret_password_lookup_sync (const GSecretSchema *schema, + GCancellable *cancellable, + GError **error, + ...) G_GNUC_NULL_TERMINATED; + +#endif + +void gsecret_password_delete (const GSecretSchema *schema, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data, + ...) G_GNUC_NULL_TERMINATED; + +void gsecret_password_deletev (GHashTable *attributes, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean gsecret_password_delete_finish (GAsyncResult *result, + GError **error); + +gboolean gsecret_password_delete_sync (const GSecretSchema* schema, + GCancellable *cancellable, + GError **error, + ...) G_GNUC_NULL_TERMINATED; + +gboolean gsecret_password_deletev_sync (GHashTable *attributes, + GCancellable *cancellable, + GError **error); + +void gsecret_password_free (gpointer password); + +G_END_DECLS + +#endif /* __G_SERVICE_H___ */ diff --git a/library/gsecret-private.h b/library/gsecret-private.h index 17c97c9..15b0b6b 100644 --- a/library/gsecret-private.h +++ b/library/gsecret-private.h @@ -30,22 +30,44 @@ typedef struct { #define GSECRET_SERVICE_BUS_NAME "org.freedesktop.Secret.Service" +#define GSECRET_ITEM_INTERFACE "org.freedesktop.Secret.Item" +#define GSECRET_COLLECTION_INTERFACE "org.freedesktop.Secret.Collection" +#define GSECRET_PROMPT_INTERFACE "org.freedesktop.Secret.Prompt" #define GSECRET_SERVICE_INTERFACE "org.freedesktop.Secret.Service" -#define GSECRET_COLLECTION_INTERFACE "org.freedesktop.Secret.Collection" +#define GSECRET_PROMPT_SIGNAL_COMPLETED "Completed" GSecretParams * _gsecret_params_new (GCancellable *cancellable, GVariant *in); void _gsecret_params_free (gpointer data); +GSecretPrompt * _gsecret_prompt_instance (GDBusConnection *connection, + const gchar *object_path); + gchar * _gsecret_util_parent_path (const gchar *path); +gboolean _gsecret_util_empty_path (const gchar *path); + GVariant * _gsecret_util_variant_for_attributes (GHashTable *attributes); +GHashTable * _gsecret_util_attributes_for_varargs (const GSecretSchema *schema, + va_list va); + +void _gsecret_service_set_default_bus_name (const gchar *bus_name); + GSecretService * _gsecret_service_bare_instance (GDBusConnection *connection, const gchar *bus_name); +void _gsecret_service_bare_connect (const gchar *bus_name, + gboolean ensure_session, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GSecretService * _gsecret_service_bare_connect_finish (GAsyncResult *result, + GError **error); + GVariant * _gsecret_service_encode_secret (GSecretService *self, GSecretValue *value); diff --git a/library/gsecret-prompt.c b/library/gsecret-prompt.c new file mode 100644 index 0000000..f1e34c6 --- /dev/null +++ b/library/gsecret-prompt.c @@ -0,0 +1,433 @@ +/* GSecret - GLib wrapper for Secret Prompt + * + * Copyright 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 of the licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + */ + +#include "config.h" + +#include "gsecret-dbus-generated.h" +#include "gsecret-private.h" +#include "gsecret-prompt.h" + +#include +#include + +#include + +struct _GSecretPromptPrivate { + gint prompted; + GVariant *last_result; +}; + +G_DEFINE_TYPE (GSecretPrompt, gsecret_prompt, G_TYPE_DBUS_PROXY); + +static void +gsecret_prompt_init (GSecretPrompt *self) +{ + self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GSECRET_TYPE_PROMPT, + GSecretPromptPrivate); +} + +static void +gsecret_prompt_finalize (GObject *obj) +{ + GSecretPrompt *self = GSECRET_PROMPT (obj); + + if (self->pv->last_result) + g_variant_unref (self->pv->last_result); + + G_OBJECT_CLASS (gsecret_prompt_parent_class)->finalize (obj); +} + +static void +gsecret_prompt_class_init (GSecretPromptClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gsecret_prompt_finalize; + + g_type_class_add_private (klass, sizeof (GSecretPromptPrivate)); +} + +typedef struct { + GMainLoop *loop; + GAsyncResult *result; +} RunClosure; + +static void +on_prompt_run_complete (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + RunClosure *closure = user_data; + closure->result = g_object_ref (result); + g_main_loop_quit (closure->loop); +} + +GSecretPrompt * +gsecret_prompt_instance (GSecretService *service, + const gchar *prompt_path) +{ + GDBusProxy *proxy; + GSecretPrompt *prompt; + GError *error = NULL; + + g_return_val_if_fail (GSECRET_IS_SERVICE (service), NULL); + g_return_val_if_fail (prompt_path != NULL, NULL); + + proxy = G_DBUS_PROXY (service); + prompt = g_initable_new (GSECRET_TYPE_PROMPT, NULL, &error, + "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, + "g-interface-info", _gsecret_gen_prompt_interface_info (), + "g-name", g_dbus_proxy_get_name (proxy), + "g-connection", g_dbus_proxy_get_connection (proxy), + "g-object-path", prompt_path, + "g-interface-name", GSECRET_PROMPT_INTERFACE, + NULL); + + if (error != NULL) { + g_warning ("couldn't create GSecretPrompt object: %s", error->message); + g_clear_error (&error); + return NULL; + } + + return prompt; +} + +gboolean +gsecret_prompt_run (GSecretPrompt *self, + gulong window_id, + GCancellable *cancellable, + GError **error) +{ + GMainContext *context; + RunClosure *closure; + gboolean ret; + + g_return_val_if_fail (GSECRET_IS_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); + + context = g_main_context_get_thread_default (); + + closure = g_new0 (RunClosure, 1); + closure->loop = g_main_loop_new (context, FALSE); + + gsecret_prompt_perform (self, window_id, cancellable, + on_prompt_run_complete, closure); + + g_main_loop_run (closure->loop); + + ret = gsecret_prompt_perform_finish (self, closure->result, error); + + g_main_loop_unref (closure->loop); + g_object_unref (closure->result); + g_free (closure); + + return ret; +} + +gboolean +gsecret_prompt_perform_sync (GSecretPrompt *self, + gulong window_id, + GCancellable *cancellable, + GError **error) +{ + GMainContext *context; + gboolean ret; + + g_return_val_if_fail (GSECRET_IS_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); + + context = g_main_context_new (); + g_main_context_push_thread_default (context); + + ret = gsecret_prompt_run (self, window_id, cancellable, error); + + /* Needed to prevent memory leaks */ + while (g_main_context_iteration (context, FALSE)); + + g_main_context_pop_thread_default (context); + g_main_context_unref (context); + + return ret; +} + +typedef struct { + GDBusConnection *connection; + GCancellable *call_cancellable; + GCancellable *async_cancellable; + gulong cancelled_sig; + gboolean prompting; + gboolean dismissed; + gboolean vanished; + gboolean completed; + guint signal; + guint watch; +} PerformClosure; + +static void +perform_closure_free (gpointer data) +{ + PerformClosure *closure = data; + g_object_unref (closure->call_cancellable); + g_clear_object (&closure->async_cancellable); + g_object_unref (closure->connection); + g_assert (closure->signal == 0); + g_assert (closure->watch == 0); + g_slice_free (PerformClosure, closure); +} + +static void +perform_prompt_complete (GSimpleAsyncResult *res, + gboolean dismissed) +{ + PerformClosure *closure = g_simple_async_result_get_op_res_gpointer (res); + + closure->dismissed = dismissed; + if (closure->completed) + return; + closure->completed = TRUE; + + if (closure->signal) + g_dbus_connection_signal_unsubscribe (closure->connection, closure->signal); + closure->signal = 0; + + if (closure->watch) + g_bus_unwatch_name (closure->watch); + closure->watch = 0; + + if (closure->cancelled_sig) + g_signal_handler_disconnect (closure->async_cancellable, closure->cancelled_sig); + closure->cancelled_sig = 0; + + g_simple_async_result_complete (res); +} + +static void +on_prompt_completed (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + GSecretPrompt *self = GSECRET_PROMPT (g_async_result_get_source_object (user_data)); + PerformClosure *closure = g_simple_async_result_get_op_res_gpointer (res); + gboolean dismissed; + + closure->prompting = FALSE; + + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(bv)"))) { + g_warning ("GSecretPrompt received invalid %s signal of type %s", + signal_name, g_variant_get_type_string (parameters)); + perform_prompt_complete (res, TRUE); + + } else { + g_return_if_fail (self->pv->last_result == NULL); + g_variant_get (parameters, "(bv)", &dismissed, &self->pv->last_result); + perform_prompt_complete (res, dismissed); + } + + g_object_unref (self); +} + +static void +on_prompt_prompted (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + PerformClosure *closure = g_simple_async_result_get_op_res_gpointer (res); + GSecretPrompt *self = GSECRET_PROMPT (source); + GError *error = NULL; + GVariant *retval; + + retval = g_dbus_proxy_call_finish (G_DBUS_PROXY (self), result, &error); + + if (retval) + g_variant_unref (retval); + if (closure->vanished) + g_clear_error (&error); + + if (error != NULL) { + g_simple_async_result_take_error (res, error); + perform_prompt_complete (res, TRUE); + + } else { + g_atomic_int_inc (&self->pv->prompted); + + /* And now we wait for the signal */ + closure->prompting = TRUE; + } + + g_object_unref (res); +} + +static void +on_prompt_vanished (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + PerformClosure *closure = g_simple_async_result_get_op_res_gpointer (res); + closure->vanished = TRUE; + g_cancellable_cancel (closure->call_cancellable); + perform_prompt_complete (res, TRUE); +} + +static void +on_prompt_dismissed (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + PerformClosure *closure = g_simple_async_result_get_op_res_gpointer (res); + GSecretPrompt *self = GSECRET_PROMPT (source); + GError *error = NULL; + GVariant *retval; + + retval = g_dbus_proxy_call_finish (G_DBUS_PROXY (self), result, &error); + + if (retval) + g_variant_unref (retval); + if (closure->vanished) + g_clear_error (&error); + + if (error != NULL) { + g_simple_async_result_take_error (res, error); + perform_prompt_complete (res, TRUE); + } + + g_object_unref (res); +} + +static void +on_prompt_cancelled (GCancellable *cancellable, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + PerformClosure *closure = g_simple_async_result_get_op_res_gpointer (res); + GSecretPrompt *self = GSECRET_PROMPT (g_async_result_get_source_object (user_data)); + + /* Instead of cancelling our dbus calls, we cancel the prompt itself via this dbus call */ + + g_dbus_proxy_call (G_DBUS_PROXY (self), "Dismiss", g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, + closure->call_cancellable, + on_prompt_dismissed, g_object_ref (res)); + + g_object_unref (self); +} + +void +gsecret_prompt_perform (GSecretPrompt *self, + gulong window_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *res; + PerformClosure *closure; + const gchar *owner_name; + const gchar *object_path; + GDBusProxy *proxy; + gchar *window; + + g_return_if_fail (GSECRET_IS_PROMPT (self)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + + if (g_atomic_int_get (&self->pv->prompted)) { + g_warning ("The prompt object has already had its prompt called."); + return; + } + + proxy = G_DBUS_PROXY (self); + + res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, + gsecret_prompt_perform); + closure = g_slice_new0 (PerformClosure); + closure->connection = g_object_ref (g_dbus_proxy_get_connection (proxy)); + closure->call_cancellable = g_cancellable_new (); + closure->async_cancellable = cancellable ? g_object_ref (cancellable) : NULL; + g_simple_async_result_set_op_res_gpointer (res, closure, perform_closure_free); + + if (window_id == 0) + window = g_strdup (""); + else + window = g_strdup_printf ("%lu", window_id); + + owner_name = g_dbus_proxy_get_name_owner (proxy); + object_path = g_dbus_proxy_get_object_path (proxy); + + closure->signal = g_dbus_connection_signal_subscribe (closure->connection, owner_name, + GSECRET_PROMPT_INTERFACE, + GSECRET_PROMPT_SIGNAL_COMPLETED, + object_path, NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + on_prompt_completed, + g_object_ref (res), + g_object_unref); + + closure->watch = g_bus_watch_name_on_connection (closure->connection, owner_name, + G_BUS_NAME_WATCHER_FLAGS_NONE, NULL, + on_prompt_vanished, + g_object_ref (res), + g_object_unref); + + if (closure->async_cancellable) { + closure->cancelled_sig = g_cancellable_connect (closure->async_cancellable, + G_CALLBACK (on_prompt_cancelled), + res, NULL); + } + + g_dbus_proxy_call (proxy, "Prompt", g_variant_new ("(s)", window), + G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, + closure->call_cancellable, on_prompt_prompted, g_object_ref (res)); + + g_free (window); + g_object_unref (res); +} + +gboolean +gsecret_prompt_perform_finish (GSecretPrompt *self, + GAsyncResult *result, + GError **error) +{ + PerformClosure *closure; + GSimpleAsyncResult *res; + + g_return_val_if_fail (GSECRET_IS_PROMPT (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), + gsecret_prompt_perform), FALSE); + + res = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (res, error)) + return FALSE; + + closure = g_simple_async_result_get_op_res_gpointer (res); + return !closure->dismissed; +} + +GVariant * +gsecret_prompt_get_result_value (GSecretPrompt *self) +{ + g_return_val_if_fail (GSECRET_IS_PROMPT (self), NULL); + + if (self->pv->last_result) + return g_variant_ref (self->pv->last_result); + + return NULL; +} diff --git a/library/gsecret-prompt.h b/library/gsecret-prompt.h index cb43339..7abfb93 100644 --- a/library/gsecret-prompt.h +++ b/library/gsecret-prompt.h @@ -15,6 +15,8 @@ #include +#include "gsecret-types.h" + G_BEGIN_DECLS #define GSECRET_TYPE_PROMPT (gsecret_prompt_get_type ()) @@ -24,13 +26,13 @@ G_BEGIN_DECLS #define GSECRET_IS_PROMPT_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), GSECRET_TYPE_PROMPT)) #define GSECRET_PROMPT_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), GSECRET_TYPE_PROMPT, GSecretPromptClass)) -typedef struct _GSecretPrompt GSecretPrompt; typedef struct _GSecretPromptClass GSecretPromptClass; typedef struct _GSecretPromptPrivate GSecretPromptPrivate; struct _GSecretPromptClass { GDBusProxyClass parent_class; - padding; + + gpointer padding[8]; }; struct _GSecretPrompt { @@ -38,19 +40,32 @@ struct _GSecretPrompt { GSecretPromptPrivate *pv; }; -GType gsecret_service_get_type (void) G_GNUC_CONST; +GType gsecret_prompt_get_type (void) G_GNUC_CONST; -GSecretService* gsecret_collection_xxx_new (void); +GSecretPrompt * gsecret_prompt_instance (GSecretService *service, + const gchar *prompt_path); -GSecretPrompt* gsecret_prompt_instance (GDBusConnection *connection, - const gchar *object_path, +gboolean gsecret_prompt_run (GSecretPrompt *self, + gulong window_id, + GCancellable *cancellable, GError **error); -GSecretPrompt* gsecret_prompt_instance_sync (GDBusConnection *connection, - const gchar *object_path); +gboolean gsecret_prompt_perform_sync (GSecretPrompt *self, + gulong window_id, + GCancellable *cancellable, + GError **error); - gsecret_prompt_perform - gsecret_prompt_dismiss +void gsecret_prompt_perform (GSecretPrompt *self, + gulong window_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean gsecret_prompt_perform_finish (GSecretPrompt *self, + GAsyncResult *result, + GError **error); + +GVariant * gsecret_prompt_get_result_value (GSecretPrompt *self); G_END_DECLS diff --git a/library/gsecret-service.c b/library/gsecret-service.c index 50e5be3..44c3e70 100644 --- a/library/gsecret-service.c +++ b/library/gsecret-service.c @@ -13,6 +13,7 @@ #include "config.h" #include "gsecret-dbus-generated.h" +#include "gsecret-item.h" #include "gsecret-private.h" #include "gsecret-service.h" #include "gsecret-types.h" @@ -36,6 +37,8 @@ EGG_SECURE_GLIB_DEFINITIONS (); EGG_SECURE_DECLARE (secret_service); +static const gchar *default_bus_name = GSECRET_SERVICE_BUS_NAME; + #define ALGORITHMS_AES "dh-ietf1024-sha256-aes128-cbc-pkcs7" #define ALGORITHMS_PLAIN "plain" @@ -60,6 +63,45 @@ static gpointer service_instance = NULL; G_DEFINE_TYPE (GSecretService, gsecret_service, G_TYPE_DBUS_PROXY); +typedef struct { + GAsyncResult *result; + GMainContext *context; + GMainLoop *loop; +} SyncClosure; + +static SyncClosure * +sync_closure_new (void) +{ + SyncClosure *closure; + + closure = g_new0 (SyncClosure, 1); + + closure->context = g_main_context_new (); + closure->loop = g_main_loop_new (closure->context, FALSE); + + return closure; +} + +static void +sync_closure_free (gpointer data) +{ + SyncClosure *closure = data; + + g_clear_object (&closure->result); + g_main_loop_unref (closure->loop); + g_main_context_unref (closure->context); +} + +static void +on_sync_result (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + SyncClosure *closure = user_data; + closure->result = g_object_ref (result); + g_main_loop_quit (closure->loop); +} + static void gsecret_session_free (gpointer data) { @@ -95,6 +137,65 @@ gsecret_service_finalize (GObject *obj) G_OBJECT_CLASS (gsecret_service_parent_class)->finalize (obj); } +static gboolean +gsecret_service_real_prompt_sync (GSecretService *self, + GSecretPrompt *prompt, + GCancellable *cancellable, + GError **error) +{ + return gsecret_prompt_perform_sync (prompt, 0, cancellable, error); +} + +static void +on_real_prompt_completed (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + GError *error = NULL; + gboolean ret; + + ret = gsecret_prompt_perform_finish (GSECRET_PROMPT (source), result, &error); + g_simple_async_result_set_op_res_gboolean (res, ret); + if (error != NULL) + g_simple_async_result_take_error (res, error); + g_simple_async_result_complete (res); + + g_object_unref (res); +} + +static void +gsecret_service_real_prompt_async (GSecretService *self, + GSecretPrompt *prompt, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *res; + + res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, + gsecret_service_real_prompt_async); + + gsecret_prompt_perform (prompt, 0, cancellable, + on_real_prompt_completed, + g_object_ref (res)); + + g_object_unref (res); +} + +static gboolean +gsecret_service_real_prompt_finish (GSecretService *self, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *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_gboolean (res); +} + static void gsecret_service_class_init (GSecretServiceClass *klass) { @@ -102,9 +203,20 @@ gsecret_service_class_init (GSecretServiceClass *klass) object_class->finalize = gsecret_service_finalize; + klass->prompt_sync = gsecret_service_real_prompt_sync; + klass->prompt_async = gsecret_service_real_prompt_async; + klass->prompt_finish = gsecret_service_real_prompt_finish; + g_type_class_add_private (klass, sizeof (GSecretServicePrivate)); } +void +_gsecret_service_set_default_bus_name (const gchar *bus_name) +{ + g_return_if_fail (bus_name != NULL); + default_bus_name = bus_name; +} + static void on_service_instance_gone (gpointer user_data, GObject *where_the_object_was) @@ -138,7 +250,7 @@ _gsecret_service_bare_instance (GDBusConnection *connection, /* Alternate bus name is only used for testing */ if (bus_name == NULL) - bus_name = GSECRET_SERVICE_BUS_NAME; + bus_name = default_bus_name; service = g_initable_new (GSECRET_TYPE_SERVICE, NULL, &error, "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, @@ -172,6 +284,115 @@ _gsecret_service_bare_instance (GDBusConnection *connection, return service; } +typedef struct { + GCancellable *cancellable; + GSecretService *service; + gboolean ensure_session; + gchar *bus_name; +} ConnectClosure; + +static void +connect_closure_free (gpointer data) +{ + ConnectClosure *closure = data; + g_clear_object (&closure->cancellable); + g_clear_object (&closure->service); + g_slice_free (ConnectClosure, closure); +} + +static void +on_connect_ensure (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + GError *error = NULL; + + gsecret_service_ensure_session_finish (GSECRET_SERVICE (source), result, &error); + if (error != NULL) + g_simple_async_result_take_error (res, error); + + g_simple_async_result_complete (res); + g_object_unref (res); +} + +static void +on_connect_bus (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + ConnectClosure *closure = g_simple_async_result_get_op_res_gpointer (res); + GDBusConnection *connection; + GError *error = NULL; + + connection = g_bus_get_finish (result, &error); + if (error == NULL) { + closure->service = _gsecret_service_bare_instance (connection, closure->bus_name); + if (closure->ensure_session) + gsecret_service_ensure_session (closure->service, closure->cancellable, + on_connect_ensure, g_object_ref (res)); + + else + g_simple_async_result_complete (res); + + g_object_unref (connection); + + } else { + g_simple_async_result_take_error (res, error); + g_simple_async_result_complete (res); + } + + g_object_unref (res); +} + +void +_gsecret_service_bare_connect (const gchar *bus_name, + gboolean ensure_session, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *res; + ConnectClosure *closure; + + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + + if (bus_name == NULL) + bus_name = default_bus_name; + + res = g_simple_async_result_new (NULL, callback, user_data, + _gsecret_service_bare_connect); + closure = g_slice_new0 (ConnectClosure); + closure->bus_name = g_strdup (bus_name); + closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL; + closure->ensure_session = ensure_session; + g_simple_async_result_set_op_res_gpointer (res, closure, connect_closure_free); + + g_bus_get (G_BUS_TYPE_SESSION, cancellable, on_connect_bus, g_object_ref (res)); + + g_object_unref (res); +} + +GSecretService * +_gsecret_service_bare_connect_finish (GAsyncResult *result, + GError **error) +{ + ConnectClosure *closure; + GSimpleAsyncResult *res; + + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL, + _gsecret_service_bare_connect), NULL); + + res = G_SIMPLE_ASYNC_RESULT (result); + if (g_simple_async_result_propagate_error (res, error)) + return NULL; + + closure = g_simple_async_result_get_op_res_gpointer (res); + return g_object_ref (closure->service); +} + const gchar * gsecret_service_get_session_algorithms (GSecretService *self) { @@ -1271,3 +1492,490 @@ gsecret_service_get_secrets_for_paths_sync (GSecretService *self, return values; } + +gboolean +gsecret_service_prompt_sync (GSecretService *self, + GSecretPrompt *prompt, + GCancellable *cancellable, + GError **error) +{ + GSecretServiceClass *klass; + + g_return_val_if_fail (GSECRET_IS_SERVICE (self), FALSE); + g_return_val_if_fail (GSECRET_IS_PROMPT (prompt), FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + klass = GSECRET_SERVICE_GET_CLASS (self); + g_return_val_if_fail (klass->prompt_sync != NULL, FALSE); + + return (klass->prompt_sync) (self, prompt, cancellable, error); +} + +void +gsecret_service_prompt_path (GSecretService *self, + const gchar *prompt_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSecretPrompt *prompt; + + g_return_if_fail (GSECRET_IS_SERVICE (self)); + g_return_if_fail (prompt_path != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + + prompt = gsecret_prompt_instance (self, prompt_path); + + gsecret_service_prompt (self, prompt, cancellable, callback, user_data); + + g_object_unref (prompt); +} + +void +gsecret_service_prompt (GSecretService *self, + GSecretPrompt *prompt, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSecretServiceClass *klass; + + g_return_if_fail (GSECRET_IS_SERVICE (self)); + g_return_if_fail (GSECRET_IS_PROMPT (prompt)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + + klass = GSECRET_SERVICE_GET_CLASS (self); + g_return_if_fail (klass->prompt_async != NULL); + + (klass->prompt_async) (self, prompt, cancellable, callback, user_data); +} + +gboolean +gsecret_service_prompt_finish (GSecretService *self, + GAsyncResult *result, + GError **error) +{ + GSecretServiceClass *klass; + + g_return_val_if_fail (GSECRET_IS_SERVICE (self), FALSE); + g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + klass = GSECRET_SERVICE_GET_CLASS (self); + g_return_val_if_fail (klass->prompt_finish != NULL, FALSE); + + return (klass->prompt_finish) (self, result, error); +} + +#if 0 + +void +gsecret_service_store_password (GSecretService *self, + const GSecretSchema *schema, + const gchar *collection_path, + const gchar *label, + const gchar *password, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data, + ...) +{ + +} + +gboolean +gsecret_service_store_password_finish (GSecretService *self, + GAsyncResult *result, + GError **error) +{ + +} + +void +gsecret_service_store_password_sync (GSecretService *self, + const GSecretSchema *schema, + const gchar *collection, + const gchar *display_name, + const gchar *password, + GCancellable *cancellable, + GError **error, + ...) +{ + +} + +void +gsecret_service_lookup_password (GSecretService *self, + const GSecretSchema *schema, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data, + ...) +{ + +} + +gchar * +gsecret_service_lookup_password_finish (GSecretService *self, + GAsyncResult *result, + GError **error) +{ + +} + +gchar * +gsecret_service_lookup_password_sync (GSecretService *self, + const GSecretSchema *schema, + GCancellable *cancellable, + GError **error, + ...) +{ + +} + +#endif + +typedef struct { + GCancellable *cancellable; + gboolean deleted; +} DeleteClosure; + +static void +delete_closure_free (gpointer data) +{ + DeleteClosure *closure = data; + g_clear_object (&closure->cancellable); + g_slice_free (DeleteClosure, closure); +} + +static void +on_delete_prompted (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + DeleteClosure *closure = g_simple_async_result_get_op_res_gpointer (res); + GError *error = NULL; + + gsecret_service_prompt_finish (GSECRET_SERVICE (source), result, &error); + + if (error == NULL) + closure->deleted = TRUE; + else + g_simple_async_result_take_error (res, error); + + g_simple_async_result_complete (res); + g_object_unref (res); +} + +static void +on_delete_complete (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + DeleteClosure *closure = g_simple_async_result_get_op_res_gpointer (res); + GSecretService *self = GSECRET_SERVICE (g_async_result_get_source_object (user_data)); + const gchar *prompt_path; + GError *error = NULL; + GVariant *retval; + + retval = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error); + if (error == NULL) { + g_variant_get (retval, "(&o)", &prompt_path); + + if (_gsecret_util_empty_path (prompt_path)) { + closure->deleted = TRUE; + g_simple_async_result_complete (res); + + } else { + gsecret_service_prompt_path (self, prompt_path, + closure->cancellable, + on_delete_prompted, + g_object_ref (res)); + } + + g_variant_unref (retval); + + } else { + g_simple_async_result_take_error (res, error); + g_simple_async_result_complete (res); + } + + g_object_unref (self); + g_object_unref (res); +} + +void +gsecret_service_delete_path (GSecretService *self, + const gchar *item_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *res; + DeleteClosure *closure; + + g_return_if_fail (GSECRET_IS_SERVICE (self)); + g_return_if_fail (item_path != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + + res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, + gsecret_service_delete_path); + closure = g_slice_new0 (DeleteClosure); + closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL; + g_simple_async_result_set_op_res_gpointer (res, closure, delete_closure_free); + + g_dbus_connection_call (g_dbus_proxy_get_connection (G_DBUS_PROXY (self)), + g_dbus_proxy_get_name (G_DBUS_PROXY (self)), + item_path, GSECRET_ITEM_INTERFACE, + "Delete", g_variant_new ("()"), G_VARIANT_TYPE ("(o)"), + G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, + cancellable, on_delete_complete, g_object_ref (res)); + + g_object_unref (res); +} + +gboolean +gsecret_service_delete_path_finish (GSecretService *self, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *res; + DeleteClosure *closure; + + g_return_val_if_fail (GSECRET_IS_SERVICE (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), + gsecret_service_delete_path), FALSE); + + res = G_SIMPLE_ASYNC_RESULT (result); + if (g_simple_async_result_propagate_error (res, error)) + return FALSE; + + closure = g_simple_async_result_get_op_res_gpointer (res); + return closure->deleted; +} + +gboolean +gsecret_service_delete_path_sync (GSecretService *self, + const gchar *item_path, + GCancellable *cancellable, + GError **error) +{ + SyncClosure *closure; + gboolean result; + + g_return_val_if_fail (GSECRET_IS_SERVICE (self), FALSE); + g_return_val_if_fail (item_path != NULL, FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + closure = sync_closure_new (); + g_main_context_push_thread_default (closure->context); + + gsecret_service_delete_path (self, item_path, cancellable, on_sync_result, closure); + + g_main_loop_run (closure->loop); + + result = gsecret_service_delete_path_finish (self, closure->result, error); + + g_main_context_pop_thread_default (closure->context); + sync_closure_free (closure); + + return result; +} + +static void +on_delete_password_complete (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + GSecretService *self = GSECRET_SERVICE (g_async_result_get_source_object (user_data)); + DeleteClosure *closure = g_simple_async_result_get_op_res_gpointer (res); + GError *error = NULL; + + closure->deleted = gsecret_service_delete_path_finish (self, result, &error); + 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); +} + +static void +on_search_delete_password (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + DeleteClosure *closure = g_simple_async_result_get_op_res_gpointer (res); + GSecretService *self = GSECRET_SERVICE (g_async_result_get_source_object (user_data)); + const gchar *path = NULL; + GError *error = NULL; + gchar **locked; + gchar **unlocked; + + gsecret_service_search_for_paths_finish (self, result, &unlocked, &locked, &error); + if (error != NULL) { + g_simple_async_result_take_error (res, error); + g_simple_async_result_complete (res); + + } else { + /* Choose the first path */ + if (unlocked && unlocked[0]) + path = unlocked[0]; + else if (locked && locked[0]) + path = locked[0]; + + /* Nothing to delete? */ + if (path == NULL) { + closure->deleted = FALSE; + g_simple_async_result_complete (res); + + /* Delete the first path */ + } else { + closure->deleted = TRUE; + gsecret_service_delete_path (self, path, + closure->cancellable, + on_delete_password_complete, + g_object_ref (res)); + } + } + + g_strfreev (locked); + g_strfreev (unlocked); + g_object_unref (self); + g_object_unref (res); +} + +void +gsecret_service_delete_password (GSecretService *self, + const GSecretSchema *schema, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data, + ...) +{ + GHashTable *attributes; + va_list va; + + g_return_if_fail (GSECRET_SERVICE (self)); + g_return_if_fail (schema != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + + va_start (va, user_data); + attributes = _gsecret_util_attributes_for_varargs (schema, va); + va_end (va); + + gsecret_service_delete_passwordv (self, attributes, cancellable, + callback, user_data); + + g_hash_table_unref (attributes); +} + +void +gsecret_service_delete_passwordv (GSecretService *self, + GHashTable *attributes, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *res; + DeleteClosure *closure; + + g_return_if_fail (GSECRET_SERVICE (self)); + g_return_if_fail (attributes != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + + res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, + gsecret_service_delete_password); + closure = g_slice_new0 (DeleteClosure); + closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL; + g_simple_async_result_set_op_res_gpointer (res, closure, delete_closure_free); + + gsecret_service_search_for_paths (self, attributes, cancellable, + on_search_delete_password, g_object_ref (res)); + + g_object_unref (res); +} + +gboolean +gsecret_service_delete_password_finish (GSecretService *self, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *res; + DeleteClosure *closure; + + g_return_val_if_fail (GSECRET_IS_SERVICE (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), + gsecret_service_delete_password), FALSE); + + res = G_SIMPLE_ASYNC_RESULT (result); + if (g_simple_async_result_propagate_error (res, error)) + return FALSE; + + closure = g_simple_async_result_get_op_res_gpointer (res); + return closure->deleted; +} + +gboolean +gsecret_service_delete_password_sync (GSecretService *self, + const GSecretSchema* schema, + GCancellable *cancellable, + GError **error, + ...) +{ + GHashTable *attributes; + gboolean result; + va_list va; + + g_return_val_if_fail (GSECRET_IS_SERVICE (self), FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + va_start (va, error); + attributes = _gsecret_util_attributes_for_varargs (schema, va); + va_end (va); + + result = gsecret_service_delete_passwordv_sync (self, attributes, cancellable, error); + + g_hash_table_unref (attributes); + + return result; +} + +gboolean +gsecret_service_delete_passwordv_sync (GSecretService *self, + GHashTable *attributes, + GCancellable *cancellable, + GError **error) +{ + SyncClosure *closure; + gboolean result; + + g_return_val_if_fail (GSECRET_IS_SERVICE (self), FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + closure = sync_closure_new (); + g_main_context_push_thread_default (closure->context); + + gsecret_service_delete_passwordv (self, attributes, cancellable, + on_sync_result, closure); + + g_main_loop_run (closure->loop); + + result = gsecret_service_delete_password_finish (self, closure->result, error); + + g_main_context_pop_thread_default (closure->context); + sync_closure_free (closure); + + return result; +} diff --git a/library/gsecret-service.h b/library/gsecret-service.h index af13f19..a65a621 100644 --- a/library/gsecret-service.h +++ b/library/gsecret-service.h @@ -15,6 +15,8 @@ #include +#include "gsecret-prompt.h" +#include "gsecret-types.h" #include "gsecret-value.h" G_BEGIN_DECLS @@ -26,19 +28,26 @@ G_BEGIN_DECLS #define GSECRET_IS_SERVICE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), GSECRET_TYPE_SERVICE)) #define GSECRET_SERVICE_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), GSECRET_TYPE_SERVICE, GSecretServiceClass)) -typedef struct _GSecretService GSecretService; typedef struct _GSecretServiceClass GSecretServiceClass; typedef struct _GSecretServicePrivate GSecretServicePrivate; struct _GSecretServiceClass { GDBusProxyClass parent_class; - GType collection_type; - GType item_type; + gboolean (*prompt_sync) (GSecretService *self, + GSecretPrompt *prompt, + GCancellable *cancellable, + GError **error); -#if 0 - padding; -#endif + void (*prompt_async) (GSecretService *self, + GSecretPrompt *prompt, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + + gboolean (*prompt_finish) (GSecretService *self, + GAsyncResult *result, + GError **error); }; struct _GSecretService { @@ -186,34 +195,132 @@ void gsecret_service_unlock (GSecretService *se GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); +#endif -gboolean gsecret_service_ - GList **unlocked, +gint gsecret_service_unlock_paths_sync (GSecretService *self, + const gchar **paths, + GCancellable *cancellable, + gchar ***unlocked, + GError **error); + +void gsecret_service_unlock_paths (GSecretService *self, + const gchar **paths, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gint gsecret_service_unlock_paths_finish (GSecretService *self, + GAsyncResult *result, + gchar ***unlocked, + GError **error); + +gboolean gsecret_service_prompt_sync (GSecretService *self, GSecretPrompt *prompt, + GCancellable *cancellable, GError **error); -gboolean gsecret_service_unlock (GSecretService *self, - GList *objects, - GList **unlocked, +void gsecret_service_prompt (GSecretService *self, GSecretPrompt *prompt, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +void gsecret_service_prompt_path (GSecretService *self, + const gchar *prompt_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean gsecret_service_prompt_finish (GSecretService *self, + GAsyncResult *result, GError **error); -gboolean gsecret_service_unlock_for_paths (GSecretService *self, - GList *objects, - GList **unlocked, - GSecretPrompt *prompt, +#if 0 +void gsecret_service_store_password (GSecretService *self, + const GSecretSchema *schema, + const gchar *collection_path, + const gchar *label, + const gchar *password, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data, + ...) G_GNUC_NULL_TERMINATED; + +gboolean gsecret_service_store_password_finish (GSecretService *self, + GAsyncResult *result, GError **error); -GHashTable* gsecret_service_get_secrets (GList *items, +void gsecret_service_store_password_sync (GSecretService *self, + const GSecretSchema *schema, + const gchar *collection, + const gchar *display_name, + const gchar *password, + GCancellable *cancellable, + GError **error, + ...) G_GNUC_NULL_TERMINATED; + +void gsecret_service_lookup_password (GSecretService *self, + const GSecretSchema *schema, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data, + ...) G_GNUC_NULL_TERMINATED; + +gchar * gsecret_service_lookup_password_finish (GSecretService *self, + GAsyncResult *result, GError **error); -GHashTable* gsecret_service_get_secrets_for_paths (GList *items, +gchar * gsecret_service_lookup_password_sync (GSecretService *self, + const GSecretSchema *schema, + GCancellable *cancellable, + GError **error, + ...) G_GNUC_NULL_TERMINATED; +#endif + +void gsecret_service_delete_path (GSecretService *self, + const gchar *item_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean gsecret_service_delete_path_finish (GSecretService *self, + GAsyncResult *result, GError **error); -gsecret_collection_create_collection +gboolean gsecret_service_delete_path_sync (GSecretService *self, + const gchar *item_path, + GCancellable *cancellable, + GError **error); -GList* gsecret_service_get_collections +void gsecret_service_delete_password (GSecretService *self, + const GSecretSchema *schema, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data, + ...) G_GNUC_NULL_TERMINATED; +void gsecret_service_delete_passwordv (GSecretService *self, + GHashTable *attributes, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean gsecret_service_delete_password_finish (GSecretService *self, + GAsyncResult *result, + GError **error); + +gboolean gsecret_service_delete_password_sync (GSecretService *self, + const GSecretSchema* schema, + GCancellable *cancellable, + GError **error, + ...) G_GNUC_NULL_TERMINATED; + +gboolean gsecret_service_delete_passwordv_sync (GSecretService *self, + GHashTable *attributes, + GCancellable *cancellable, + GError **error); + +#if 0 GSecretCollection* gsecret_service_read_alias (GSecretService *self, const gchar *alias, GError **error); diff --git a/library/gsecret-types.h b/library/gsecret-types.h index eadfd87..486d91e 100644 --- a/library/gsecret-types.h +++ b/library/gsecret-types.h @@ -25,6 +25,36 @@ typedef enum { GSECRET_ERROR_PROTOCOL = 1, } GSecretError; +typedef enum { + GSECRET_ATTRIBUTE_BOOLEAN, + GSECRET_ATTRIBUTE_STRING, + GSECRET_ATTRIBUTE_INTEGER +} GSecretSchemaType; + +typedef struct { + const gchar *schema_name; + struct { + const gchar* name; + GSecretSchemaType type; + } attributes[32]; + + /* */ + gpointer reserved1; + gpointer reserved2; + gpointer reserved3; + gpointer reserved4; + gpointer reserved5; + gpointer reserved6; + gpointer reserved7; + gpointer reserved8; +} GSecretSchema; + +typedef struct _GSecretCollection GSecretCollection; +typedef struct _GSecretItem GSecretItem; +typedef struct _GSecretPrompt GSecretPrompt; +typedef struct _GSecretService GSecretService; +typedef struct _GSecretValue GSecretValue; + G_END_DECLS #endif /* __G_SERVICE_H___ */ diff --git a/library/gsecret-util.c b/library/gsecret-util.c index 0fd98dd..45c97f1 100644 --- a/library/gsecret-util.c +++ b/library/gsecret-util.c @@ -72,6 +72,13 @@ _gsecret_util_parent_path (const gchar *path) return g_strndup (path, pos - path); } +gboolean +_gsecret_util_empty_path (const gchar *path) +{ + g_return_val_if_fail (path != NULL, TRUE); + return (g_str_equal (path, "") || g_str_equal (path, "/")); +} + GVariant * _gsecret_util_variant_for_attributes (GHashTable *attributes) { @@ -91,3 +98,71 @@ _gsecret_util_variant_for_attributes (GHashTable *attributes) return g_variant_builder_end (&builder); } + +GHashTable * +_gsecret_util_attributes_for_varargs (const GSecretSchema *schema, + va_list args) +{ + const gchar *attribute_name; + GSecretSchemaType type; + GHashTable *attributes; + const gchar *string; + gboolean type_found; + gchar *value = NULL; + gboolean boolean; + gint integer; + gint i; + + attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + for (;;) { + attribute_name = va_arg (args, const gchar *); + if (attribute_name == NULL) + break; + + type_found = FALSE; + for (i = 0; i < G_N_ELEMENTS (schema->attributes); ++i) { + if (!schema->attributes[i].name) + break; + if (g_str_equal (schema->attributes[i].name, attribute_name)) { + type_found = TRUE; + type = schema->attributes[i].type; + break; + } + } + + if (!type_found) { + g_warning ("The attribute '%s' was not found in the password schema.", attribute_name); + g_hash_table_unref (attributes); + return NULL; + } + + switch (type) { + case GSECRET_ATTRIBUTE_BOOLEAN: + boolean = va_arg (args, gboolean); + value = g_strdup (boolean ? "true" : "false"); + break; + case GSECRET_ATTRIBUTE_STRING: + string = va_arg (args, gchar *); + if (!g_utf8_validate (string, -1, NULL)) { + g_warning ("The value for attribute '%s' was not a valid utf-8 string.", attribute_name); + g_hash_table_unref (attributes); + return NULL; + } + value = g_strdup (string); + break; + case GSECRET_ATTRIBUTE_INTEGER: + integer = va_arg (args, gint); + value = g_strdup_printf ("%d", integer); + break; + default: + g_warning ("The password attribute '%s' has an invalid type in the password schema.", attribute_name); + g_hash_table_unref (attributes); + return NULL; + } + + g_hash_table_insert (attributes, g_strdup (attribute_name), value); + } + + return attributes; +} diff --git a/library/gsecret-value.h b/library/gsecret-value.h index ebaa3d3..2046467 100644 --- a/library/gsecret-value.h +++ b/library/gsecret-value.h @@ -15,12 +15,12 @@ #include +#include "gsecret-types.h" + G_BEGIN_DECLS #define GSECRET_TYPE_VALUE (gsecret_service_get_type ()) -typedef struct _GSecretValue GSecretValue; - GType gsecret_value_get_type (void) G_GNUC_CONST; GSecretValue* gsecret_value_new (const gchar *secret, diff --git a/library/org.freedesktop.Secrets.xml b/library/org.freedesktop.Secrets.xml index f9749f8..8182df4 100644 --- a/library/org.freedesktop.Secrets.xml +++ b/library/org.freedesktop.Secrets.xml @@ -68,109 +68,93 @@ - + - + + + + + - - - - - + + + - - - + + + + - - - - + + + + + + + - - - - - - - + + + - - - + + + - - - + + + - - - + - + - + - + - + - + - + - + + + - + + + + - - - + + + - - - - + - - - - + - + + - + - + - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + +s diff --git a/library/tests/Makefile.am b/library/tests/Makefile.am index 9604b24..2cd4221 100644 --- a/library/tests/Makefile.am +++ b/library/tests/Makefile.am @@ -11,8 +11,10 @@ LDADD = \ $(NULL) TEST_PROGS = \ + test-prompt \ test-service \ test-session \ + test-password \ $(NULL) check_PROGRAMS = \ diff --git a/library/tests/mock-service-delete.py b/library/tests/mock-service-delete.py new file mode 100644 index 0000000..40a932d --- /dev/null +++ b/library/tests/mock-service-delete.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python + +import dbus +import mock +import sys + +service = mock.SecretService() +service.add_standard_objects() + +collection = mock.SecretCollection(service, "to_delete", locked=False) +mock.SecretItem(collection, "item", attributes={ "number": "1", "string": "one", "even": "false" }, secret="uno") +mock.SecretItem(collection, "confirm", attributes={ "number": "2", "string": "two", "even": "true" }, secret="dos", confirm=True) + +collection = mock.SecretCollection(service, "two_delete", locked=True) +mock.SecretItem(collection, "locked", attributes={ "number": "3", "string": "three", "even": "false" }, secret="tres") + +service.listen() \ No newline at end of file diff --git a/library/tests/mock-service-prompt.py b/library/tests/mock-service-prompt.py new file mode 100644 index 0000000..785b147 --- /dev/null +++ b/library/tests/mock-service-prompt.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python + +import dbus +import mock +import sys + +class ErrorPrompt(mock.SecretPrompt): + def __init__(self, service, sender, prompt_name): + mock.SecretPrompt.__init__(self, service, sender, prompt_name) + + @dbus.service.method('org.freedesktop.Secret.Prompt') + def Prompt(self, window_id): + raise mock.NotSupported("This should cause prompting to fail") + +class VanishPrompt(mock.SecretPrompt): + def __init__(self, service, sender, prompt_name): + mock.SecretPrompt.__init__(self, service, sender, prompt_name) + + @dbus.service.method('org.freedesktop.Secret.Prompt') + def Prompt(self, window_id): + sys.exit(0) + +class WindowPrompt(mock.SecretPrompt): + def __init__(self, service, sender, prompt_name): + mock.SecretPrompt.__init__(self, service, sender, prompt_name) + + @dbus.service.method('org.freedesktop.Secret.Prompt') + def Prompt(self, window_id): + self.result = dbus.String(window_id, variant_level=1) + mock.SecretPrompt.Prompt(self, window_id) + +service = mock.SecretService() +service.add_standard_objects() + +mock.SecretPrompt(service, None, "simple") +mock.SecretPrompt(service, None, "delay", delay=0.5) +mock.SecretPrompt(service, None, "result", result=dbus.String("Special Result", variant_level=1)) +ErrorPrompt(service, None, "error") +VanishPrompt(service, None, "vanish") +WindowPrompt(service, None, "window") + +service.listen() \ No newline at end of file diff --git a/library/tests/mock/service.py b/library/tests/mock/service.py index b3f9187..6a49382 100644 --- a/library/tests/mock/service.py +++ b/library/tests/mock/service.py @@ -11,9 +11,10 @@ # See the included COPYING file for more information. # +import getopt import os import sys -import getopt +import time import unittest import aes @@ -90,6 +91,48 @@ class AesAlgorithm(): "".join([chr(i) for i in ciph])) +class SecretPrompt(dbus.service.Object): + def __init__(self, service, sender, prompt_name=None, delay=0, + dismiss=False, result=dbus.String("", variant_level=1), + action=None): + self.sender = sender + self.service = service + self.delay = 0 + self.dismiss = False + self.result = result + self.action = action + self.completed = False + if prompt_name: + self.path = "/org/freedesktop/secrets/prompts/%s" % prompt_name + else: + self.path = "/org/freedesktop/secrets/prompts/p%d" % next_identifier() + dbus.service.Object.__init__(self, service.bus_name, self.path) + service.add_prompt(self) + assert self.path not in objects + objects[self.path] = self + + def _complete(self): + if self.completed: + return + self.completed = True + self.Completed(self.dismiss, self.result) + self.remove_from_connection() + + @dbus.service.method('org.freedesktop.Secret.Prompt') + def Prompt(self, window_id): + if self.action: + self.action() + gobject.timeout_add(self.delay * 1000, self._complete) + + @dbus.service.method('org.freedesktop.Secret.Prompt') + def Dismiss(self): + self._complete() + + @dbus.service.signal(dbus_interface='org.freedesktop.Secret.Prompt', signature='bv') + def Completed(self, dismiss, result): + pass + + class SecretSession(dbus.service.Object): def __init__(self, service, sender, algorithm, key): self.sender = sender @@ -116,7 +159,7 @@ class SecretSession(dbus.service.Object): class SecretItem(dbus.service.Object): def __init__(self, collection, identifier, label="Item", attributes={ }, - secret="", content_type="text/plain"): + secret="", confirm=False, content_type="text/plain"): self.collection = collection self.identifier = identifier self.label = label @@ -125,6 +168,7 @@ class SecretItem(dbus.service.Object): self.content_type = content_type self.locked = collection.locked self.path = "%s/%s" % (collection.path, identifier) + self.confirm = confirm dbus.service.Object.__init__(self, collection.service.bus_name, self.path) collection.items[identifier] = self objects[self.path] = self @@ -135,6 +179,10 @@ class SecretItem(dbus.service.Object): return False return True + def perform_delete(self): + del self.collection.items[self.identifier] + del objects[self.path] + @dbus.service.method('org.freedesktop.Secret.Item', sender_keyword='sender') def GetSecret(self, session_path, sender=None): session = objects.get(session_path, None) @@ -144,6 +192,16 @@ class SecretItem(dbus.service.Object): raise IsLocked("secret is locked: %s" % self.path) return session.encode_secret(self.secret, self.content_type) + @dbus.service.method('org.freedesktop.Secret.Item', sender_keyword='sender') + def Delete(self, sender=None): + if self.confirm: + prompt = SecretPrompt(self.collection.service, sender, + dismiss=False, action=self.perform_delete) + return dbus.ObjectPath(prompt.path) + else: + self.perform_delete() + return dbus.ObjectPath("/") + class SecretCollection(dbus.service.Object): def __init__(self, service, identifier, label="Collection", locked=False): @@ -179,6 +237,7 @@ class SecretService(dbus.service.Object): self.bus_name = dbus.service.BusName(name, allow_replacement=True, replace_existing=True) dbus.service.Object.__init__(self, self.bus_name, '/org/freedesktop/secrets') self.sessions = { } + self.prompts = { } self.collections = { } def on_name_owner_changed(owned, old_owner, new_owner): @@ -213,6 +272,14 @@ class SecretService(dbus.service.Object): def remove_session(self, session): self.sessions[session.sender].remove(session) + def add_prompt(self, prompt): + if prompt.sender not in self.prompts: + self.prompts[prompt.sender] = [] + self.prompts[prompt.sender].append(prompt) + + def remove_prompt (self, prompt): + self.prompts[prompt.sender].remove(prompt) + def find_item(self, object): if object.startswith(COLLECTION_PREFIX): parts = object[len(COLLECTION_PREFIX):].split("/", 1) @@ -254,6 +321,7 @@ class SecretService(dbus.service.Object): results[item_path] = item.GetSecret(session_path, sender) return results + def parse_options(args): global bus_name try: diff --git a/library/tests/test-password.c b/library/tests/test-password.c new file mode 100644 index 0000000..9bad85f --- /dev/null +++ b/library/tests/test-password.c @@ -0,0 +1,137 @@ +/* GSecret - GLib wrapper for Secret Service + * + * Copyright 2012 Red Hat Inc. + * + * 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 of the licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + * + * Author: Stef Walter + */ + +#include "config.h" + +#include "gsecret-password.h" +#include "gsecret-private.h" + +#include "egg/egg-testing.h" + +#include + +#include +#include + +static gchar *MOCK_NAME = "org.mock.Service"; + +static const GSecretSchema DELETE_SCHEMA = { + "org.mock.schema.Delete", + { + { "number", GSECRET_ATTRIBUTE_INTEGER }, + { "string", GSECRET_ATTRIBUTE_STRING }, + { "even", GSECRET_ATTRIBUTE_BOOLEAN }, + } +}; + +typedef struct { + GPid pid; +} Test; + +static void +setup (Test *test, + gconstpointer data) +{ + GError *error = NULL; + const gchar *mock_script = data; + gchar *argv[] = { + "python", (gchar *)mock_script, + "--name", MOCK_NAME, + NULL + }; + + _gsecret_service_set_default_bus_name (MOCK_NAME); + + g_spawn_async (SRCDIR, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &test->pid, &error); + g_assert_no_error (error); + g_usleep (200 * 1000); +} + +static void +teardown (Test *test, + gconstpointer unused) +{ + g_assert (test->pid); + if (kill (test->pid, SIGTERM) < 0) + g_error ("kill() failed: %s", g_strerror (errno)); + g_spawn_close_pid (test->pid); +} + +static void +on_complete_get_result (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GAsyncResult **ret = user_data; + g_assert (ret != NULL); + g_assert (*ret == NULL); + *ret = g_object_ref (result); + egg_test_wait_stop (); +} + +static void +test_delete_sync (Test *test, + gconstpointer used) +{ + GError *error = NULL; + gboolean ret; + + ret = gsecret_password_delete_sync (&DELETE_SCHEMA, NULL, &error, + "even", FALSE, + "string", "one", + "number", 1, + NULL); + + g_assert_no_error (error); + g_assert (ret == TRUE); +} + +static void +test_delete_async (Test *test, + gconstpointer used) +{ + GError *error = NULL; + GAsyncResult *result = NULL; + gboolean ret; + + gsecret_password_delete (&DELETE_SCHEMA, NULL, + on_complete_get_result, &result, + "even", FALSE, + "string", "one", + "number", 1, + NULL); + + g_assert (result == NULL); + + egg_test_wait (); + + ret = gsecret_password_delete_finish (result, &error); + g_assert_no_error (error); + g_assert (ret == TRUE); + + g_object_unref (result); +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + g_set_prgname ("test-password"); + g_type_init (); + + g_test_add ("/password/delete-sync", Test, "mock-service-delete.py", setup, test_delete_sync, teardown); + g_test_add ("/password/delete-async", Test, "mock-service-delete.py", setup, test_delete_async, teardown); + + return egg_tests_run_with_loop (); +} diff --git a/library/tests/test-prompt.c b/library/tests/test-prompt.c new file mode 100644 index 0000000..c7197cb --- /dev/null +++ b/library/tests/test-prompt.c @@ -0,0 +1,429 @@ +/* GSecret - GLib wrapper for Secret Service + * + * Copyright 2011 Red Hat Inc. + * + * 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 of the licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + * + * Author: Stef Walter + */ + + +#include "config.h" + +#include "gsecret-item.h" +#include "gsecret-service.h" +#include "gsecret-private.h" +#include "gsecret-prompt.h" + +#include "egg/egg-testing.h" + +#include + +#include +#include + +static gchar *MOCK_NAME = "org.mock.Service"; + +typedef struct { + GPid pid; + GDBusConnection *connection; + GSecretService *service; +} Test; + +static void +setup (Test *test, + gconstpointer data) +{ + GError *error = NULL; + const gchar *mock_script = data; + gchar *argv[] = { + "python", (gchar *)mock_script, + "--name", MOCK_NAME, + NULL + }; + + _gsecret_service_set_default_bus_name (MOCK_NAME); + + g_spawn_async (SRCDIR, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &test->pid, &error); + g_assert_no_error (error); + g_usleep (200 * 1000); + + test->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + g_assert_no_error (error); + + test->service = _gsecret_service_bare_instance (test->connection, NULL); +} + +static void +teardown (Test *test, + gconstpointer unused) +{ + GError *error = NULL; + + g_object_unref (test->service); + egg_assert_not_object (test->service); + + g_assert (test->pid); + if (kill (test->pid, SIGTERM) < 0) { + if (errno != ESRCH) + g_error ("kill() failed: %s", g_strerror (errno)); + } + g_spawn_close_pid (test->pid); + + g_dbus_connection_flush_sync (test->connection, NULL, &error); + g_assert_no_error (error); + g_object_unref (test->connection); +} + + +static void +on_async_result (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GAsyncResult **ret = user_data; + g_assert (ret != NULL); + g_assert (*ret == NULL); + *ret = g_object_ref (result); + egg_test_wait_stop (); +} + +static gboolean +on_idle_increment (gpointer user_data) +{ + guint *value = user_data; + ++(*value); + return TRUE; +} + +static void +test_perform_sync (Test *test, + gconstpointer unused) +{ + GSecretPrompt *prompt; + GError *error = NULL; + gboolean ret; + guint value = 0; + guint increment_id; + + /* Verify that main loop does not run during this call */ + increment_id = g_idle_add (on_idle_increment, &value); + + prompt = gsecret_prompt_instance (test->service, "/org/freedesktop/secrets/prompts/simple"); + + ret = gsecret_prompt_perform_sync (prompt, 0, NULL, &error); + g_assert_no_error (error); + g_assert (ret == TRUE); + + g_assert_cmpuint (value, ==, 0); + g_source_remove (increment_id); + + g_object_unref (prompt); + egg_assert_not_object (prompt); +} + +static void +test_perform_run (Test *test, + gconstpointer unused) +{ + GSecretPrompt *prompt; + GError *error = NULL; + gboolean ret; + guint value = 0; + guint increment_id; + + /* Verify that main loop does run during this call */ + increment_id = g_idle_add (on_idle_increment, &value); + + prompt = gsecret_prompt_instance (test->service, "/org/freedesktop/secrets/prompts/simple"); + + ret = gsecret_prompt_run (prompt, 0, NULL, &error); + g_assert_no_error (error); + g_assert (ret); + + g_assert_cmpuint (value, >, 0); + g_source_remove (increment_id); + + /* Make sure everything completes */ + egg_test_wait_idle (); + + g_object_unref (prompt); + egg_assert_not_object (prompt); +} + +static void +test_perform_async (Test *test, + gconstpointer unused) +{ + GSecretPrompt *prompt; + GError *error = NULL; + GAsyncResult *result = NULL; + gboolean ret; + + prompt = gsecret_prompt_instance (test->service, "/org/freedesktop/secrets/prompts/simple"); + + gsecret_prompt_perform (prompt, 0, NULL, on_async_result, &result); + g_assert (result == NULL); + + egg_test_wait (); + + ret = gsecret_prompt_perform_finish (prompt, result, &error); + g_assert_no_error (error); + g_assert (ret == TRUE); + g_object_unref (result); + + /* Make sure everything completes */ + egg_test_wait_idle (); + + g_object_unref (prompt); + egg_assert_not_object (prompt); +} + +static void +test_perform_cancel (Test *test, + gconstpointer unused) +{ + GSecretPrompt *prompt; + GError *error = NULL; + GAsyncResult *result = NULL; + GCancellable *cancellable; + gboolean ret; + + prompt = gsecret_prompt_instance (test->service, "/org/freedesktop/secrets/prompts/delay"); + + cancellable = g_cancellable_new (); + gsecret_prompt_perform (prompt, 0, cancellable, on_async_result, &result); + g_assert (result == NULL); + + g_cancellable_cancel (cancellable); + g_object_unref (cancellable); + + egg_test_wait (); + + ret = gsecret_prompt_perform_finish (prompt, result, &error); + g_assert_no_error (error); + g_assert (ret == TRUE); + + g_object_unref (result); + + /* Make sure everything completes */ + egg_test_wait_idle (); + + g_object_unref (prompt); + egg_assert_not_object (prompt); +} + +static void +test_perform_fail (Test *test, + gconstpointer unused) +{ + GSecretPrompt *prompt; + GError *error = NULL; + gboolean ret; + + prompt = gsecret_prompt_instance (test->service, "/org/freedesktop/secrets/prompts/error"); + + ret = gsecret_prompt_perform_sync (prompt, 0, NULL, &error); + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED); + g_assert (ret == FALSE); + + g_object_unref (prompt); + egg_assert_not_object (prompt); +} + +static void +test_perform_vanish (Test *test, + gconstpointer unused) +{ + GSecretPrompt *prompt; + GError *error = NULL; + gboolean ret; + + prompt = gsecret_prompt_instance (test->service, "/org/freedesktop/secrets/prompts/vanish"); + + ret = gsecret_prompt_perform_sync (prompt, 0, NULL, &error); + g_assert_no_error (error); + g_assert (ret == FALSE); + + g_object_unref (prompt); + egg_assert_not_object (prompt); +} + +static void +test_prompt_result (Test *test, + gconstpointer unused) +{ + GSecretPrompt *prompt; + GError *error = NULL; + gboolean ret; + GVariant *result; + + prompt = gsecret_prompt_instance (test->service, "/org/freedesktop/secrets/prompts/result"); + + result = gsecret_prompt_get_result_value (prompt); + g_assert (result == NULL); + + ret = gsecret_prompt_perform_sync (prompt, 0, NULL, &error); + g_assert_no_error (error); + g_assert (ret == TRUE); + + result = gsecret_prompt_get_result_value (prompt); + g_assert (result != NULL); + g_assert_cmpstr (g_variant_get_type_string (result), ==, "s"); + g_assert_cmpstr (g_variant_get_string (result, NULL), ==, "Special Result"); + g_variant_unref (result); + + g_object_unref (prompt); + egg_assert_not_object (prompt); +} + +static void +test_prompt_window_id (Test *test, + gconstpointer unused) +{ + GSecretPrompt *prompt; + GError *error = NULL; + gboolean ret; + GVariant *result; + + prompt = gsecret_prompt_instance (test->service, "/org/freedesktop/secrets/prompts/window"); + + ret = gsecret_prompt_perform_sync (prompt, 555, NULL, &error); + g_assert_no_error (error); + g_assert (ret == TRUE); + + result = gsecret_prompt_get_result_value (prompt); + g_assert (result != NULL); + g_assert_cmpstr (g_variant_get_type_string (result), ==, "s"); + g_assert_cmpstr (g_variant_get_string (result, NULL), ==, "555"); + g_variant_unref (result); + + g_object_unref (prompt); + egg_assert_not_object (prompt); +} + +static void +test_service_sync (Test *test, + gconstpointer unused) +{ + GSecretPrompt *prompt; + GError *error = NULL; + gboolean ret; + + prompt = gsecret_prompt_instance (test->service, "/org/freedesktop/secrets/prompts/simple"); + + ret = gsecret_service_prompt_sync (test->service, prompt, NULL, &error); + g_assert_no_error (error); + g_assert (ret == TRUE); + + g_object_unref (prompt); + egg_assert_not_object (prompt); +} + +static void +test_service_async (Test *test, + gconstpointer unused) +{ + GSecretPrompt *prompt; + GError *error = NULL; + GAsyncResult *result = NULL; + gboolean ret; + + prompt = gsecret_prompt_instance (test->service, "/org/freedesktop/secrets/prompts/simple"); + + gsecret_service_prompt (test->service, prompt, NULL, on_async_result, &result); + g_assert (result == NULL); + + egg_test_wait (); + + ret = gsecret_service_prompt_finish (test->service, result, &error); + g_assert_no_error (error); + g_assert (ret == TRUE); + g_object_unref (result); + + /* Make sure everything completes */ + egg_test_wait_idle (); + + g_object_unref (prompt); + egg_assert_not_object (prompt); +} + +static void +test_service_fail (Test *test, + gconstpointer unused) +{ + GSecretPrompt *prompt; + GError *error = NULL; + GAsyncResult *result = NULL; + gboolean ret; + + prompt = gsecret_prompt_instance (test->service, "/org/freedesktop/secrets/prompts/error"); + + gsecret_service_prompt (test->service, prompt, NULL, on_async_result, &result); + g_assert (result == NULL); + + egg_test_wait (); + + ret = gsecret_service_prompt_finish (test->service, result, &error); + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED); + g_assert (ret == FALSE); + g_object_unref (result); + + /* Make sure everything completes */ + egg_test_wait_idle (); + + g_object_unref (prompt); + egg_assert_not_object (prompt); +} + +static void +test_service_path (Test *test, + gconstpointer unused) +{ + GError *error = NULL; + GAsyncResult *result = NULL; + gboolean ret; + + gsecret_service_prompt_path (test->service, "/org/freedesktop/secrets/prompts/simple", + NULL, on_async_result, &result); + g_assert (result == NULL); + + egg_test_wait (); + + ret = gsecret_service_prompt_finish (test->service, result, &error); + g_assert_no_error (error); + g_assert (ret == TRUE); + g_object_unref (result); + + /* Make sure everything completes */ + egg_test_wait_idle (); +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + g_set_prgname ("test-prompt"); + g_type_init (); + + g_test_add ("/prompt/run", Test, "mock-service-prompt.py", setup, test_perform_run, teardown); + g_test_add ("/prompt/perform-sync", Test, "mock-service-prompt.py", setup, test_perform_sync, teardown); + g_test_add ("/prompt/perform-async", Test, "mock-service-prompt.py", setup, test_perform_async, teardown); + g_test_add ("/prompt/perform-cancel", Test, "mock-service-prompt.py", setup, test_perform_cancel, teardown); + g_test_add ("/prompt/perform-fail", Test, "mock-service-prompt.py", setup, test_perform_fail, teardown); + g_test_add ("/prompt/perform-vanish", Test, "mock-service-prompt.py", setup, test_perform_vanish, teardown); + g_test_add ("/prompt/result", Test, "mock-service-prompt.py", setup, test_prompt_result, teardown); + g_test_add ("/prompt/window-id", Test, "mock-service-prompt.py", setup, test_prompt_window_id, teardown); + + g_test_add ("/prompt/service-sync", Test, "mock-service-prompt.py", setup, test_service_sync, teardown); + g_test_add ("/prompt/service-async", Test, "mock-service-prompt.py", setup, test_service_async, teardown); + g_test_add ("/prompt/service-path", Test, "mock-service-prompt.py", setup, test_service_path, teardown); + g_test_add ("/prompt/service-fail", Test, "mock-service-prompt.py", setup, test_service_fail, teardown); + + return egg_tests_run_with_loop (); +} diff --git a/library/tests/test-service.c b/library/tests/test-service.c index 50fbbd0..b7e59c6 100644 --- a/library/tests/test-service.c +++ b/library/tests/test-service.c @@ -25,6 +25,15 @@ static gchar *MOCK_NAME = "org.mock.Service"; +static const GSecretSchema DELETE_SCHEMA = { + "org.mock.schema.Delete", + { + { "number", GSECRET_ATTRIBUTE_INTEGER }, + { "string", GSECRET_ATTRIBUTE_STRING }, + { "even", GSECRET_ATTRIBUTE_BOOLEAN }, + } +}; + typedef struct { GPid pid; GDBusConnection *connection; @@ -32,8 +41,8 @@ typedef struct { } Test; static void -setup (Test *test, - gconstpointer data) +setup_mock (Test *test, + gconstpointer data) { GError *error = NULL; const gchar *mock_script = data; @@ -43,29 +52,49 @@ setup (Test *test, NULL }; + _gsecret_service_set_default_bus_name (MOCK_NAME); + g_spawn_async (SRCDIR, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &test->pid, &error); g_assert_no_error (error); g_usleep (200 * 1000); +} + +static void +setup (Test *test, + gconstpointer data) +{ + GError *error = NULL; + + setup_mock (test, data); test->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); g_assert_no_error (error); - test->service = _gsecret_service_bare_instance (test->connection, MOCK_NAME); + test->service = _gsecret_service_bare_instance (test->connection, NULL); +} + +static void +teardown_mock (Test *test, + gconstpointer unused) +{ + g_assert (test->pid); + if (kill (test->pid, SIGTERM) < 0) + g_error ("kill() failed: %s", g_strerror (errno)); + g_spawn_close_pid (test->pid); } static void teardown (Test *test, gconstpointer unused) { + egg_test_wait_idle (); + g_object_unref (test->service); egg_assert_not_object (test->service); g_clear_object (&test->connection); - g_assert (test->pid); - if (kill (test->pid, SIGTERM) < 0) - g_error ("kill() failed: %s", g_strerror (errno)); - g_spawn_close_pid (test->pid); + teardown_mock (test, unused); } static void @@ -116,6 +145,60 @@ test_instance (void) g_object_unref (connection); } +static void +test_connect_sync (Test *test, + gconstpointer used) +{ + GError *error = NULL; + GAsyncResult *result = NULL; + GSecretService *service; + const gchar *path; + + /* Passing false, not session */ + _gsecret_service_bare_connect (MOCK_NAME, FALSE, NULL, on_complete_get_result, &result); + g_assert (result == NULL); + + egg_test_wait (); + + service = _gsecret_service_bare_connect_finish (result, &error); + g_assert (GSECRET_IS_SERVICE (service)); + g_assert_no_error (error); + g_object_unref (result); + + path = gsecret_service_get_session_path (service); + g_assert (path == NULL); + + g_object_unref (service); + egg_assert_not_object (service); +} + +static void +test_connect_ensure_sync (Test *test, + gconstpointer used) +{ + GError *error = NULL; + GAsyncResult *result = NULL; + GSecretService *service; + const gchar *path; + + /* Passing true, ensures session is established */ + _gsecret_service_bare_connect (MOCK_NAME, TRUE, NULL, on_complete_get_result, &result); + g_assert (result == NULL); + + egg_test_wait (); + + service = _gsecret_service_bare_connect_finish (result, &error); + g_assert_no_error (error); + g_assert (GSECRET_IS_SERVICE (service)); + g_object_unref (result); + + path = gsecret_service_get_session_path (service); + g_assert (path != NULL); + + g_object_unref (service); + egg_assert_not_object (service); +} + static void test_search_paths (Test *test, gconstpointer used) @@ -405,6 +488,111 @@ test_secrets_for_paths_async (Test *test, g_hash_table_unref (values); } +static void +test_delete_for_path_sync (Test *test, + gconstpointer used) + +{ + const gchar *path_item_one = "/org/freedesktop/secrets/collection/to_delete/item"; + GError *error = NULL; + gboolean ret; + + ret = gsecret_service_delete_path_sync (test->service, path_item_one, NULL, &error); + g_assert_no_error (error); + g_assert (ret == TRUE); +} + +static void +test_delete_for_path_sync_prompt (Test *test, + gconstpointer used) + +{ + const gchar *path_item_one = "/org/freedesktop/secrets/collection/to_delete/confirm"; + GError *error = NULL; + gboolean ret; + + ret = gsecret_service_delete_path_sync (test->service, path_item_one, NULL, &error); + g_assert_no_error (error); + g_assert (ret == TRUE); +} + +static void +test_delete_password_sync (Test *test, + gconstpointer used) +{ + GError *error = NULL; + gboolean ret; + + ret = gsecret_service_delete_password_sync (test->service, &DELETE_SCHEMA, NULL, &error, + "even", FALSE, + "string", "one", + "number", 1, + NULL); + + g_assert_no_error (error); + g_assert (ret == TRUE); +} + +static void +test_delete_password_async (Test *test, + gconstpointer used) +{ + GError *error = NULL; + GAsyncResult *result = NULL; + gboolean ret; + + gsecret_service_delete_password (test->service, &DELETE_SCHEMA, NULL, + on_complete_get_result, &result, + "even", FALSE, + "string", "one", + "number", 1, + NULL); + + g_assert (result == NULL); + + egg_test_wait (); + + ret = gsecret_service_delete_password_finish (test->service, result, &error); + g_assert_no_error (error); + g_assert (ret == TRUE); + + g_object_unref (result); +} + +static void +test_delete_password_locked (Test *test, + gconstpointer used) +{ + GError *error = NULL; + gboolean ret; + + ret = gsecret_service_delete_password_sync (test->service, &DELETE_SCHEMA, NULL, &error, + "even", FALSE, + "string", "three", + "number", 3, + NULL); + + g_assert_no_error (error); + g_assert (ret == TRUE); +} + +static void +test_delete_password_no_match (Test *test, + gconstpointer used) +{ + GError *error = NULL; + gboolean ret; + + /* Won't match anything */ + ret = gsecret_service_delete_password_sync (test->service, &DELETE_SCHEMA, NULL, &error, + "even", TRUE, + "string", "one", + NULL); + + g_assert_no_error (error); + g_assert (ret == FALSE); +} + int main (int argc, char **argv) { @@ -413,14 +601,26 @@ main (int argc, char **argv) g_type_init (); g_test_add_func ("/service/instance", test_instance); + + g_test_add ("/service/connect-sync", Test, "mock-service-normal.py", setup_mock, test_connect_sync, teardown_mock); + g_test_add ("/service/connect-ensure-sync", Test, "mock-service-normal.py", setup_mock, test_connect_ensure_sync, teardown_mock); + g_test_add ("/service/search-for-paths", Test, "mock-service-normal.py", setup, test_search_paths, teardown); g_test_add ("/service/search-for-paths-async", Test, "mock-service-normal.py", setup, test_search_paths_async, teardown); g_test_add ("/service/search-for-paths-nulls", Test, "mock-service-normal.py", setup, test_search_paths_nulls, teardown); + g_test_add ("/service/secret-for-path", Test, "mock-service-normal.py", setup, test_secret_for_path, teardown); g_test_add ("/service/secret-for-path-plain", Test, "mock-service-only-plain.py", setup, test_secret_for_path, teardown); g_test_add ("/service/secret-for-path-async", Test, "mock-service-normal.py", setup, test_secret_for_path_async, teardown); g_test_add ("/service/secrets-for-paths", Test, "mock-service-normal.py", setup, test_secrets_for_paths, teardown); g_test_add ("/service/secrets-for-paths-async", Test, "mock-service-normal.py", setup, test_secrets_for_paths_async, teardown); + g_test_add ("/service/delete-for-path", Test, "mock-service-delete.py", setup, test_delete_for_path_sync, teardown); + g_test_add ("/service/delete-for-path-with-prompt", Test, "mock-service-delete.py", setup, test_delete_for_path_sync_prompt, teardown); + g_test_add ("/service/delete-password-sync", Test, "mock-service-delete.py", setup, test_delete_password_sync, teardown); + g_test_add ("/service/delete-password-async", Test, "mock-service-delete.py", setup, test_delete_password_async, teardown); + g_test_add ("/service/delete-password-locked", Test, "mock-service-delete.py", setup, test_delete_password_locked, teardown); + g_test_add ("/service/delete-password-no-match", Test, "mock-service-delete.py", setup, test_delete_password_no_match, teardown); + return egg_tests_run_with_loop (); } diff --git a/library/tests/test-session.c b/library/tests/test-session.c index 5611939..98df7be 100644 --- a/library/tests/test-session.c +++ b/library/tests/test-session.c @@ -44,6 +44,8 @@ setup (Test *test, NULL }; + _gsecret_service_set_default_bus_name (MOCK_NAME); + g_spawn_async (SRCDIR, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &test->pid, &error); g_assert_no_error (error); g_usleep (200 * 1000); @@ -51,7 +53,7 @@ setup (Test *test, test->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); g_assert_no_error (error); - test->service = _gsecret_service_bare_instance (test->connection, MOCK_NAME); + test->service = _gsecret_service_bare_instance (test->connection, NULL); } static void