[libsecret] Keep a cached SecretValue on each SecretItem



commit 49dd623e6e6a688ce7b63dc75fb410a203a58256
Author: Stef Walter <stefw gnome org>
Date:   Thu Jul 5 15:38:38 2012 +0200

    Keep a cached SecretValue on each SecretItem
    
     * This item can be retrieved immediately and loaded asynchronously
     * It can also be loaded during initialization of the SecretItem
       object

 docs/reference/libsecret/libsecret-sections.txt |    9 +-
 library/secret-collection.c                     |    5 +-
 library/secret-item.c                           |  358 ++++++++++++++++++-----
 library/secret-item.h                           |   17 +-
 library/secret-methods.c                        |    5 +-
 library/secret-private.h                        |    3 +
 library/tests/test-item.c                       |   83 ++++--
 library/tests/test-methods.c                    |   12 +-
 8 files changed, 377 insertions(+), 115 deletions(-)
---
diff --git a/docs/reference/libsecret/libsecret-sections.txt b/docs/reference/libsecret/libsecret-sections.txt
index c9fcdff..1168fea 100644
--- a/docs/reference/libsecret/libsecret-sections.txt
+++ b/docs/reference/libsecret/libsecret-sections.txt
@@ -38,6 +38,7 @@ secret_collection_get_type
 <INCLUDE>secret/secret-unstable.h</INCLUDE>
 SecretItem
 SecretItemClass
+SecretItemFlags
 secret_item_new
 secret_item_new_finish
 secret_item_new_sync
@@ -56,12 +57,14 @@ secret_item_get_label
 secret_item_set_label
 secret_item_set_label_finish
 secret_item_set_label_sync
+secret_item_get_flags
 secret_item_get_locked
 secret_item_get_modified
 secret_item_get_service
 secret_item_get_secret
-secret_item_get_secret_finish
-secret_item_get_secret_sync
+secret_item_load_secret
+secret_item_load_secret_finish
+secret_item_load_secret_sync
 secret_item_set_secret
 secret_item_set_secret_finish
 secret_item_set_secret_sync
@@ -73,8 +76,10 @@ SECRET_ITEM
 SECRET_ITEM_CLASS
 SECRET_ITEM_GET_CLASS
 SECRET_TYPE_ITEM
+SECRET_TYPE_ITEM_FLAGS
 SecretItemPrivate
 secret_item_get_type
+secret_item_flags_get_type
 </SECTION>
 
 <SECTION>
diff --git a/library/secret-collection.c b/library/secret-collection.c
index d762989..56992b5 100644
--- a/library/secret-collection.c
+++ b/library/secret-collection.c
@@ -321,8 +321,8 @@ collection_load_items_async (SecretCollection *self,
 
 		/* No such collection yet create a new one */
 		if (item == NULL) {
-			secret_item_new (self->pv->service, path, cancellable,
-			                 on_load_item, g_object_ref (res));
+			secret_item_new (self->pv->service, path, SECRET_ITEM_NONE,
+			                 cancellable, on_load_item, g_object_ref (res));
 			closure->items_loading++;
 
 		} else {
@@ -375,6 +375,7 @@ collection_load_items_sync (SecretCollection *self,
 		/* No such collection yet create a new one */
 		if (item == NULL) {
 			item = secret_item_new_sync (self->pv->service, path,
+			                             SECRET_ITEM_NONE,
 			                             cancellable, error);
 			if (item == NULL) {
 				ret = FALSE;
diff --git a/library/secret-item.c b/library/secret-item.c
index 4435a91..ce53955 100644
--- a/library/secret-item.c
+++ b/library/secret-item.c
@@ -16,6 +16,7 @@
 
 #include "secret-collection.h"
 #include "secret-dbus-generated.h"
+#include "secret-enum-types.h"
 #include "secret-item.h"
 #include "secret-private.h"
 #include "secret-service.h"
@@ -64,9 +65,19 @@
  * The class for #SecretItem.
  */
 
+/**
+ * SecretItemFlags:
+ * @SECRET_ITEM_NONE: no flags for initializing the #SecretItem
+ * @SECRET_ITEM_LOAD_SECRET: load the secret when initializing the #SecretItem
+ *
+ * Flags which determine which parts of the #SecretItem proxy are initialized
+ * during a secret_item_new() operation.
+ */
+
 enum {
 	PROP_0,
 	PROP_SERVICE,
+	PROP_FLAGS,
 	PROP_ATTRIBUTES,
 	PROP_LABEL,
 	PROP_LOCKED,
@@ -74,10 +85,15 @@ enum {
 	PROP_MODIFIED
 };
 
-/* Thread safe: no changes between construct and finalize */
 struct _SecretItemPrivate {
+	/* No changes between construct and finalize */
 	SecretService *service;
+	SecretItemFlags init_flags;
 	GCancellable *cancellable;
+
+	/* Locked by mutex */
+	GMutex mutex;
+	SecretValue *value;
 };
 
 static GInitableIface *secret_item_initable_parent_iface = NULL;
@@ -98,6 +114,7 @@ secret_item_init (SecretItem *self)
 {
 	self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, SECRET_TYPE_ITEM, SecretItemPrivate);
 	self->pv->cancellable = g_cancellable_new ();
+	g_mutex_init (&self->pv->mutex);
 }
 
 static void
@@ -150,6 +167,9 @@ secret_item_set_property (GObject *obj,
 			g_object_add_weak_pointer (G_OBJECT (self->pv->service),
 			                           (gpointer *)&self->pv->service);
 		break;
+	case PROP_FLAGS:
+		self->pv->init_flags = g_value_get_flags (value);
+		break;
 	case PROP_ATTRIBUTES:
 		secret_item_set_attributes (self, g_value_get_boxed (value),
 		                            self->pv->cancellable, on_set_attributes,
@@ -178,6 +198,9 @@ secret_item_get_property (GObject *obj,
 	case PROP_SERVICE:
 		g_value_set_object (value, self->pv->service);
 		break;
+	case PROP_FLAGS:
+		g_value_set_flags (value, secret_item_get_flags (self));
+		break;
 	case PROP_ATTRIBUTES:
 		g_value_take_boxed (value, secret_item_get_attributes (self));
 		break;
@@ -219,6 +242,7 @@ secret_item_finalize (GObject *obj)
 		                              (gpointer *)&self->pv->service);
 
 	g_object_unref (self->pv->cancellable);
+	g_mutex_clear (&self->pv->mutex);
 
 	G_OBJECT_CLASS (secret_item_parent_class)->finalize (obj);
 }
@@ -286,6 +310,17 @@ secret_item_class_init (SecretItemClass *klass)
 	                                 SECRET_TYPE_SERVICE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
 
 	/**
+	 * SecretService:flags:
+	 *
+	 * A set of flags describing which parts of the secret item have
+	 * been initialized.
+	 */
+	g_object_class_install_property (gobject_class, PROP_FLAGS,
+	             g_param_spec_flags ("flags", "Flags", "Item flags",
+	                                 secret_item_flags_get_type (), SECRET_ITEM_NONE,
+	                                 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+	/**
 	 * SecretItem:attributes:
 	 *
 	 * The attributes set on this item. Attributes are used to locate an
@@ -344,11 +379,70 @@ secret_item_class_init (SecretItemClass *klass)
 	g_type_class_add_private (gobject_class, sizeof (SecretItemPrivate));
 }
 
+typedef struct {
+	GCancellable *cancellable;
+} InitClosure;
+
+static void
+init_closure_free (gpointer data)
+{
+	InitClosure *closure = data;
+	g_clear_object (&closure->cancellable);
+	g_slice_free (InitClosure, closure);
+}
+
+static gboolean
+item_ensure_for_flags_sync (SecretItem *self,
+                            SecretItemFlags flags,
+                            GCancellable *cancellable,
+                            GError **error)
+{
+	if (flags & SECRET_ITEM_LOAD_SECRET && !secret_item_get_locked (self)) {
+		if (!secret_item_load_secret_sync (self, cancellable, error))
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+static void
+on_init_load_secret (GObject *source,
+                     GAsyncResult *result,
+                     gpointer user_data)
+{
+	GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
+	SecretItem *self = SECRET_ITEM (source);
+	GError *error = NULL;
+
+	if (!secret_item_load_secret_finish (self, result, &error))
+		g_simple_async_result_take_error (async, error);
+
+	g_simple_async_result_complete (async);
+	g_object_unref (async);
+}
+
+static void
+item_ensure_for_flags_async (SecretItem *self,
+                             SecretItemFlags flags,
+                             GSimpleAsyncResult *async)
+{
+	InitClosure *init = g_simple_async_result_get_op_res_gpointer (async);
+
+	if (flags & SECRET_ITEM_LOAD_SECRET && !secret_item_get_locked (self))
+		secret_item_load_secret (self, init->cancellable,
+		                         on_init_load_secret, g_object_ref (async));
+
+	else
+		g_simple_async_result_complete (async);
+}
+
 static gboolean
 secret_item_initable_init (GInitable *initable,
                            GCancellable *cancellable,
                            GError **error)
 {
+	SecretItem *self;
 	GDBusProxy *proxy;
 
 	if (!secret_item_initable_parent_iface->init (initable, cancellable, error))
@@ -363,7 +457,8 @@ secret_item_initable_init (GInitable *initable,
 		return FALSE;
 	}
 
-	return TRUE;
+	self = SECRET_ITEM (initable);
+	return item_ensure_for_flags_sync (self, self->pv->init_flags, cancellable, error);
 }
 
 static void
@@ -387,14 +482,18 @@ on_init_base (GObject *source,
 	if (!secret_item_async_initable_parent_iface->init_finish (G_ASYNC_INITABLE (self),
 	                                                           result, &error)) {
 		g_simple_async_result_take_error (res, error);
+		g_simple_async_result_complete (res);
 
 	} else if (!_secret_util_have_cached_properties (proxy)) {
 		g_simple_async_result_set_error (res, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD,
 		                                 "No such secret item at path: %s",
 		                                 g_dbus_proxy_get_object_path (proxy));
+		g_simple_async_result_complete (res);
+
+	} else {
+		item_ensure_for_flags_async (self, self->pv->init_flags, res);
 	}
 
-	g_simple_async_result_complete (res);
 	g_object_unref (res);
 }
 
@@ -406,9 +505,13 @@ secret_item_async_initable_init_async (GAsyncInitable *initable,
                                        gpointer user_data)
 {
 	GSimpleAsyncResult *res;
+	InitClosure *init;
 
 	res = g_simple_async_result_new (G_OBJECT (initable), callback, user_data,
 	                                 secret_item_async_initable_init_async);
+	init = g_slice_new0 (InitClosure);
+	init->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+	g_simple_async_result_set_op_res_gpointer (res, init, init_closure_free);
 
 	secret_item_async_initable_parent_iface->init_async (initable, io_priority,
 	                                                     cancellable,
@@ -445,6 +548,7 @@ secret_item_async_initable_iface (GAsyncInitableIface *iface)
  * secret_item_new:
  * @service: a secret service object
  * @item_path: the D-Bus path of the collection
+ * @flags: initialization flags for the new item
  * @cancellable: optional cancellation object
  * @callback: called when the operation completes
  * @user_data: data to be passed to the callback
@@ -456,6 +560,7 @@ secret_item_async_initable_iface (GAsyncInitableIface *iface)
 void
 secret_item_new (SecretService *service,
                  const gchar *item_path,
+                 SecretItemFlags flags,
                  GCancellable *cancellable,
                  GAsyncReadyCallback callback,
                  gpointer user_data)
@@ -477,6 +582,7 @@ secret_item_new (SecretService *service,
 	                            "g-object-path", item_path,
 	                            "g-interface-name", SECRET_ITEM_INTERFACE,
 	                            "service", service,
+	                            "flags", flags,
 	                            NULL);
 }
 
@@ -513,6 +619,7 @@ secret_item_new_finish (GAsyncResult *result,
  * secret_item_new_sync:
  * @service: a secret service object
  * @item_path: the D-Bus path of the item
+ * @flags: initialization flags for the new item
  * @cancellable: optional cancellation object
  * @error: location to place an error on failure
  *
@@ -527,6 +634,7 @@ secret_item_new_finish (GAsyncResult *result,
 SecretItem *
 secret_item_new_sync (SecretService *service,
                       const gchar *item_path,
+                      SecretItemFlags flags,
                       GCancellable *cancellable,
                       GError **error)
 {
@@ -548,6 +656,7 @@ secret_item_new_sync (SecretService *service,
 	                       "g-object-path", item_path,
 	                       "g-interface-name", SECRET_ITEM_INTERFACE,
 	                       "service", service,
+	                       "flags", flags,
 	                       NULL);
 }
 
@@ -571,10 +680,41 @@ secret_item_refresh (SecretItem *self)
 	                             NULL, NULL, NULL);
 }
 
+void
+_secret_item_set_cached_secret (SecretItem *self,
+                                SecretValue *value)
+{
+	SecretValue *other = NULL;
+	gboolean updated = FALSE;
+
+	g_return_if_fail (SECRET_IS_ITEM (self));
+
+	if (value != NULL)
+		secret_value_ref (value);
+
+	g_mutex_lock (&self->pv->mutex);
+
+	if (value != self->pv->value) {
+		other = self->pv->value;
+		self->pv->value = value;
+		updated = TRUE;
+	} else {
+		other = value;
+	}
+
+	g_mutex_unlock (&self->pv->mutex);
+
+	if (other != NULL)
+		secret_value_unref (other);
+
+	if (updated)
+		g_object_notify (G_OBJECT (self), "flags");
+}
 
 typedef struct {
 	GCancellable *cancellable;
 	SecretItem *item;
+	SecretValue *value;
 } CreateClosure;
 
 static void
@@ -583,6 +723,7 @@ create_closure_free (gpointer data)
 	CreateClosure *closure = data;
 	g_clear_object (&closure->cancellable);
 	g_clear_object (&closure->item);
+	secret_value_unref (closure->value);
 	g_slice_free (CreateClosure, closure);
 }
 
@@ -599,6 +740,9 @@ on_create_item (GObject *source,
 	if (error != NULL)
 		g_simple_async_result_take_error (res, error);
 
+	/* As a convenince mark down the SecretValue on the item */
+	_secret_item_set_cached_secret (closure->item, closure->value);
+
 	g_simple_async_result_complete (res);
 	g_object_unref (res);
 }
@@ -616,8 +760,9 @@ on_create_path (GObject *source,
 
 	path = secret_service_create_item_path_finish (service, result, &error);
 	if (error == NULL) {
-		secret_item_new (service, path, closure->cancellable,
-		                 on_create_item, g_object_ref (res));
+		secret_item_new (service, path, SECRET_ITEM_NONE,
+		                 closure->cancellable, on_create_item,
+		                 g_object_ref (res));
 	} else {
 		g_simple_async_result_take_error (res, error);
 		g_simple_async_result_complete (res);
@@ -696,6 +841,7 @@ secret_item_create (SecretCollection *collection,
 	                                 secret_item_create);
 	closure = g_slice_new0 (CreateClosure);
 	closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+	closure->value = secret_value_ref (value);
 	g_simple_async_result_set_op_res_gpointer (res, closure, create_closure_free);
 
 	properties = item_properties_new (label, attributes);
@@ -799,7 +945,8 @@ secret_item_create_sync (SecretCollection *collection,
 	                                             value, replace, cancellable, error);
 
 	if (path != NULL) {
-		item = secret_item_new_sync (service, path, cancellable, error);
+		item = secret_item_new_sync (service, path, SECRET_ITEM_NONE,
+		                             cancellable, error);
 		g_free (path);
 	}
 
@@ -936,6 +1083,36 @@ secret_item_delete_sync (SecretItem *self,
 }
 
 /**
+ * secret_item_get_flags:
+ * @self: the secret item proxy
+ *
+ * Get the flags representing what features of the #SecretItem proxy
+ * have been initialized.
+ *
+ * Use secret_item_load_secret() to initialize further features
+ * and change the flags.
+ *
+ * Returns: the flags for features initialized
+ */
+SecretItemFlags
+secret_item_get_flags (SecretItem *self)
+{
+	SecretServiceFlags flags = 0;
+
+	g_return_val_if_fail (SECRET_IS_ITEM (self), SECRET_ITEM_NONE);
+
+	g_mutex_lock (&self->pv->mutex);
+
+	if (self->pv->value)
+		flags |= SECRET_ITEM_LOAD_SECRET;
+
+	g_mutex_unlock (&self->pv->mutex);
+
+	return flags;
+
+}
+
+/**
  * secret_item_get_service:
  * @self: an item
  *
@@ -951,30 +1128,59 @@ secret_item_get_service (SecretItem *self)
 }
 
 
+/**
+ * secret_item_get_secret:
+ * @self: an item
+ *
+ * Get the secret value of this item. If this item is locked or the secret
+ * has not yet been loaded then this will return %NULL.
+ *
+ * To load the secret call the secret_item_load_secret() method. You can also
+ * pass the %SECRET_ITEM_LOAD_SECRET flag to secret_item_new().
+ *
+ * Returns: (transfer full) (allow-none): the secret value which should be
+ *          released with secret_value_unref(), or %NULL
+ */
+SecretValue *
+secret_item_get_secret (SecretItem *self)
+{
+	SecretValue *value = NULL;
+
+	g_return_val_if_fail (SECRET_IS_ITEM (self), NULL);
+
+	g_mutex_lock (&self->pv->mutex);
+
+	if (self->pv->value)
+		value = secret_value_ref (self->pv->value);
+
+	g_mutex_unlock (&self->pv->mutex);
+
+	return value;
+}
+
+
 typedef struct {
 	GCancellable *cancellable;
-	SecretValue *value;
-} GetClosure;
+} LoadClosure;
 
 static void
-get_closure_free (gpointer data)
+load_closure_free (gpointer data)
 {
-	GetClosure *closure = data;
+	LoadClosure *closure = data;
 	g_clear_object (&closure->cancellable);
-	secret_value_unref (closure->value);
-	g_slice_free (GetClosure, closure);
+	g_slice_free (LoadClosure, closure);
 }
 
 static void
-on_item_get_secret (GObject *source,
-                    GAsyncResult *result,
-                    gpointer user_data)
+on_item_load_secret (GObject *source,
+                     GAsyncResult *result,
+                     gpointer user_data)
 {
 	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
 	SecretItem *self = SECRET_ITEM (g_async_result_get_source_object (user_data));
-	GetClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
 	SecretSession *session;
 	GError *error = NULL;
+	SecretValue *value;
 	GVariant *retval;
 	GVariant *child;
 
@@ -984,12 +1190,16 @@ on_item_get_secret (GObject *source,
 		g_variant_unref (retval);
 
 		session = _secret_service_get_session (self->pv->service);
-		closure->value = _secret_session_decode_secret (session, child);
+		value = _secret_session_decode_secret (session, child);
 		g_variant_unref (child);
 
-		if (closure->value == NULL)
+		if (value == NULL) {
 			g_set_error (&error, SECRET_ERROR, SECRET_ERROR_PROTOCOL,
 			             _("Received invalid secret from the secret storage"));
+		} else {
+			_secret_item_set_cached_secret (self, value);
+			secret_value_unref (value);
+		}
 	}
 
 	if (error != NULL)
@@ -1000,13 +1210,13 @@ on_item_get_secret (GObject *source,
 }
 
 static void
-on_get_ensure_session (GObject *source,
-                       GAsyncResult *result,
-                       gpointer user_data)
+on_load_ensure_session (GObject *source,
+                        GAsyncResult *result,
+                        gpointer user_data)
 {
 	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
 	SecretItem *self = SECRET_ITEM (g_async_result_get_source_object (user_data));
-	GetClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
+	LoadClosure *load = g_simple_async_result_get_op_res_gpointer (res);
 	const gchar *session_path;
 	GError *error = NULL;
 
@@ -1019,8 +1229,8 @@ on_get_ensure_session (GObject *source,
 		g_assert (session_path != NULL && session_path[0] != '\0');
 		g_dbus_proxy_call (G_DBUS_PROXY (self), "GetSecret",
 		                   g_variant_new ("(o)", session_path),
-		                   G_DBUS_CALL_FLAGS_NONE, -1, closure->cancellable,
-		                   on_item_get_secret, g_object_ref (res));
+		                   G_DBUS_CALL_FLAGS_NONE, -1, load->cancellable,
+		                   on_item_load_secret, g_object_ref (res));
 	}
 
 	g_object_unref (self);
@@ -1028,100 +1238,103 @@ on_get_ensure_session (GObject *source,
 }
 
 /**
- * secret_item_get_secret:
- * @self: an item
+ * secret_item_load_secret:
+ * @self: an item proxy
  * @cancellable: optional cancellation object
  * @callback: called when the operation completes
  * @user_data: data to pass to the callback
  *
- * Get the secret value of this item.
+ * Load the secret value of this item.
  *
  * Each item has a single secret which might be a password or some
- * other secret binary value.
+ * other secret binary value. You can load the secret value on creation of
+ * a secret item proxy by passing the %SECRET_ITEM_LOAD_SECRET flag to the
+ * secret_item_new() method.
+ *
+ * This function will fail if the secret item is locked.
  *
  * This function returns immediately and completes asynchronously.
  */
 void
-secret_item_get_secret (SecretItem *self,
-                        GCancellable *cancellable,
-                        GAsyncReadyCallback callback,
-                        gpointer user_data)
+secret_item_load_secret (SecretItem *self,
+                         GCancellable *cancellable,
+                         GAsyncReadyCallback callback,
+                         gpointer user_data)
 {
 	GSimpleAsyncResult *res;
-	GetClosure *closure;
+	LoadClosure *closure;
 
 	g_return_if_fail (SECRET_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, secret_item_get_secret);
-	closure = g_slice_new0 (GetClosure);
+	                                 user_data, secret_item_load_secret);
+	closure = g_slice_new0 (LoadClosure);
 	closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
-	g_simple_async_result_set_op_res_gpointer (res, closure, get_closure_free);
+	g_simple_async_result_set_op_res_gpointer (res, closure, load_closure_free);
 
 	secret_service_ensure_session (self->pv->service, cancellable,
-	                               on_get_ensure_session,
+	                               on_load_ensure_session,
 	                               g_object_ref (res));
 
 	g_object_unref (res);
 }
 
 /**
- * secret_item_get_secret_finish:
- * @self: an item
+ * secret_item_load_secret_finish:
+ * @self: an item proxy
  * @result: asynchronous result passed to callback
  * @error: location to place error on failure
  *
- * Get the secret value of this item.
+ * Complete asynchronous operation to load the secret value of this item.
  *
- * Complete asynchronous operation to get the secret value of this item.
+ * The newly loaded secret value can be accessed by calling
+ * secret_item_get_secret().
  *
- * Returns: (transfer full): the newly allocated secret value in this
- *          item, which should be released with secret_value_unref()
+ * Returns: whether the secret item succesfully loaded or not
  */
-SecretValue *
-secret_item_get_secret_finish (SecretItem *self,
-                               GAsyncResult *result,
-                               GError **error)
+gboolean
+secret_item_load_secret_finish (SecretItem *self,
+                                GAsyncResult *result,
+                                GError **error)
 {
 	GSimpleAsyncResult *res;
-	GetClosure *closure;
 
 	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
-	                      secret_item_get_secret), NULL);
+	                      secret_item_load_secret), FALSE);
 
 	res = G_SIMPLE_ASYNC_RESULT (result);
 	if (g_simple_async_result_propagate_error (res, error))
-		return NULL;
+		return FALSE;
 
-	closure = g_simple_async_result_get_op_res_gpointer (res);
-	return closure->value ? secret_value_ref (closure->value) : NULL;
+	return TRUE;
 }
 
 /**
- * secret_item_get_secret_sync:
+ * secret_item_load_secret_sync:
  * @self: an item
  * @cancellable: optional cancellation object
  * @error: location to place error on failure
  *
- * Get the secret value of this item.
+ * Load the secret value of this item.
  *
  * Each item has a single secret which might be a password or some
- * other secret binary value.
+ * other secret binary value. You can load the secret value on creation of
+ * a secret item proxy by passing the %SECRET_ITEM_LOAD_SECRET flag to the
+ * secret_item_new_sync() method.
  *
  * This function may block indefinetely. Use the asynchronous version
  * in user interface threads.
  *
- * Returns: (transfer full): the newly allocated secret value in this
- *          item, which should be released with secret_value_unref()
+ * Returns: whether the secret item succesfully loaded or not
  */
-SecretValue *
-secret_item_get_secret_sync (SecretItem *self,
-                             GCancellable *cancellable,
-                             GError **error)
+gboolean
+secret_item_load_secret_sync (SecretItem *self,
+                              GCancellable *cancellable,
+                              GError **error)
 {
 	SecretSync *sync;
-	SecretValue *value;
+	gboolean result;
 
 	g_return_val_if_fail (SECRET_IS_ITEM (self), FALSE);
 	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
@@ -1130,16 +1343,16 @@ secret_item_get_secret_sync (SecretItem *self,
 	sync = _secret_sync_new ();
 	g_main_context_push_thread_default (sync->context);
 
-	secret_item_get_secret (self, cancellable, _secret_sync_on_result, sync);
+	secret_item_load_secret (self, cancellable, _secret_sync_on_result, sync);
 
 	g_main_loop_run (sync->loop);
 
-	value = secret_item_get_secret_finish (self, sync->result, error);
+	result = secret_item_load_secret_finish (self, sync->result, error);
 
 	g_main_context_pop_thread_default (sync->context);
 	_secret_sync_free (sync);
 
-	return value;
+	return result;
 }
 
 typedef struct {
@@ -1150,10 +1363,10 @@ typedef struct {
 static void
 set_closure_free (gpointer data)
 {
-	GetClosure *closure = data;
-	g_clear_object (&closure->cancellable);
-	secret_value_unref (closure->value);
-	g_slice_free (GetClosure, closure);
+	SetClosure *set = data;
+	g_clear_object (&set->cancellable);
+	secret_value_unref (set->value);
+	g_slice_free (SetClosure, set);
 }
 
 static void
@@ -1162,17 +1375,22 @@ on_item_set_secret (GObject *source,
                     gpointer user_data)
 {
 	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+	SecretItem *self = SECRET_ITEM (g_async_result_get_source_object (user_data));
+	SetClosure *set = g_simple_async_result_get_op_res_gpointer (res);
 	GError *error = NULL;
 	GVariant *retval;
 
 	retval = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), result, &error);
 
-	if (error != NULL)
+	if (error == NULL)
+		_secret_item_set_cached_secret (self, set->value);
+	else
 		g_simple_async_result_take_error (res, error);
 	if (retval != NULL)
 		g_variant_unref (retval);
 
 	g_simple_async_result_complete (res);
+	g_object_unref (self);
 	g_object_unref (res);
 }
 
diff --git a/library/secret-item.h b/library/secret-item.h
index 90744cc..1733f39 100644
--- a/library/secret-item.h
+++ b/library/secret-item.h
@@ -27,6 +27,11 @@
 
 G_BEGIN_DECLS
 
+typedef enum {
+	SECRET_ITEM_NONE,
+	SECRET_ITEM_LOAD_SECRET = 1 << 1
+} SecretItemFlags;
+
 #define SECRET_TYPE_ITEM            (secret_item_get_type ())
 #define SECRET_ITEM(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), SECRET_TYPE_ITEM, SecretItem))
 #define SECRET_ITEM_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), SECRET_TYPE_ITEM, SecretItemClass))
@@ -55,6 +60,7 @@ GType               secret_item_get_type                   (void) G_GNUC_CONST;
 
 void                secret_item_new                        (SecretService *service,
                                                             const gchar *item_path,
+                                                            SecretItemFlags flags,
                                                             GCancellable *cancellable,
                                                             GAsyncReadyCallback callback,
                                                             gpointer user_data);
@@ -64,6 +70,7 @@ SecretItem *        secret_item_new_finish                 (GAsyncResult *result
 
 SecretItem *        secret_item_new_sync                   (SecretService *service,
                                                             const gchar *item_path,
+                                                            SecretItemFlags flags,
                                                             GCancellable *cancellable,
                                                             GError **error);
 
@@ -102,18 +109,22 @@ gboolean            secret_item_delete_sync                (SecretItem *self,
                                                             GCancellable *cancellable,
                                                             GError **error);
 
+SecretItemFlags     secret_item_get_flags                  (SecretItem *self);
+
 SecretService *     secret_item_get_service                (SecretItem *self);
 
-void                secret_item_get_secret                 (SecretItem *self,
+SecretValue *       secret_item_get_secret                 (SecretItem *self);
+
+void                secret_item_load_secret                (SecretItem *self,
                                                             GCancellable *cancellable,
                                                             GAsyncReadyCallback callback,
                                                             gpointer user_data);
 
-SecretValue *       secret_item_get_secret_finish          (SecretItem *self,
+gboolean            secret_item_load_secret_finish         (SecretItem *self,
                                                             GAsyncResult *result,
                                                             GError **error);
 
-SecretValue *       secret_item_get_secret_sync            (SecretItem *self,
+gboolean            secret_item_load_secret_sync           (SecretItem *self,
                                                             GCancellable *cancellable,
                                                             GError **error);
 
diff --git a/library/secret-methods.c b/library/secret-methods.c
index b380939..6842384 100644
--- a/library/secret-methods.c
+++ b/library/secret-methods.c
@@ -286,7 +286,7 @@ search_load_item_async (SecretService *self,
 
 	item = _secret_service_find_item_instance (self, path);
 	if (item == NULL) {
-		secret_item_new (self, path, closure->cancellable,
+		secret_item_new (self, path, SECRET_ITEM_NONE, closure->cancellable,
 		                  on_search_loaded, g_object_ref (res));
 		closure->loading++;
 	} else {
@@ -448,7 +448,8 @@ service_load_items_sync (SecretService *self,
 	for (i = 0; paths[i] != NULL; i++) {
 		item = _secret_service_find_item_instance (self, paths[i]);
 		if (item == NULL)
-			item = secret_item_new_sync (self, paths[i], cancellable, error);
+			item = secret_item_new_sync (self, paths[i], SECRET_ITEM_NONE,
+			                             cancellable, error);
 		if (item == NULL) {
 			g_list_free_full (result, g_object_unref);
 			return FALSE;
diff --git a/library/secret-private.h b/library/secret-private.h
index 6afb92e..e6a86d7 100644
--- a/library/secret-private.h
+++ b/library/secret-private.h
@@ -166,6 +166,9 @@ GVariant *           _secret_session_encode_secret            (SecretSession *se
 SecretValue *        _secret_session_decode_secret            (SecretSession *session,
                                                                GVariant *encoded);
 
+void                 _secret_item_set_cached_secret           (SecretItem *self,
+                                                               SecretValue *value);
+
 const SecretSchema * _secret_schema_ref_if_nonstatic          (const SecretSchema *schema);
 
 void                 _secret_schema_unref_if_nonstatic        (const SecretSchema *schema);
diff --git a/library/tests/test-item.c b/library/tests/test-item.c
index da71f7e..a627cd9 100644
--- a/library/tests/test-item.c
+++ b/library/tests/test-item.c
@@ -89,7 +89,7 @@ test_new_sync (Test *test,
 	GError *error = NULL;
 	SecretItem *item;
 
-	item = secret_item_new_sync (test->service, item_path, NULL, &error);
+	item = secret_item_new_sync (test->service, item_path, SECRET_ITEM_NONE, NULL, &error);
 	g_assert_no_error (error);
 
 	g_assert_cmpstr (g_dbus_proxy_get_object_path (G_DBUS_PROXY (item)), ==, item_path);
@@ -105,7 +105,7 @@ test_new_sync_noexist (Test *test,
 	GError *error = NULL;
 	SecretItem *item;
 
-	item = secret_item_new_sync (test->service, item_path, NULL, &error);
+	item = secret_item_new_sync (test->service, item_path, SECRET_ITEM_NONE, NULL, &error);
 	g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD);
 	g_assert (item == NULL);
 }
@@ -119,7 +119,8 @@ test_new_async (Test *test,
 	GError *error = NULL;
 	SecretItem *item;
 
-	secret_item_new (test->service, item_path, NULL, on_async_result, &result);
+	secret_item_new (test->service, item_path, SECRET_ITEM_NONE,
+	                 NULL, on_async_result, &result);
 	g_assert (result == NULL);
 
 	egg_test_wait ();
@@ -142,7 +143,8 @@ test_new_async_noexist (Test *test,
 	GError *error = NULL;
 	SecretItem *item;
 
-	secret_item_new (test->service, item_path, NULL, on_async_result, &result);
+	secret_item_new (test->service, item_path, SECRET_ITEM_NONE,
+	                 NULL, on_async_result, &result);
 	g_assert (result == NULL);
 
 	egg_test_wait ();
@@ -248,7 +250,7 @@ test_properties (Test *test,
 	gboolean locked;
 	gchar *label;
 
-	item = secret_item_new_sync (test->service, item_path, NULL, &error);
+	item = secret_item_new_sync (test->service, item_path, SECRET_ITEM_NONE, NULL, &error);
 	g_assert_no_error (error);
 
 	g_assert (secret_item_get_locked (item) == FALSE);
@@ -304,7 +306,7 @@ test_set_label_sync (Test *test,
 	gboolean ret;
 	gchar *label;
 
-	item = secret_item_new_sync (test->service, item_path, NULL, &error);
+	item = secret_item_new_sync (test->service, item_path, SECRET_ITEM_NONE, NULL, &error);
 	g_assert_no_error (error);
 
 	label = secret_item_get_label (item);
@@ -333,7 +335,7 @@ test_set_label_async (Test *test,
 	gboolean ret;
 	gchar *label;
 
-	item = secret_item_new_sync (test->service, item_path, NULL, &error);
+	item = secret_item_new_sync (test->service, item_path, SECRET_ITEM_NONE, NULL, &error);
 	g_assert_no_error (error);
 
 	label = secret_item_get_label (item);
@@ -367,7 +369,7 @@ test_set_label_prop (Test *test,
 	guint sigs = 2;
 	gchar *label;
 
-	item = secret_item_new_sync (test->service, item_path, NULL, &error);
+	item = secret_item_new_sync (test->service, item_path, SECRET_ITEM_NONE, NULL, &error);
 	g_assert_no_error (error);
 
 	label = secret_item_get_label (item);
@@ -397,7 +399,7 @@ test_set_attributes_sync (Test *test,
 	gboolean ret;
 	GHashTable *attributes;
 
-	item = secret_item_new_sync (test->service, item_path, NULL, &error);
+	item = secret_item_new_sync (test->service, item_path, SECRET_ITEM_NONE, NULL, &error);
 	g_assert_no_error (error);
 
 	attributes = secret_item_get_attributes (item);
@@ -435,7 +437,7 @@ test_set_attributes_async (Test *test,
 	SecretItem *item;
 	gboolean ret;
 
-	item = secret_item_new_sync (test->service, item_path, NULL, &error);
+	item = secret_item_new_sync (test->service, item_path, SECRET_ITEM_NONE, NULL, &error);
 	g_assert_no_error (error);
 
 	attributes = secret_item_get_attributes (item);
@@ -477,7 +479,7 @@ test_set_attributes_prop (Test *test,
 	GHashTable *attributes;
 	guint sigs = 2;
 
-	item = secret_item_new_sync (test->service, item_path, NULL, &error);
+	item = secret_item_new_sync (test->service, item_path, SECRET_ITEM_NONE, NULL, &error);
 	g_assert_no_error (error);
 
 	attributes = secret_item_get_attributes (item);
@@ -508,21 +510,28 @@ test_set_attributes_prop (Test *test,
 }
 
 static void
-test_get_secret_sync (Test *test,
-                      gconstpointer unused)
+test_load_secret_sync (Test *test,
+                       gconstpointer unused)
 {
 	const gchar *item_path = "/org/freedesktop/secrets/collection/english/1";
 	GError *error = NULL;
 	SecretItem *item;
 	SecretValue *value;
 	gconstpointer data;
+	gboolean ret;
 	gsize length;
 
-	item = secret_item_new_sync (test->service, item_path, NULL, &error);
+	item = secret_item_new_sync (test->service, item_path, SECRET_ITEM_NONE, NULL, &error);
 	g_assert_no_error (error);
 
-	value = secret_item_get_secret_sync (item, NULL, &error);
+	value = secret_item_get_secret (item);
+	g_assert (value == NULL);
+
+	ret = secret_item_load_secret_sync (item, NULL, &error);
 	g_assert_no_error (error);
+	g_assert (ret == TRUE);
+
+	value = secret_item_get_secret (item);
 	g_assert (value != NULL);
 
 	data = secret_value_get (value, &length);
@@ -534,8 +543,8 @@ test_get_secret_sync (Test *test,
 }
 
 static void
-test_get_secret_async (Test *test,
-                       gconstpointer unused)
+test_load_secret_async (Test *test,
+                        gconstpointer unused)
 {
 	const gchar *item_path = "/org/freedesktop/secrets/collection/english/1";
 	GAsyncResult *result = NULL;
@@ -543,21 +552,28 @@ test_get_secret_async (Test *test,
 	SecretItem *item;
 	SecretValue *value;
 	gconstpointer data;
+	gboolean ret;
 	gsize length;
 
-	item = secret_item_new_sync (test->service, item_path, NULL, &error);
+	item = secret_item_new_sync (test->service, item_path, SECRET_ITEM_NONE, NULL, &error);
 	g_assert_no_error (error);
 
-	secret_item_get_secret (item, NULL, on_async_result, &result);
+	value = secret_item_get_secret (item);
+	g_assert (value == NULL);
+
+	secret_item_load_secret (item, NULL, on_async_result, &result);
 	g_assert (result == NULL);
 
 	egg_test_wait ();
 
-	value = secret_item_get_secret_finish (item, result, &error);
+	ret = secret_item_load_secret_finish (item, result, &error);
 	g_assert_no_error (error);
-	g_assert (value != NULL);
+	g_assert (ret == TRUE);
 	g_object_unref (result);
 
+	value = secret_item_get_secret (item);
+	g_assert (value != NULL);
+
 	data = secret_value_get (value, &length);
 	egg_assert_cmpmem (data, length, ==, "111", 3);
 
@@ -575,22 +591,29 @@ test_set_secret_sync (Test *test,
 	SecretItem *item;
 	gconstpointer data;
 	SecretValue *value;
-	gsize length;
+	SecretValue *check;
 	gboolean ret;
+	gsize length;
 
 	value = secret_value_new ("Sinking", -1, "strange/content-type");
 
-	item = secret_item_new_sync (test->service, item_path, NULL, &error);
+	item = secret_item_new_sync (test->service, item_path, SECRET_ITEM_NONE, NULL, &error);
 	g_assert_no_error (error);
 
 	ret = secret_item_set_secret_sync (item, value, NULL, &error);
 	g_assert_no_error (error);
 	g_assert (ret == TRUE);
 
+	check = secret_item_get_secret (item);
+	g_assert (check == value);
+	secret_value_unref (check);
 	secret_value_unref (value);
 
-	value = secret_item_get_secret_sync (item, NULL, &error);
+	ret = secret_item_load_secret_sync (item, NULL, &error);
 	g_assert_no_error (error);
+	g_assert (ret == TRUE);
+
+	value = secret_item_get_secret (item);
 	g_assert (value != NULL);
 
 	data = secret_value_get (value, &length);
@@ -610,7 +633,7 @@ test_delete_sync (Test *test,
 	SecretItem *item;
 	gboolean ret;
 
-	item = secret_item_new_sync (test->service, item_path, NULL, &error);
+	item = secret_item_new_sync (test->service, item_path, SECRET_ITEM_NONE, NULL, &error);
 	g_assert_no_error (error);
 
 	ret = secret_item_delete_sync (item, NULL, &error);
@@ -619,7 +642,7 @@ test_delete_sync (Test *test,
 
 	g_object_unref (item);
 
-	item = secret_item_new_sync (test->service, item_path, NULL, &error);
+	item = secret_item_new_sync (test->service, item_path, SECRET_ITEM_NONE, NULL, &error);
 	g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD);
 	g_assert (item == NULL);
 }
@@ -634,7 +657,7 @@ test_delete_async (Test *test,
 	SecretItem *item;
 	gboolean ret;
 
-	item = secret_item_new_sync (test->service, item_path, NULL, &error);
+	item = secret_item_new_sync (test->service, item_path, SECRET_ITEM_NONE, NULL, &error);
 	g_assert_no_error (error);
 
 	secret_item_delete (item, NULL, on_async_result, &result);
@@ -648,7 +671,7 @@ test_delete_async (Test *test,
 
 	g_object_unref (item);
 
-	item = secret_item_new_sync (test->service, item_path, NULL, &error);
+	item = secret_item_new_sync (test->service, item_path, SECRET_ITEM_NONE, NULL, &error);
 	g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD);
 	g_assert (item == NULL);
 }
@@ -673,8 +696,8 @@ main (int argc, char **argv)
 	g_test_add ("/item/set-attributes-sync", Test, "mock-service-normal.py", setup, test_set_attributes_sync, teardown);
 	g_test_add ("/item/set-attributes-async", Test, "mock-service-normal.py", setup, test_set_attributes_async, teardown);
 	g_test_add ("/item/set-attributes-prop", Test, "mock-service-normal.py", setup, test_set_attributes_prop, teardown);
-	g_test_add ("/item/get-secret-sync", Test, "mock-service-normal.py", setup, test_get_secret_sync, teardown);
-	g_test_add ("/item/get-secret-async", Test, "mock-service-normal.py", setup, test_get_secret_async, teardown);
+	g_test_add ("/item/load-secret-sync", Test, "mock-service-normal.py", setup, test_load_secret_sync, teardown);
+	g_test_add ("/item/load-secret-async", Test, "mock-service-normal.py", setup, test_load_secret_async, teardown);
 	g_test_add ("/item/set-secret-sync", Test, "mock-service-normal.py", setup, test_set_secret_sync, teardown);
 	g_test_add ("/item/delete-sync", Test, "mock-service-normal.py", setup, test_delete_sync, teardown);
 	g_test_add ("/item/delete-async", Test, "mock-service-normal.py", setup, test_delete_async, teardown);
diff --git a/library/tests/test-methods.c b/library/tests/test-methods.c
index c0d049c..f0f6665 100644
--- a/library/tests/test-methods.c
+++ b/library/tests/test-methods.c
@@ -561,9 +561,9 @@ test_secrets_sync (Test *test,
 	GList *items = NULL;
 	gsize length;
 
-	item_one = secret_item_new_sync (test->service, path_item_one, NULL, &error);
-	item_two = secret_item_new_sync (test->service, path_item_two, NULL, &error);
-	item_three = secret_item_new_sync (test->service, path_item_three, NULL, &error);
+	item_one = secret_item_new_sync (test->service, path_item_one, SECRET_ITEM_NONE, NULL, &error);
+	item_two = secret_item_new_sync (test->service, path_item_two, SECRET_ITEM_NONE, NULL, &error);
+	item_three = secret_item_new_sync (test->service, path_item_three, SECRET_ITEM_NONE, NULL, &error);
 
 	items = g_list_append (items, item_one);
 	items = g_list_append (items, item_two);
@@ -608,13 +608,13 @@ test_secrets_async (Test *test,
 	GList *items = NULL;
 	gsize length;
 
-	item_one = secret_item_new_sync (test->service, path_item_one, NULL, &error);
+	item_one = secret_item_new_sync (test->service, path_item_one, SECRET_ITEM_NONE, NULL, &error);
 	g_assert_no_error (error);
 
-	item_two = secret_item_new_sync (test->service, path_item_two, NULL, &error);
+	item_two = secret_item_new_sync (test->service, path_item_two, SECRET_ITEM_NONE, NULL, &error);
 	g_assert_no_error (error);
 
-	item_three = secret_item_new_sync (test->service, path_item_three, NULL, &error);
+	item_three = secret_item_new_sync (test->service, path_item_three, SECRET_ITEM_NONE, NULL, &error);
 	g_assert_no_error (error);
 
 



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