[gnome-keyring/dbus-api] [secret-store] Implement creation and deletion of collection.



commit 9c2e4fc16101db49fe9e4cd4e76562383174edcf
Author: Stef Walter <stef memberwebs com>
Date:   Sun Nov 8 02:03:52 2009 +0000

    [secret-store] Implement creation and deletion of collection.
    
    Implement creation, storing and removal of token collections and
    items. Creating a collection with a master password is not yet
    supported.

 pkcs11/secret-store/gck-secret-binary.c            |    2 +-
 pkcs11/secret-store/gck-secret-collection.c        |  330 ++++++++++++++++++--
 pkcs11/secret-store/gck-secret-collection.h        |   26 ++-
 pkcs11/secret-store/gck-secret-item.c              |   58 ++++
 pkcs11/secret-store/gck-secret-item.h              |    4 +
 pkcs11/secret-store/gck-secret-module.c            |  211 ++++++++++++-
 pkcs11/secret-store/gck-secret-object.c            |   67 ++++-
 pkcs11/secret-store/gck-secret-object.h            |    8 +-
 pkcs11/secret-store/gck-secret-search.c            |   84 ++----
 pkcs11/secret-store/gck-secret-textual.c           |    9 +-
 pkcs11/secret-store/tests/test-secret-module.c     |   47 +++-
 .../tests/unit-test-secret-collection.c            |  208 ++++++++++++
 pkcs11/secret-store/tests/unit-test-secret-item.c  |  116 ++++++-
 .../secret-store/tests/unit-test-secret-search.c   |    4 +-
 14 files changed, 1025 insertions(+), 149 deletions(-)
---
diff --git a/pkcs11/secret-store/gck-secret-binary.c b/pkcs11/secret-store/gck-secret-binary.c
index f21f236..bf4474e 100644
--- a/pkcs11/secret-store/gck-secret-binary.c
+++ b/pkcs11/secret-store/gck-secret-binary.c
@@ -956,7 +956,7 @@ gck_secret_binary_read (GckSecretCollection *collection, GckSecretData *sdata,
 		
 		item = gck_secret_collection_get_item (collection, items[i].identifier);
 		if (item == NULL)
-			item = gck_secret_collection_create_item (collection, items[i].identifier);
+			item = gck_secret_collection_new_item (collection, items[i].identifier);
 		
 		setup_item_from_info (item, sdata, &items[i]);
 	}
diff --git a/pkcs11/secret-store/gck-secret-collection.c b/pkcs11/secret-store/gck-secret-collection.c
index cb78d97..363f727 100644
--- a/pkcs11/secret-store/gck-secret-collection.c
+++ b/pkcs11/secret-store/gck-secret-collection.c
@@ -31,6 +31,7 @@
 #include "gck/gck-authenticator.h"
 #include "gck/gck-secret.h"
 #include "gck/gck-session.h"
+#include "gck/gck-transaction.h"
 
 #include <glib/gi18n.h>
 
@@ -46,10 +47,15 @@ struct _GckSecretCollection {
 	GckSecretData *sdata;
 	GHashTable *items;
 	gchar *filename;
+	guint32 watermark;
 };
 
 G_DEFINE_TYPE (GckSecretCollection, gck_secret_collection, GCK_TYPE_SECRET_OBJECT);
 
+/* Forward declarations */
+static void add_item (GckSecretCollection *, GckTransaction *, GckSecretItem *);
+static void remove_item (GckSecretCollection *, GckTransaction *, GckSecretItem *);
+
 /* -----------------------------------------------------------------------------
  * INTERNAL
  */
@@ -128,6 +134,141 @@ expose_each_item (gpointer key, gpointer value, gpointer user_data)
 	gck_object_expose (value, expose);
 }
 
+static gboolean
+complete_add (GckTransaction *transaction, GckSecretCollection *self, GckSecretItem *item)
+{
+	if (gck_transaction_get_failed (transaction))
+		remove_item (self, NULL, item);
+	g_object_unref (item);
+	return TRUE;
+}
+
+static void
+add_item (GckSecretCollection *self, GckTransaction *transaction, GckSecretItem *item)
+{
+	const gchar *identifier;
+	guint32 number;
+
+	g_assert (GCK_IS_SECRET_COLLECTION (self));
+	g_assert (GCK_IS_SECRET_ITEM (item));
+
+	identifier = gck_secret_object_get_identifier (GCK_SECRET_OBJECT (item));
+	g_return_if_fail (identifier);
+
+	/* Make note of the highest numeric identifier, for later use */
+	number = strtoul (identifier, NULL, 10);
+	if (number > self->watermark)
+		self->watermark = number;
+
+	g_hash_table_replace (self->items, g_strdup (identifier), g_object_ref (item));
+
+	if (gck_object_is_exposed (GCK_OBJECT (self)))
+		gck_object_expose_full (GCK_OBJECT (item), transaction, TRUE);
+	if (transaction)
+		gck_transaction_add (transaction, self, (GckTransactionFunc)complete_add,
+		                     g_object_ref (item));
+
+}
+
+static gboolean
+complete_remove (GckTransaction *transaction, GckSecretCollection *self, GckSecretItem *item)
+{
+	if (gck_transaction_get_failed (transaction))
+		add_item (self, NULL, item);
+	g_object_unref (item);
+	return TRUE;
+}
+
+static void
+remove_item (GckSecretCollection *self, GckTransaction *transaction, GckSecretItem *item)
+{
+	const gchar *identifier;
+
+	g_assert (GCK_IS_SECRET_COLLECTION (self));
+	g_assert (GCK_IS_SECRET_ITEM (item));
+
+	identifier = gck_secret_object_get_identifier (GCK_SECRET_OBJECT (item));
+	g_return_if_fail (identifier);
+
+	g_object_ref (item);
+
+	g_hash_table_remove (self->items, identifier);
+
+	gck_object_expose_full (GCK_OBJECT (item), transaction, FALSE);
+	if (transaction)
+		gck_transaction_add (transaction, self, (GckTransactionFunc)complete_remove,
+		                     g_object_ref (item));
+
+	g_object_unref (item);
+}
+
+static void
+factory_create_collection (GckSession *session, GckTransaction *transaction,
+                           CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs, GckObject **result)
+{
+	GckSecretCollection *collection = NULL;
+	CK_ATTRIBUTE *attr;
+	GckManager *manager;
+	gchar *identifier = NULL;
+	gchar *label = NULL;
+	gboolean is_token;
+	GckAuthenticator *auth;
+	CK_RV rv;
+
+	g_return_if_fail (GCK_IS_TRANSACTION (transaction));
+	g_return_if_fail (attrs || !n_attrs);
+	g_return_if_fail (result);
+
+	if (!gck_attributes_find_boolean (attrs, n_attrs, CKA_TOKEN, &is_token))
+		is_token = FALSE;
+	if (is_token)
+		manager = gck_module_get_manager (gck_session_get_module (session));
+	else
+		manager = gck_session_get_manager (session);
+
+	/* See if a collection attribute was specified, not present means all collections */
+	attr = gck_attributes_find (attrs, n_attrs, CKA_LABEL);
+	if (attr != NULL) {
+		rv = gck_attribute_get_string (attr, &label);
+		if (rv != CKR_OK)
+			return gck_transaction_fail (transaction, rv);
+		identifier = g_utf8_strdown (label, -1);
+		g_strdelimit (identifier, ":/\\<>|\t\n\r\v ", '_');
+		gck_attribute_consume (attr);
+	}
+
+	if (!identifier || !identifier[0]) {
+		g_free (identifier);
+		identifier = g_strdup ("unnamed");
+	}
+
+	collection = g_object_new (GCK_TYPE_SECRET_COLLECTION,
+	                           "module", gck_session_get_module (session),
+	                           "identifier", identifier,
+	                           "manager", manager,
+	                           "label", label,
+	                           NULL);
+
+	g_free (identifier);
+	g_free (label);
+
+	/*
+	 * HACK: We are expected to have an unlocked collection. This is
+	 * currently a chicken and egg problem, as there's no way to set
+	 * credentials. Actually currently there's no way to set credentials.
+	 */
+	rv = gck_authenticator_create (GCK_OBJECT (collection), gck_session_get_manager (session),
+	                               NULL, 0, &auth);
+	if (rv != CKR_OK) {
+		gck_transaction_fail (transaction, rv);
+		g_object_unref (collection);
+	} else {
+		gck_session_add_session_object (session, transaction, GCK_OBJECT (auth));
+		*result = GCK_OBJECT (collection);
+		g_object_unref (auth);
+	}
+}
+
 /* -----------------------------------------------------------------------------
  * OBJECT
  */
@@ -304,39 +445,32 @@ gck_secret_collection_class_init (GckSecretCollectionClass *klass)
 	g_object_class_install_property (gobject_class, PROP_FILENAME,
 	           g_param_spec_string ("filename", "Filename", "Collection filename (without path)",
 	                                NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	gck_secret_object_class_unique_identifiers (secret_class);
 }
 
-#if 0
-static gboolean
-gck_secret_collection_real_save (GckSerializable *base, GckSecret *login, guchar **data, gsize *n_data)
-{
-	GckSecretCollection *self = GCK_SECRET_COLLECTION (base);
-	GckSecret *master;
-	GckDataResult res;
+/* -----------------------------------------------------------------------------
+ * PUBLIC
+ */
 
-	g_return_val_if_fail (GCK_IS_SECRET_COLLECTION (self), FALSE);
-	g_return_val_if_fail (data, FALSE);
-	g_return_val_if_fail (n_data, FALSE);
+GckFactory*
+gck_secret_collection_get_factory (void)
+{
+	static CK_OBJECT_CLASS klass = CKO_G_COLLECTION;
 
-	if (!self->sdata)
-		g_return_val_if_reached (FALSE);
+	static CK_ATTRIBUTE attributes[] = {
+		{ CKA_CLASS, &klass, sizeof (klass) },
+	};
 
-	master = gck_secret_data_get_master (self->sdata);
-	if (master == NULL)
-		res = gck_secret_textual_write (self, data, n_data);
-	else
-		res = gck_secret_binary_write (self, master, data, n_data);
+	static GckFactory factory = {
+		attributes,
+		G_N_ELEMENTS (attributes),
+		factory_create_collection
+	};
 
-	/* TODO: This doesn't transfer knowledge of 'no password' back up */
-	return (res == GCK_DATA_SUCCESS);
+	return &factory;
 }
 
-#endif
-
-/* -----------------------------------------------------------------------------
- * PUBLIC
- */
-
 const gchar*
 gck_secret_collection_get_filename (GckSecretCollection *self)
 {
@@ -373,8 +507,51 @@ gck_secret_collection_get_item (GckSecretCollection *self, const gchar *identifi
 	return g_hash_table_lookup (self->items, identifier);
 }
 
+gboolean
+gck_secret_collection_has_item (GckSecretCollection *self, GckSecretItem *item)
+{
+	const gchar *identifier;
+
+	g_return_val_if_fail (GCK_IS_SECRET_COLLECTION (self), FALSE);
+	g_return_val_if_fail (GCK_IS_SECRET_ITEM (item), FALSE);
+
+	identifier = gck_secret_object_get_identifier (GCK_SECRET_OBJECT (item));
+	return g_hash_table_lookup (self->items, identifier) == item;
+}
+
+GckSecretCollection*
+gck_secret_collection_find (CK_ATTRIBUTE_PTR attr, ...)
+{
+	CK_OBJECT_CLASS klass = CKO_G_COLLECTION;
+	GckSecretCollection *result = NULL;
+	GckManager *manager;
+	CK_ATTRIBUTE attrs[2];
+	GList *objects;
+	va_list va;
+
+	g_assert (attr);
+
+	attrs[0].type = CKA_CLASS;
+	attrs[0].ulValueLen = sizeof (klass);
+	attrs[0].pValue = &klass;
+	attrs[1].type = CKA_ID;
+	attrs[1].ulValueLen = attr->ulValueLen;
+	attrs[1].pValue = attr->pValue;
+
+	va_start (va, attr);
+	while (!result && (manager = va_arg (va, GckManager*)) != NULL) {
+		objects = gck_manager_find_by_attributes (manager, attrs, 2);
+		if (objects && GCK_IS_SECRET_COLLECTION (objects->data))
+			result = objects->data;
+		g_list_free (objects);
+	}
+	va_end (va);
+
+	return result;
+}
+
 GckSecretItem*
-gck_secret_collection_create_item (GckSecretCollection *self, const gchar *identifier)
+gck_secret_collection_new_item (GckSecretCollection *self, const gchar *identifier)
 {
 	GckSecretItem *item;
 
@@ -389,22 +566,59 @@ gck_secret_collection_create_item (GckSecretCollection *self, const gchar *ident
 	                     "identifier", identifier,
 	                     NULL);
 
-	g_hash_table_replace (self->items, g_strdup (identifier), item);
+	add_item (self, NULL, item);
+	g_object_unref (item);
+	return item;
+}
+
+GckSecretItem*
+gck_secret_collection_create_item (GckSecretCollection *self, GckTransaction *transaction)
+{
+	GckSecretItem *item;
+	gchar *identifier = NULL;
+
+	g_return_val_if_fail (GCK_IS_SECRET_COLLECTION (self), NULL);
+	g_return_val_if_fail (transaction, NULL);
+	g_return_val_if_fail (!gck_transaction_get_failed (transaction), NULL);
+
+	do {
+		g_free (identifier);
+		identifier = g_strdup_printf ("%d", ++(self->watermark));
+	} while (g_hash_table_lookup (self->items, identifier));
+
+	item = g_object_new (GCK_TYPE_SECRET_ITEM,
+	                     "module", gck_object_get_module (GCK_OBJECT (self)),
+	                     "manager", gck_object_get_manager (GCK_OBJECT (self)),
+	                     "collection", self,
+	                     "identifier", identifier,
+	                     NULL);
+
+	g_free (identifier);
+	add_item (self, transaction, item);
+	g_object_unref (item);
 	return item;
 }
 
 void
 gck_secret_collection_remove_item (GckSecretCollection *self, GckSecretItem *item)
 {
-	const gchar *identifier;
-
 	g_return_if_fail (GCK_IS_SECRET_COLLECTION (self));
 	g_return_if_fail (GCK_IS_SECRET_ITEM (item));
+	g_return_if_fail (gck_secret_collection_has_item (self, item));
 
-	identifier = gck_secret_object_get_identifier (GCK_SECRET_OBJECT (item));
-	g_return_if_fail (identifier);
+	remove_item (self, NULL, item);
+}
 
-	g_hash_table_remove (self->items, identifier);
+void
+gck_secret_collection_destroy_item (GckSecretCollection *self, GckTransaction *transaction,
+                                    GckSecretItem *item)
+{
+	g_return_if_fail (GCK_IS_SECRET_COLLECTION (self));
+	g_return_if_fail (GCK_IS_TRANSACTION (transaction));
+	g_return_if_fail (GCK_IS_SECRET_ITEM (item));
+	g_return_if_fail (gck_secret_collection_has_item (self, item));
+
+	remove_item (self, transaction, item);
 }
 
 GckSecretData*
@@ -450,3 +664,55 @@ gck_secret_collection_load (GckSecretCollection *self)
 
 	return load_collection_and_secret_data (self, self->sdata, self->filename);
 }
+
+void
+gck_secret_collection_save (GckSecretCollection *self, GckTransaction *transaction)
+{
+	GckSecret *master;
+	GckDataResult res;
+	guchar *data;
+	gsize n_data;
+
+	g_return_if_fail (GCK_IS_SECRET_COLLECTION (self));
+	g_return_if_fail (GCK_IS_TRANSACTION (transaction));
+	g_return_if_fail (!gck_transaction_get_failed (transaction));
+
+	if (!self->sdata)
+		g_return_if_reached ();
+
+	master = gck_secret_data_get_master (self->sdata);
+	if (master == NULL || gck_secret_equals (master, NULL, 0))
+		res = gck_secret_textual_write (self, self->sdata, &data, &n_data);
+	else
+		res = gck_secret_binary_write (self, self->sdata, &data, &n_data);
+
+	switch (res) {
+	case GCK_DATA_FAILURE:
+	case GCK_DATA_UNRECOGNIZED:
+		g_warning ("couldn't prepare to write out keyring: %s", self->filename);
+		gck_transaction_fail (transaction, CKR_GENERAL_ERROR);
+		break;
+	case GCK_DATA_LOCKED:
+		g_warning ("locked error while writing out keyring: %s", self->filename);
+		gck_transaction_fail (transaction, CKR_GENERAL_ERROR);
+		break;
+	case GCK_DATA_SUCCESS:
+		gck_transaction_write_file (transaction, self->filename, data, n_data);
+		g_free (data);
+		break;
+	default:
+		g_assert_not_reached ();
+	};
+}
+
+void
+gck_secret_collection_destroy (GckSecretCollection *self, GckTransaction *transaction)
+{
+	g_return_if_fail (GCK_IS_SECRET_COLLECTION (self));
+	g_return_if_fail (GCK_IS_TRANSACTION (transaction));
+	g_return_if_fail (!gck_transaction_get_failed (transaction));
+
+	gck_object_expose_full (GCK_OBJECT (self), transaction, FALSE);
+	if (self->filename)
+		gck_transaction_remove_file (transaction, self->filename);
+}
diff --git a/pkcs11/secret-store/gck-secret-collection.h b/pkcs11/secret-store/gck-secret-collection.h
index 2f00cc9..98d9cf5 100644
--- a/pkcs11/secret-store/gck-secret-collection.h
+++ b/pkcs11/secret-store/gck-secret-collection.h
@@ -26,6 +26,8 @@
 
 #include "gck-secret-object.h"
 
+#define GCK_FACTORY_SECRET_COLLECTION            (gck_secret_collection_get_factory ())
+
 #define GCK_TYPE_SECRET_COLLECTION               (gck_secret_collection_get_type ())
 #define GCK_SECRET_COLLECTION(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_SECRET_COLLECTION, GckSecretCollection))
 #define GCK_SECRET_COLLECTION_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GCK_TYPE_SECRET_COLLECTION, GckSecretCollectionClass))
@@ -37,12 +39,24 @@ typedef struct _GckSecretCollectionClass GckSecretCollectionClass;
 
 struct _GckSecretCollectionClass {
 	GckSecretObjectClass parent_class;
+	GHashTable *identifiers;
 };
 
 GType                gck_secret_collection_get_type        (void);
 
+GckFactory*          gck_secret_collection_get_factory     (void) G_GNUC_CONST;
+
+GckSecretCollection* gck_secret_collection_find            (CK_ATTRIBUTE_PTR attr,
+                                                            ...) G_GNUC_NULL_TERMINATED;
+
 GckDataResult        gck_secret_collection_load            (GckSecretCollection *self);
 
+void                 gck_secret_collection_save            (GckSecretCollection *self,
+                                                            GckTransaction *transaction);
+
+void                 gck_secret_collection_destroy         (GckSecretCollection *self,
+                                                            GckTransaction *transaction);
+
 const gchar*         gck_secret_collection_get_filename    (GckSecretCollection *self);
 
 void                 gck_secret_collection_set_filename    (GckSecretCollection *self,
@@ -53,12 +67,22 @@ GList*               gck_secret_collection_get_items       (GckSecretCollection
 GckSecretItem*       gck_secret_collection_get_item        (GckSecretCollection *self,
                                                             const gchar *identifier);
 
-GckSecretItem*       gck_secret_collection_create_item     (GckSecretCollection *self,
+gboolean             gck_secret_collection_has_item        (GckSecretCollection *self,
+                                                            GckSecretItem *item);
+
+GckSecretItem*       gck_secret_collection_new_item        (GckSecretCollection *self,
                                                             const gchar *identifier);
 
 void                 gck_secret_collection_remove_item     (GckSecretCollection *self,
                                                             GckSecretItem *item);
 
+GckSecretItem*       gck_secret_collection_create_item     (GckSecretCollection *self,
+                                                            GckTransaction *transaction);
+
+void                 gck_secret_collection_destroy_item    (GckSecretCollection *self,
+                                                            GckTransaction *transaction,
+                                                            GckSecretItem *item);
+
 void                 gck_secret_collection_unlocked_clear  (GckSecretCollection *self);
 
 GckSecretData*       gck_secret_collection_unlocked_data   (GckSecretCollection *self,
diff --git a/pkcs11/secret-store/gck-secret-item.c b/pkcs11/secret-store/gck-secret-item.c
index a40e8c4..558a5ff 100644
--- a/pkcs11/secret-store/gck-secret-item.c
+++ b/pkcs11/secret-store/gck-secret-item.c
@@ -26,7 +26,9 @@
 #include "gck-secret-item.h"
 
 #include "gck/gck-attributes.h"
+#include "gck/gck-module.h"
 #include "gck/gck-secret.h"
+#include "gck/gck-session.h"
 #include "gck/gck-transaction.h"
 
 #include "pkcs11/pkcs11i.h"
@@ -95,6 +97,44 @@ begin_set_fields (GckSecretItem *self, GckTransaction *transaction, GHashTable *
 	self->fields = fields;
 }
 
+static void
+factory_create_item (GckSession *session, GckTransaction *transaction,
+                     CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs, GckObject **result)
+{
+	GckSecretCollection *collection = NULL;
+	GckSecretItem *item;
+	GckManager *manager;
+	CK_ATTRIBUTE *attr;
+	gboolean is_token;
+
+	g_return_if_fail (GCK_IS_TRANSACTION (transaction));
+	g_return_if_fail (attrs || !n_attrs);
+	g_return_if_fail (result);
+
+	if (!gck_attributes_find_boolean (attrs, n_attrs, CKA_TOKEN, &is_token))
+		is_token = FALSE;
+
+	/* See if a collection attribute was specified */
+	attr = gck_attributes_find (attrs, n_attrs, CKA_G_COLLECTION);
+	if (attr != NULL) {
+		gck_attribute_consume (attr);
+		if (is_token)
+			manager = gck_module_get_manager (gck_session_get_module (session));
+		else
+			manager = gck_session_get_manager (session);
+		collection = gck_secret_collection_find (attr, manager, NULL);
+	}
+
+	if (!collection)
+		return gck_transaction_fail (transaction, CKR_TEMPLATE_INCOMPLETE);
+
+	/* The collection owns the item */
+	item = gck_secret_collection_create_item (collection, transaction);
+
+	/* All the other fields are set later ... */
+	*result = g_object_ref (item);
+}
+
 /* -----------------------------------------------------------------------------
  * OBJECT 
  */
@@ -309,6 +349,24 @@ gck_secret_item_class_init (GckSecretItemClass *klass)
  * PUBLIC
  */
 
+GckFactory*
+gck_secret_item_get_factory (void)
+{
+	static CK_OBJECT_CLASS klass = CKO_SECRET_KEY;
+
+	static CK_ATTRIBUTE attributes[] = {
+		{ CKA_CLASS, &klass, sizeof (klass) },
+	};
+
+	static GckFactory factory = {
+		attributes,
+		G_N_ELEMENTS (attributes),
+		factory_create_item
+	};
+
+	return &factory;
+}
+
 GckSecretCollection*
 gck_secret_item_get_collection (GckSecretItem *self)
 {
diff --git a/pkcs11/secret-store/gck-secret-item.h b/pkcs11/secret-store/gck-secret-item.h
index fd3344f..f992cc9 100644
--- a/pkcs11/secret-store/gck-secret-item.h
+++ b/pkcs11/secret-store/gck-secret-item.h
@@ -27,6 +27,8 @@
 #include "gck-secret-object.h"
 #include "gck-secret-collection.h"
 
+#define GCK_FACTORY_SECRET_ITEM             (gck_secret_item_get_factory ())
+
 #define GCK_TYPE_SECRET_ITEM               (gck_secret_item_get_type ())
 #define GCK_SECRET_ITEM(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_SECRET_ITEM, GckSecretItem))
 #define GCK_SECRET_ITEM_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GCK_TYPE_SECRET_ITEM, GckSecretItemClass))
@@ -42,6 +44,8 @@ struct _GckSecretItemClass {
 
 GType                  gck_secret_item_get_type               (void);
 
+GckFactory*            gck_secret_item_get_factory            (void) G_GNUC_CONST;
+
 GckSecretCollection*   gck_secret_item_get_collection         (GckSecretItem *self);
 
 GHashTable*            gck_secret_item_get_fields             (GckSecretItem *self);
diff --git a/pkcs11/secret-store/gck-secret-module.c b/pkcs11/secret-store/gck-secret-module.c
index a986bbf..fdf2178 100644
--- a/pkcs11/secret-store/gck-secret-module.c
+++ b/pkcs11/secret-store/gck-secret-module.c
@@ -22,12 +22,17 @@
 #include "config.h"
 
 #include "gck-secret-collection.h"
+#include "gck-secret-item.h"
 #include "gck-secret-module.h"
 #include "gck-secret-search.h"
 #include "gck-secret-store.h"
 
 #include "gck/gck-file-tracker.h"
+#include "gck/gck-transaction.h"
 
+#include <glib/gstdio.h>
+
+#include <errno.h>
 #include <fcntl.h>
 #include <string.h>
 
@@ -71,6 +76,10 @@ G_DEFINE_TYPE (GckSecretModule, gck_secret_module, GCK_TYPE_MODULE);
 
 GckModule*  _gck_secret_store_get_module_for_testing (void);
 
+/* Forward declarations */
+static void add_collection (GckSecretModule *, GckTransaction *, GckSecretCollection *);
+static void remove_collection (GckSecretModule *, GckTransaction *, GckSecretCollection *);
+
 /* -----------------------------------------------------------------------------
  * ACTUAL PKCS#11 Module Implementation 
  */
@@ -83,6 +92,107 @@ GCK_DEFINE_MODULE (gck_secret_module, GCK_TYPE_SECRET_MODULE);
  * INTERNAL 
  */
 
+static gboolean
+complete_add (GckTransaction *transaction, GObject *obj, gpointer user_data)
+{
+	GckSecretCollection *collection = GCK_SECRET_COLLECTION (user_data);
+	if (gck_transaction_get_failed (transaction))
+		remove_collection (GCK_SECRET_MODULE (obj), NULL, collection);
+	g_object_unref (collection);
+	return TRUE;
+}
+
+static void
+add_collection (GckSecretModule *self, GckTransaction *transaction, GckSecretCollection  *collection)
+{
+	const gchar *identifier;
+
+	g_assert (GCK_IS_SECRET_MODULE(self));
+	g_assert (GCK_IS_SECRET_COLLECTION (collection));
+
+	identifier = gck_secret_object_get_identifier (GCK_SECRET_OBJECT (collection));
+	g_return_if_fail (identifier);
+
+	g_hash_table_replace (self->collections, g_strdup (identifier), g_object_ref (collection));
+
+	gck_object_expose_full (GCK_OBJECT (collection), transaction, TRUE);
+	if (transaction)
+		gck_transaction_add (transaction, self, complete_add, g_object_ref (collection));
+}
+
+static gboolean
+complete_remove (GckTransaction *transaction, GObject *obj, gpointer user_data)
+{
+	GckSecretCollection *collection = GCK_SECRET_COLLECTION (user_data);
+	if (gck_transaction_get_failed (transaction))
+		add_collection (GCK_SECRET_MODULE (obj), NULL, collection);
+	g_object_unref (collection);
+	return TRUE;
+}
+
+static void
+remove_collection (GckSecretModule *self, GckTransaction *transaction, GckSecretCollection *collection)
+{
+	const gchar *identifier;
+
+	g_assert (GCK_IS_SECRET_MODULE (self));
+	g_assert (GCK_IS_SECRET_COLLECTION (collection));
+
+	identifier = gck_secret_object_get_identifier (GCK_SECRET_OBJECT (collection));
+	g_return_if_fail (identifier);
+
+	g_hash_table_remove (self->collections, identifier);
+
+	gck_object_expose_full (GCK_OBJECT (collection), transaction, FALSE);
+	if (transaction)
+		gck_transaction_add (transaction, self, complete_remove, g_object_ref (collection));
+}
+
+static gchar*
+identifier_from_filename (GckSecretModule *self, const gchar *filename)
+{
+	gchar *identifier;
+
+	/* Do we have one for this path yet? */
+	identifier = g_path_get_basename (filename);
+
+	/* Remove the keyring suffix */
+	if (g_str_has_suffix (identifier, ".keyring"))
+		identifier[strlen(identifier) - 8] = 0;
+
+	return identifier;
+}
+
+static gchar*
+identifier_to_new_filename (GckSecretModule *self, const gchar *identifier)
+{
+	gchar *filename;
+	gint i;
+	int fd;
+
+	for (i = 0; i < G_MAXINT; ++i) {
+		if (i == 0)
+			filename = g_strdup_printf ("%s/%s.keyring", self->directory, identifier);
+		else
+			filename = g_strdup_printf ("%s/%s_%d.keyring", self->directory, identifier, i);
+
+		/* Try to create the file, and check that it doesn't exist */
+		fd = g_open (filename, O_RDONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+		if (fd == -1) {
+			if (errno != EEXIST)
+				break;
+		} else {
+			close (fd);
+			break;
+		}
+
+		g_free (filename);
+	}
+
+	return filename;
+}
+
+
 static void
 on_file_load (GckFileTracker *tracker, const gchar *path, GckSecretModule *self)
 {
@@ -90,20 +200,20 @@ on_file_load (GckFileTracker *tracker, const gchar *path, GckSecretModule *self)
 	GckManager *manager;
 	GckDataResult res;
 	gboolean created;
-	gchar *basename;
+	gchar *identifier;
 
 	manager = gck_module_get_manager (GCK_MODULE (self));
 	g_return_if_fail (manager);
 
 	/* Do we have one for this path yet? */
-	basename = g_path_get_basename (path);
-	collection = g_hash_table_lookup (self->collections, basename);
+	identifier = identifier_from_filename (self, path);
+	collection = g_hash_table_lookup (self->collections, path);
 
 	if (collection == NULL) {
 		created = TRUE;
 		collection = g_object_new (GCK_TYPE_SECRET_COLLECTION,
 		                           "module", self,
-		                           "identifier", basename,
+		                           "identifier", identifier,
 		                           "filename", path,
 		                           "manager", manager,
 		                           NULL);
@@ -113,11 +223,8 @@ on_file_load (GckFileTracker *tracker, const gchar *path, GckSecretModule *self)
 
 	switch (res) {
 	case GCK_DATA_SUCCESS:
-		if (created) {
-			g_hash_table_replace (self->collections, basename, collection);
-			gck_object_expose (GCK_OBJECT (collection), TRUE);
-			basename = NULL;
-		}
+		if (created)
+			add_collection (self, NULL, collection);
 		break;
 	case GCK_DATA_LOCKED:
 		g_message ("master password for keyring changed without our knowledge: %s", path);
@@ -133,21 +240,21 @@ on_file_load (GckFileTracker *tracker, const gchar *path, GckSecretModule *self)
 		g_assert_not_reached ();
 	}
 
-	g_free (basename);
+	g_object_unref (collection);
+	g_free (identifier);
 }
 
 static void
 on_file_remove (GckFileTracker *tracker, const gchar *path, GckSecretModule *self)
 {
-	gchar *basename;
+	GckSecretCollection *collection;
 
 	g_return_if_fail (path);
 	g_return_if_fail (GCK_IS_SECRET_MODULE (self));
 
-	basename = g_path_get_basename (path);
-	if (!g_hash_table_remove (self->collections, basename))
-		g_return_if_reached ();
-	g_free (basename);
+	collection = g_hash_table_lookup (self->collections, path);
+	if (collection)
+		remove_collection (self, NULL, collection);
 }
 
 /* -----------------------------------------------------------------------------
@@ -185,6 +292,74 @@ gck_secret_module_real_refresh_token (GckModule *base)
 	return CKR_OK;
 }
 
+static void
+gck_secret_module_real_store_object (GckModule *module, GckTransaction *transaction,
+                                     GckObject *object)
+{
+	GckSecretModule *self = GCK_SECRET_MODULE (module);
+	GckSecretCollection *collection = NULL;
+	const gchar *identifier;
+	gchar *filename;
+
+	/* Adding an item */
+	if (GCK_IS_SECRET_ITEM (object)) {
+		collection = gck_secret_item_get_collection (GCK_SECRET_ITEM (object));
+		g_return_if_fail (GCK_IS_SECRET_COLLECTION (collection));
+
+	/* Adding a collection */
+	} else if (GCK_IS_SECRET_COLLECTION (object)) {
+		collection = GCK_SECRET_COLLECTION (object);
+	}
+
+	/* No other kind of token object */
+	if (collection == NULL) {
+		g_warning ("can't store object of type '%s' on secret token", G_OBJECT_TYPE_NAME (object));
+		gck_transaction_fail (transaction, CKR_GENERAL_ERROR);
+		return;
+	}
+
+	/* Setup a filename for this collection */
+	if (!gck_secret_collection_get_filename (collection)) {
+		identifier = gck_secret_object_get_identifier (GCK_SECRET_OBJECT (collection));
+		filename = identifier_to_new_filename (self, identifier);
+		gck_secret_collection_set_filename (collection, filename);
+		g_free (filename);
+	}
+
+	gck_secret_collection_save (collection, transaction);
+	if (!gck_transaction_get_failed (transaction))
+		add_collection (self, transaction, collection);
+}
+
+static void
+gck_secret_module_real_remove_object (GckModule *module, GckTransaction *transaction,
+                                      GckObject *object)
+{
+	GckSecretModule *self = GCK_SECRET_MODULE (module);
+	GckSecretCollection *collection;
+
+	/* Removing an item */
+	if (GCK_IS_SECRET_ITEM (object)) {
+		collection = gck_secret_item_get_collection (GCK_SECRET_ITEM (object));
+		g_return_if_fail (GCK_IS_SECRET_COLLECTION (collection));
+		gck_secret_collection_destroy_item (collection, transaction, GCK_SECRET_ITEM (object));
+		if (!gck_transaction_get_failed (transaction))
+			gck_secret_collection_save (collection, transaction);
+
+	/* Removing a collection */
+	} else if (GCK_IS_SECRET_COLLECTION (object)) {
+		collection = GCK_SECRET_COLLECTION (object);
+		gck_secret_collection_destroy (collection, transaction);
+		if (!gck_transaction_get_failed (transaction))
+			remove_collection (self, transaction, collection);
+
+	/* No other token objects */
+	} else {
+		gck_transaction_fail (transaction, CKR_FUNCTION_NOT_SUPPORTED);
+		g_return_if_reached ();
+	}
+}
+
 static GObject* 
 gck_secret_module_constructor (GType type, guint n_props, GObjectConstructParam *props) 
 {
@@ -211,6 +386,8 @@ gck_secret_module_init (GckSecretModule *self)
 {
 	self->collections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
 	gck_module_register_factory (GCK_MODULE (self), GCK_FACTORY_SECRET_SEARCH);
+	gck_module_register_factory (GCK_MODULE (self), GCK_FACTORY_SECRET_ITEM);
+	gck_module_register_factory (GCK_MODULE (self), GCK_FACTORY_SECRET_COLLECTION);
 }
 
 static void
@@ -252,11 +429,13 @@ gck_secret_module_class_init (GckSecretModuleClass *klass)
 	gobject_class->constructor = gck_secret_module_constructor;
 	gobject_class->dispose = gck_secret_module_dispose;
 	gobject_class->finalize = gck_secret_module_finalize;
-	
+
 	module_class->get_slot_info = gck_secret_module_real_get_slot_info;
 	module_class->get_token_info = gck_secret_module_real_get_token_info;
 	module_class->parse_argument = gck_secret_module_real_parse_argument;
 	module_class->refresh_token = gck_secret_module_real_refresh_token;
+	module_class->remove_token_object = gck_secret_module_real_remove_object;
+	module_class->store_token_object = gck_secret_module_real_store_object;
 }
 
 /* ---------------------------------------------------------------------------------------
diff --git a/pkcs11/secret-store/gck-secret-object.c b/pkcs11/secret-store/gck-secret-object.c
index 9ca01a9..39783e9 100644
--- a/pkcs11/secret-store/gck-secret-object.c
+++ b/pkcs11/secret-store/gck-secret-object.c
@@ -81,6 +81,45 @@ begin_set_label (GckSecretObject *self, GckTransaction *transaction, gchar *labe
 	self->pv->label = label;
 }
 
+static gchar*
+register_identifier (GckSecretObjectClass *klass, const gchar *identifier)
+{
+	gchar *result;
+	gint i;
+
+	g_assert (klass);
+	g_assert (identifier);
+
+	if (!klass->identifiers)
+		return g_strdup (identifier);
+
+	for (i = 0; i < G_MAXINT; ++i) {
+		if (i == 0)
+			result = g_strdup (identifier);
+		else
+			result = g_strdup_printf ("%s_%d", identifier, i);
+		if (g_hash_table_lookup (klass->identifiers, result)) {
+			g_free (result);
+		} else {
+			g_hash_table_insert (klass->identifiers, result, result);
+			return result;
+		}
+	}
+
+	g_assert_not_reached ();
+}
+
+static void
+unregister_identifier (GckSecretObjectClass *klass, gchar *identifier)
+{
+	g_assert (klass);
+	g_assert (identifier);
+
+	if (klass->identifiers)
+		g_hash_table_remove (klass->identifiers, identifier);
+	g_free (identifier);
+}
+
 /* -----------------------------------------------------------------------------
  * OBJECT 
  */
@@ -168,16 +207,19 @@ static void
 gck_secret_object_set_property (GObject *obj, guint prop_id, const GValue *value, 
                                 GParamSpec *pspec)
 {
+	GckSecretObjectClass *klass = GCK_SECRET_OBJECT_GET_CLASS (obj);
 	GckSecretObject *self = GCK_SECRET_OBJECT (obj);
-	
+	const gchar *identifier;
+
 	switch (prop_id) {
 	case PROP_LABEL:
 		gck_secret_object_set_label (self, g_value_get_string (value));
 		break;
 	case PROP_IDENTIFIER:
 		g_return_if_fail (!self->pv->identifier);
-		self->pv->identifier = g_value_dup_string (value);
-		g_return_if_fail (self->pv->identifier);
+		identifier = g_value_get_string (value);
+		g_return_if_fail (identifier);
+		self->pv->identifier = register_identifier (klass, identifier);
 		break;
 	case PROP_CREATED:
 		gck_secret_object_set_created (self, g_value_get_long (value));
@@ -217,18 +259,13 @@ gck_secret_object_get_property (GObject *obj, guint prop_id, GValue *value,
 }
 
 static void
-gck_secret_object_dispose (GObject *obj)
-{
-	/* GckSecretObject *self = GCK_SECRET_OBJECT (obj); */
-	G_OBJECT_CLASS (gck_secret_object_parent_class)->dispose (obj);
-}
-
-static void
 gck_secret_object_finalize (GObject *obj)
 {
+	GckSecretObjectClass *klass = GCK_SECRET_OBJECT_GET_CLASS (obj);
 	GckSecretObject *self = GCK_SECRET_OBJECT (obj);
 
-	g_free (self->pv->identifier);
+	if (self->pv->identifier)
+		unregister_identifier (klass, self->pv->identifier);
 	self->pv->identifier = NULL;
 	
 	g_free (self->pv->label);
@@ -250,7 +287,6 @@ gck_secret_object_class_init (GckSecretObjectClass *klass)
 	g_type_class_add_private (klass, sizeof (GckSecretObjectPrivate));
 
 	gobject_class->constructor = gck_secret_object_constructor;
-	gobject_class->dispose = gck_secret_object_dispose;
 	gobject_class->finalize = gck_secret_object_finalize;
 	gobject_class->set_property = gck_secret_object_set_property;
 	gobject_class->get_property = gck_secret_object_get_property;
@@ -281,6 +317,13 @@ gck_secret_object_class_init (GckSecretObjectClass *klass)
  * PUBLIC 
  */
 
+void
+gck_secret_object_class_unique_identifiers (GckSecretObjectClass *klass)
+{
+	if (!klass->identifiers)
+		klass->identifiers = g_hash_table_new (g_str_hash, g_str_equal);
+}
+
 const gchar*
 gck_secret_object_get_identifier (GckSecretObject *self)
 {
diff --git a/pkcs11/secret-store/gck-secret-object.h b/pkcs11/secret-store/gck-secret-object.h
index 106961e..b2b905f 100644
--- a/pkcs11/secret-store/gck-secret-object.h
+++ b/pkcs11/secret-store/gck-secret-object.h
@@ -44,8 +44,10 @@ struct _GckSecretObject {
 };
 struct _GckSecretObjectClass {
 	GckObjectClass parent_class;
-	
+	GHashTable *identifiers;
+
 	gboolean (*is_locked) (GckSecretObject *self, GckSession *session);
+
 };
 
 GType                gck_secret_object_get_type        (void);
@@ -72,4 +74,8 @@ void                 gck_secret_object_was_modified    (GckSecretObject *self);
 gboolean             gck_secret_object_is_locked       (GckSecretObject *self,
                                                         GckSession *session);
 
+void       gck_secret_object_class_unique_identifiers  (GckSecretObjectClass *klass);
+
+gchar*     gck_secret_object_anonymous_identifier      (void);
+
 #endif /* __GCK_SECRET_OBJECT_H__ */
diff --git a/pkcs11/secret-store/gck-secret-search.c b/pkcs11/secret-store/gck-secret-search.c
index 4fe7563..d177520 100644
--- a/pkcs11/secret-store/gck-secret-search.c
+++ b/pkcs11/secret-store/gck-secret-search.c
@@ -159,63 +159,26 @@ on_manager_gone_away (gpointer user_data, GObject *where_the_object_was)
 	self->managers = g_list_delete_link (self->managers, l);
 }
 
-static GckSecretCollection*
-find_collection_in_managers (GList *managers, CK_ATTRIBUTE_PTR attr)
-{
-	CK_OBJECT_CLASS klass = CKO_G_COLLECTION;
-	GckSecretCollection *result = NULL;
-	CK_ATTRIBUTE attrs[2];
-	GList *objects, *l;
-
-	g_assert (attr);
-
-	attrs[0].type = CKA_CLASS;
-	attrs[0].ulValueLen = sizeof (klass);
-	attrs[0].pValue = &klass;
-	attrs[1].type = CKA_ID;
-	attrs[1].ulValueLen = attr->ulValueLen;
-	attrs[1].pValue = attr->pValue;
-
-	for (l = managers; !result && l; l = g_list_next (l)) {
-		objects = gck_manager_find_by_attributes (l->data, attrs, 2);
-		if (objects && GCK_IS_SECRET_COLLECTION (objects->data))
-			result = objects->data;
-		g_list_free (objects);
-	}
-
-	return result;
-}
-
 static void
-populate_search_from_managers (GckSecretSearch *self, GList *managers)
+populate_search_from_manager (GckSecretSearch *self, GckManager *manager)
 {
 	GList *objects, *o;
-	GckManager *manager;
-	GList *l;
 
-	g_assert (!self->managers);
-	self->managers = managers;
-
-	/* Load any new items or collections */
-	gck_module_refresh_token (gck_object_get_module (GCK_OBJECT (self)));
-
-	for (l = self->managers; l; l = g_list_next (l)) {
-		manager = GCK_MANAGER (l->data);
+	self->managers = g_list_append (self->managers, manager);
 
-		/* Add in all the objects */
-		objects = gck_manager_find_by_class (manager, CKO_SECRET_KEY);
-		for (o = objects; o; o = g_list_next (o))
-			on_manager_added_object (manager, o->data, self);
-		g_list_free (objects);
+	/* Add in all the objects */
+	objects = gck_manager_find_by_class (manager, CKO_SECRET_KEY);
+	for (o = objects; o; o = g_list_next (o))
+		on_manager_added_object (manager, o->data, self);
+	g_list_free (objects);
 
-		/* Track this manager */
-		g_object_weak_ref (G_OBJECT (manager), on_manager_gone_away, self);
+	/* Track this manager */
+	g_object_weak_ref (G_OBJECT (manager), on_manager_gone_away, self);
 
-		/* Watch for further events of objects */
-		g_signal_connect (manager, "object-added", G_CALLBACK (on_manager_added_object), self);
-		g_signal_connect (manager, "object-removed", G_CALLBACK (on_manager_removed_object), self);
-		g_signal_connect (manager, "attribute-changed", G_CALLBACK (on_manager_changed_object), self);
-	}
+	/* Watch for further events of objects */
+	g_signal_connect (manager, "object-added", G_CALLBACK (on_manager_added_object), self);
+	g_signal_connect (manager, "object-removed", G_CALLBACK (on_manager_removed_object), self);
+	g_signal_connect (manager, "attribute-changed", G_CALLBACK (on_manager_changed_object), self);
 }
 
 static void
@@ -223,11 +186,10 @@ factory_create_search (GckSession *session, GckTransaction *transaction,
                        CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs, GckObject **result)
 {
 	GckSecretCollection *collection = NULL;
+	GckManager *s_manager, *m_manager;
 	GckSecretSearch *search;
-	GList *managers = NULL;
 	CK_ATTRIBUTE *attr;
 	GHashTable *fields;
-	GckManager *manager;
 	GckModule *module;
 	CK_RV rv;
 
@@ -250,21 +212,17 @@ factory_create_search (GckSession *session, GckTransaction *transaction,
 		return;
 	}
 
-	manager = gck_session_get_manager (session);
+	s_manager = gck_session_get_manager (session);
 	module = gck_session_get_module (session);
-
-	/* The managers we're going to be searching */
-	managers = g_list_prepend (managers, gck_module_get_manager (module));
-	managers = g_list_prepend (managers, manager);
+	m_manager = gck_module_get_manager (module);
 
 	/* See if a collection attribute was specified, not present means all collections */
 	attr = gck_attributes_find (attrs, n_attrs, CKA_G_COLLECTION);
 	if (attr) {
-		collection = find_collection_in_managers (managers, attr);
+		collection = gck_secret_collection_find (attr, s_manager, m_manager, NULL);
 		gck_attribute_consume (attr);
 		if (!collection) {
 			g_hash_table_unref (fields);
-			g_list_free (managers);
 			gck_transaction_fail (transaction, CKR_TEMPLATE_INCONSISTENT);
 			return;
 		}
@@ -272,12 +230,16 @@ factory_create_search (GckSession *session, GckTransaction *transaction,
 
 	search = g_object_new (GCK_TYPE_SECRET_SEARCH,
 	                       "module", module,
-	                       "manager", manager,
+	                       "manager", s_manager,
 	                       "fields", fields,
 	                       "collection", collection,
 	                       NULL);
 
-	populate_search_from_managers (search, managers);
+	/* Load any new items or collections */
+	gck_module_refresh_token (module);
+
+	populate_search_from_manager (search, s_manager);
+	populate_search_from_manager (search, m_manager);
 	*result = GCK_OBJECT (search);
 }
 
diff --git a/pkcs11/secret-store/gck-secret-textual.c b/pkcs11/secret-store/gck-secret-textual.c
index 51b24c9..087a4af 100644
--- a/pkcs11/secret-store/gck-secret-textual.c
+++ b/pkcs11/secret-store/gck-secret-textual.c
@@ -478,7 +478,12 @@ gck_secret_textual_read (GckSecretCollection *collection, GckSecretData *sdata,
 
 	file = g_key_file_new ();
 	obj = GCK_SECRET_OBJECT (collection);
-	
+
+	if (!n_data) {
+		res = GCK_DATA_UNRECOGNIZED;
+		goto done;
+	}
+
 	if (!g_key_file_load_from_data (file, (const gchar*)data, n_data, G_KEY_FILE_NONE, &err)) {
 		if (g_error_matches (err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE))
 			res = GCK_DATA_UNRECOGNIZED;
@@ -529,7 +534,7 @@ gck_secret_textual_read (GckSecretCollection *collection, GckSecretData *sdata,
 		
 		item = gck_secret_collection_get_item (collection, identifier);
 		if (item == NULL)
-			item = gck_secret_collection_create_item (collection, identifier);
+			item = gck_secret_collection_new_item (collection, identifier);
 		parse_item (file, item, sdata, (const gchar**)groups);
 	}
 
diff --git a/pkcs11/secret-store/tests/test-secret-module.c b/pkcs11/secret-store/tests/test-secret-module.c
index 93b0f6b..36462de 100644
--- a/pkcs11/secret-store/tests/test-secret-module.c
+++ b/pkcs11/secret-store/tests/test-secret-module.c
@@ -23,6 +23,7 @@
 
 #include "config.h"
 #include "test-secret-module.h"
+#include "run-auto-test.h"
 
 #include "gck/gck-secret.h"
 #include "gck/gck-module.h"
@@ -34,20 +35,53 @@
 #include "gck-secret-object.h"
 #include "gck-secret-store.h"
 
+#include <string.h>
+
 static GMutex *mutex = NULL;
 
 GckModule*  _gck_secret_store_get_module_for_testing (void);
 GMutex* _gck_module_get_scary_mutex_that_you_should_not_touch (GckModule *module);
 
+static void
+copy_scratch_file (const gchar *basename)
+{
+	gchar *filename;
+	gchar *data;
+	gsize n_data;
+
+	filename = test_data_filename (basename);
+	if (!g_file_get_contents (filename, &data, &n_data, NULL))
+		g_return_if_reached ();
+	g_free (filename);
+
+	filename = test_scratch_filename (basename);
+	if (!g_file_set_contents (filename, data, n_data, NULL))
+		g_return_if_reached ();
+	g_free (filename);
+	g_free (data);
+}
+
 GckModule*
 test_secret_module_initialize_and_enter (void)
 {
 	CK_FUNCTION_LIST_PTR funcs;
+	CK_C_INITIALIZE_ARGS args;
 	GckModule *module;
+	gchar *string;
 	CK_RV rv;
 
+	/* Setup test directory to work in */
+	memset (&args, 0, sizeof (args));
+	string = g_strdup_printf ("directory='%s'", test_scratch_directory ());
+	args.pReserved = string;
+	args.flags = CKF_OS_LOCKING_OK;
+
+	/* Copy files from test-data to scratch */
+	copy_scratch_file ("encrypted.keyring");
+	copy_scratch_file ("plain.keyring");
+
 	funcs = gck_secret_store_get_functions ();
-	rv = (funcs->C_Initialize) (NULL);
+	rv = (funcs->C_Initialize) (&args);
 	g_return_val_if_fail (rv == CKR_OK, NULL);
 
 	module = _gck_secret_store_get_module_for_testing ();
@@ -56,6 +90,8 @@ test_secret_module_initialize_and_enter (void)
 	mutex = _gck_module_get_scary_mutex_that_you_should_not_touch (module);
 	test_secret_module_enter ();
 
+	g_free (string);
+
 	return module;
 }
 
@@ -104,6 +140,9 @@ test_secret_module_open_session (gboolean writable)
 	rv = gck_module_C_OpenSession (module, 1, flags, NULL, NULL, &handle);
 	g_assert (rv == CKR_OK);
 
+	rv = gck_module_C_Login (module, handle, CKU_USER, NULL, 0);
+	g_assert (rv == CKR_OK);
+
 	session = gck_module_lookup_session (module, handle);
 	g_assert (session);
 
@@ -215,7 +254,7 @@ test_secret_collection_populate (GckSecretCollection *collection, GckSecretData
 	GHashTable *fields;
 	GckSecret *secret;
 
-	item = gck_secret_collection_create_item (collection, "4");
+	item = gck_secret_collection_new_item (collection, "4");
 	gck_secret_object_set_label (GCK_SECRET_OBJECT (item), "Noises");
 	secret = gck_secret_new_from_password ("4's secret");
 	gck_secret_data_set_secret (sdata, "4", secret);
@@ -225,7 +264,7 @@ test_secret_collection_populate (GckSecretCollection *collection, GckSecretData
 	gck_secret_fields_add (fields, "pig", "grunt");
 	gck_secret_fields_add_compat_uint32 (fields, "how-many", 292929);
 
-	item = gck_secret_collection_create_item (collection, "5");
+	item = gck_secret_collection_new_item (collection, "5");
 	gck_secret_object_set_label (GCK_SECRET_OBJECT (item), "Colors");
 	secret = gck_secret_new_from_password ("5's secret");
 	gck_secret_data_set_secret (sdata, "5", secret);
@@ -235,7 +274,7 @@ test_secret_collection_populate (GckSecretCollection *collection, GckSecretData
 	gck_secret_fields_add (fields, "piglet", "pink");
 	gck_secret_fields_add_compat_uint32 (fields, "number", 8);
 
-	item = gck_secret_collection_create_item (collection, "6");
+	item = gck_secret_collection_new_item (collection, "6");
 	gck_secret_object_set_label (GCK_SECRET_OBJECT (item), "Binary Secret");
 	secret = gck_secret_new ((guchar*)"binary\0secret", 13);
 	gck_secret_data_set_secret (sdata, "6", secret);
diff --git a/pkcs11/secret-store/tests/unit-test-secret-collection.c b/pkcs11/secret-store/tests/unit-test-secret-collection.c
index 854ee90..9182c07 100644
--- a/pkcs11/secret-store/tests/unit-test-secret-collection.c
+++ b/pkcs11/secret-store/tests/unit-test-secret-collection.c
@@ -28,9 +28,11 @@
 
 #include "gck-secret-data.h"
 #include "gck-secret-collection.h"
+#include "gck-secret-item.h"
 
 #include "gck/gck-authenticator.h"
 #include "gck/gck-session.h"
+#include "gck/gck-transaction.h"
 
 #include "pkcs11/pkcs11i.h"
 
@@ -123,6 +125,14 @@ DEFINE_TEST(secret_collection_set_filename)
 	g_assert_cmpstr (filename, ==, "/tmp/filename.keyring");
 }
 
+DEFINE_TEST(secret_collection_has_item)
+{
+	GckSecretItem *item;
+
+	item = gck_secret_collection_new_item (collection, "testo");
+	g_assert (gck_secret_collection_has_item (collection, item));
+}
+
 DEFINE_TEST(secret_collection_load_unlock_plain)
 {
 	GckAuthenticator *auth;
@@ -313,3 +323,201 @@ DEFINE_TEST(secret_collection_memory_unlock_bad_password)
 	                               (guchar*)"wrong", 5, &auth);
 	g_assert (rv == CKR_PIN_INCORRECT);
 }
+
+DEFINE_TEST(secret_collection_factory)
+{
+	CK_OBJECT_CLASS klass = CKO_G_COLLECTION;
+	GckObject *object;
+	CK_RV rv;
+
+	CK_ATTRIBUTE attrs[] = {
+		{ CKA_CLASS, &klass, sizeof (klass) },
+		{ CKA_LABEL, "blah", 4 },
+	};
+
+	rv = gck_session_create_object_for_factory (session, GCK_FACTORY_SECRET_COLLECTION,
+	                                            attrs, G_N_ELEMENTS (attrs), &object);
+	g_assert (rv == CKR_OK);
+	g_assert (GCK_IS_SECRET_COLLECTION (object));
+
+	g_assert_cmpstr (gck_secret_object_get_label (GCK_SECRET_OBJECT (object)), ==, "blah");
+}
+
+DEFINE_TEST(secret_collection_factory_unnamed)
+{
+	CK_OBJECT_CLASS klass = CKO_G_COLLECTION;
+	const gchar *identifier;
+	GckObject *object;
+	CK_RV rv;
+
+	CK_ATTRIBUTE attrs[] = {
+		{ CKA_CLASS, &klass, sizeof (klass) },
+	};
+
+	rv = gck_session_create_object_for_factory (session, GCK_FACTORY_SECRET_COLLECTION,
+	                                            attrs, G_N_ELEMENTS (attrs), &object);
+	g_assert (rv == CKR_OK);
+	g_assert (GCK_IS_SECRET_COLLECTION (object));
+
+	identifier = gck_secret_object_get_identifier (GCK_SECRET_OBJECT (object));
+	g_assert_cmpstr (identifier, !=, "");
+}
+
+DEFINE_TEST(secret_collection_factory_token)
+{
+	CK_OBJECT_CLASS klass = CKO_G_COLLECTION;
+	const gchar *identifier;
+	GckObject *object;
+	CK_BBOOL token = CK_TRUE;
+	CK_RV rv;
+
+	CK_ATTRIBUTE attrs[] = {
+		{ CKA_CLASS, &klass, sizeof (klass) },
+		{ CKA_TOKEN, &token, sizeof (token) },
+		{ CKA_LABEL, "blah", 4 },
+	};
+
+	rv = gck_session_create_object_for_factory (session, GCK_FACTORY_SECRET_COLLECTION,
+	                                            attrs, G_N_ELEMENTS (attrs), &object);
+	g_assert (rv == CKR_OK);
+	g_assert (GCK_IS_SECRET_COLLECTION (object));
+
+	identifier = gck_secret_object_get_identifier (GCK_SECRET_OBJECT (object));
+	g_assert (strstr (identifier, "blah"));
+}
+
+DEFINE_TEST(secret_collection_factory_duplicate)
+{
+	CK_OBJECT_CLASS klass = CKO_G_COLLECTION;
+	const gchar *identifier1, *identifier2;
+	GckObject *object;
+	CK_RV rv;
+
+	CK_ATTRIBUTE attrs[] = {
+		{ CKA_CLASS, &klass, sizeof (klass) },
+		{ CKA_LABEL, "blah", 4 },
+	};
+
+	rv = gck_session_create_object_for_factory (session, GCK_FACTORY_SECRET_COLLECTION,
+	                                            attrs, G_N_ELEMENTS (attrs), &object);
+	g_assert (rv == CKR_OK);
+	g_assert (GCK_IS_SECRET_COLLECTION (object));
+
+	identifier1 = gck_secret_object_get_identifier (GCK_SECRET_OBJECT (object));
+	g_assert (strstr (identifier1, "blah"));
+
+	rv = gck_session_create_object_for_factory (session, GCK_FACTORY_SECRET_COLLECTION,
+	                                            attrs, G_N_ELEMENTS (attrs), &object);
+	g_assert (rv == CKR_OK);
+	g_assert (GCK_IS_SECRET_COLLECTION (object));
+
+	identifier2 = gck_secret_object_get_identifier (GCK_SECRET_OBJECT (object));
+	g_assert (strstr (identifier2, "blah"));
+
+	g_assert_cmpstr (identifier1, !=, identifier2);
+}
+
+DEFINE_TEST(secret_collection_factory_item)
+{
+	CK_OBJECT_CLASS c_klass = CKO_G_COLLECTION;
+	CK_OBJECT_CLASS i_klass = CKO_SECRET_KEY;
+	const gchar *identifier;
+	GckObject *object;
+	CK_BBOOL token = CK_TRUE;
+	CK_RV rv;
+
+	CK_ATTRIBUTE c_attrs[] = {
+		{ CKA_CLASS, &c_klass, sizeof (c_klass) },
+		{ CKA_TOKEN, &token, sizeof (token) },
+		{ CKA_LABEL, "three", 5 },
+	};
+
+	CK_ATTRIBUTE i_attrs[] = {
+		{ CKA_G_COLLECTION, NULL, 0 }, /* Filled below */
+		{ CKA_CLASS, &i_klass, sizeof (i_klass) },
+		{ CKA_TOKEN, &token, sizeof (token) },
+		{ CKA_LABEL, "Item", 4 },
+	};
+
+	rv = gck_session_create_object_for_factory (session, GCK_FACTORY_SECRET_COLLECTION,
+	                                            c_attrs, G_N_ELEMENTS (c_attrs), &object);
+	g_assert (rv == CKR_OK);
+	g_assert (GCK_IS_SECRET_COLLECTION (object));
+
+	identifier = gck_secret_object_get_identifier (GCK_SECRET_OBJECT (object));
+	i_attrs[0].pValue = (gpointer)identifier;
+	i_attrs[0].ulValueLen = strlen (identifier);
+	rv = gck_session_create_object_for_factory (session, GCK_FACTORY_SECRET_ITEM,
+	                                            i_attrs, G_N_ELEMENTS (i_attrs), &object);
+	g_assert (rv == CKR_OK);
+	g_assert (GCK_IS_SECRET_ITEM (object));
+}
+
+DEFINE_TEST(secret_collection_token_remove)
+{
+	CK_OBJECT_CLASS klass = CKO_G_COLLECTION;
+	GckTransaction *transaction;
+	GckObject *object;
+	CK_BBOOL token = CK_TRUE;
+	CK_RV rv;
+
+	CK_ATTRIBUTE attrs[] = {
+		{ CKA_CLASS, &klass, sizeof (klass) },
+		{ CKA_TOKEN, &token, sizeof (token) },
+		{ CKA_LABEL, "blah", 4 },
+	};
+
+	rv = gck_session_create_object_for_factory (session, GCK_FACTORY_SECRET_COLLECTION,
+	                                            attrs, G_N_ELEMENTS (attrs), &object);
+	g_assert (rv == CKR_OK);
+	g_assert (GCK_IS_SECRET_COLLECTION (object));
+
+	transaction = gck_transaction_new ();
+	gck_module_remove_token_object (module, transaction, object);
+	g_assert (!gck_transaction_get_failed (transaction));
+	gck_transaction_complete (transaction);
+	g_object_unref (transaction);
+}
+
+DEFINE_TEST(secret_collection_token_item_remove)
+{
+	CK_OBJECT_CLASS c_klass = CKO_G_COLLECTION;
+	CK_OBJECT_CLASS i_klass = CKO_SECRET_KEY;
+	GckTransaction *transaction;
+	const gchar *identifier;
+	GckObject *object;
+	CK_BBOOL token = CK_TRUE;
+	CK_RV rv;
+
+	CK_ATTRIBUTE c_attrs[] = {
+		{ CKA_CLASS, &c_klass, sizeof (c_klass) },
+		{ CKA_TOKEN, &token, sizeof (token) },
+		{ CKA_LABEL, "three", 5 },
+	};
+
+	CK_ATTRIBUTE i_attrs[] = {
+		{ CKA_G_COLLECTION, NULL, 0 }, /* Filled below */
+		{ CKA_CLASS, &i_klass, sizeof (i_klass) },
+		{ CKA_TOKEN, &token, sizeof (token) },
+		{ CKA_LABEL, "Item", 4 },
+	};
+
+	rv = gck_session_create_object_for_factory (session, GCK_FACTORY_SECRET_COLLECTION,
+	                                            c_attrs, G_N_ELEMENTS (c_attrs), &object);
+	g_assert (rv == CKR_OK);
+	g_assert (GCK_IS_SECRET_COLLECTION (object));
+
+	identifier = gck_secret_object_get_identifier (GCK_SECRET_OBJECT (object));
+	i_attrs[0].pValue = (gpointer)identifier;
+	i_attrs[0].ulValueLen = strlen (identifier);
+	rv = gck_session_create_object_for_factory (session, GCK_FACTORY_SECRET_ITEM,
+	                                            i_attrs, G_N_ELEMENTS (i_attrs), &object);
+	g_assert (rv == CKR_OK);
+	g_assert (GCK_IS_SECRET_ITEM (object));
+
+	transaction = gck_transaction_new ();
+	gck_module_remove_token_object (module, transaction, object);
+	g_assert (!gck_transaction_get_failed (transaction));
+	gck_transaction_complete (transaction);
+	g_object_unref (transaction);
+}
diff --git a/pkcs11/secret-store/tests/unit-test-secret-item.c b/pkcs11/secret-store/tests/unit-test-secret-item.c
index c77a75d..fb5eb9b 100644
--- a/pkcs11/secret-store/tests/unit-test-secret-item.c
+++ b/pkcs11/secret-store/tests/unit-test-secret-item.c
@@ -85,20 +85,102 @@ unlock_collection(void)
 	g_object_unref (auth);
 }
 
-DEFINE_TEST(secret_item_create)
+DEFINE_TEST(secret_item_new)
 {
 	GckSecretItem *item;
 
-	item = gck_secret_collection_create_item (collection, "the-identifier");
+	item = gck_secret_collection_new_item (collection, "the-identifier");
 	g_assert (GCK_IS_SECRET_ITEM (item));
 	g_assert_cmpstr (gck_secret_object_get_identifier (GCK_SECRET_OBJECT (item)), ==, "the-identifier");
 }
 
+DEFINE_TEST(secret_item_create)
+{
+	GckTransaction *transaction;
+	GckSecretItem *item;
+
+	transaction = gck_transaction_new ();
+	item = gck_secret_collection_create_item (collection, transaction);
+	g_assert (GCK_IS_SECRET_ITEM (item));
+	g_object_ref (item);
+	g_assert (gck_secret_collection_has_item (collection, item));
+
+	gck_transaction_complete (transaction);
+	g_object_unref (transaction);
+
+	/* Should still be there */
+	g_assert (gck_secret_collection_has_item (collection, item));
+	g_object_unref (item);
+}
+
+DEFINE_TEST(secret_item_create_failed)
+{
+	GckTransaction *transaction;
+	GckSecretItem *item;
+
+	transaction = gck_transaction_new ();
+	item = gck_secret_collection_create_item (collection, transaction);
+	g_assert (GCK_IS_SECRET_ITEM (item));
+	g_object_ref (item);
+	g_assert (gck_secret_collection_has_item (collection, item));
+
+	gck_transaction_fail (transaction, CKR_GENERAL_ERROR);
+	gck_transaction_complete (transaction);
+	g_object_unref (transaction);
+
+	/* Should no longer be there */
+	g_assert (!gck_secret_collection_has_item (collection, item));
+	g_object_unref (item);
+}
+
+DEFINE_TEST(secret_item_destroy)
+{
+	GckTransaction *transaction;
+	GckSecretItem *item;
+
+	item = gck_secret_collection_new_item (collection, "the-identifier");
+	g_assert (gck_secret_collection_has_item (collection, item));
+	g_object_ref (item);
+
+	transaction = gck_transaction_new ();
+	gck_secret_collection_destroy_item (collection, transaction, item);
+	g_assert (!gck_secret_collection_has_item (collection, item));
+
+	gck_transaction_complete (transaction);
+	g_object_unref (transaction);
+
+	/* Should not be there */
+	g_assert (!gck_secret_collection_has_item (collection, item));
+	g_object_unref (item);
+}
+
+DEFINE_TEST(secret_item_destroy_failed)
+{
+	GckTransaction *transaction;
+	GckSecretItem *item;
+
+	item = gck_secret_collection_new_item (collection, "the-identifier");
+	g_assert (gck_secret_collection_has_item (collection, item));
+	g_object_ref (item);
+
+	transaction = gck_transaction_new ();
+	gck_secret_collection_destroy_item (collection, transaction, item);
+	g_assert (!gck_secret_collection_has_item (collection, item));
+
+	gck_transaction_fail (transaction, CKR_GENERAL_ERROR);
+	gck_transaction_complete (transaction);
+	g_object_unref (transaction);
+
+	/* Should be there */
+	g_assert (gck_secret_collection_has_item (collection, item));
+	g_object_unref (item);
+}
+
 DEFINE_TEST(secret_item_collection_get)
 {
 	GckSecretItem *item, *check;
 
-	item = gck_secret_collection_create_item (collection, "the-identifier");
+	item = gck_secret_collection_new_item (collection, "the-identifier");
 	g_assert (GCK_IS_SECRET_ITEM (item));
 
 	check = gck_secret_collection_get_item (collection, "the-identifier");
@@ -110,9 +192,9 @@ DEFINE_TEST(secret_item_collection_items)
 	GList *l, *items;
 	const gchar *identifier;
 
-	gck_secret_collection_create_item (collection, "one-identifier");
-	gck_secret_collection_create_item (collection, "two-identifier");
-	gck_secret_collection_create_item (collection, "three-identifier");
+	gck_secret_collection_new_item (collection, "one-identifier");
+	gck_secret_collection_new_item (collection, "two-identifier");
+	gck_secret_collection_new_item (collection, "three-identifier");
 
 	items = gck_secret_collection_get_items (collection);
 	g_assert_cmpuint (g_list_length (items), ==, 3);
@@ -131,7 +213,7 @@ DEFINE_TEST(secret_item_collection_remove)
 {
 	GckSecretItem *item;
 
-	item = gck_secret_collection_create_item (collection, "the-identifier");
+	item = gck_secret_collection_new_item (collection, "the-identifier");
 	g_assert (gck_secret_collection_get_item (collection, "the-identifier") == item);
 
 	gck_secret_collection_remove_item (collection, item);
@@ -142,7 +224,7 @@ DEFINE_TEST(secret_item_is_locked)
 {
 	GckSecretItem *item;
 
-	item = gck_secret_collection_create_item (collection, "the-identifier");
+	item = gck_secret_collection_new_item (collection, "the-identifier");
 	g_assert (gck_secret_object_is_locked (GCK_SECRET_OBJECT (item), session) == 
 	          gck_secret_object_is_locked (GCK_SECRET_OBJECT (collection), session));
 
@@ -154,14 +236,14 @@ DEFINE_TEST(secret_item_is_locked)
 DEFINE_TEST(secret_item_get_collection)
 {
 	GckSecretItem *item;
-	item = gck_secret_collection_create_item (collection, "the-identifier");
+	item = gck_secret_collection_new_item (collection, "the-identifier");
 	g_assert (gck_secret_item_get_collection (item) == collection);
 }
 
 DEFINE_TEST(secret_item_tracks_collection)
 {
 	GckSecretItem *item;
-	item = gck_secret_collection_create_item (collection, "the-identifier");
+	item = gck_secret_collection_new_item (collection, "the-identifier");
 	g_object_ref (item);
 
 	unlock_collection();
@@ -185,7 +267,7 @@ DEFINE_TEST(secret_item_get_set_fields)
 	GHashTable *check;
 	GckSecretItem *item;
 
-	item = gck_secret_collection_create_item (collection, "the-identifier");
+	item = gck_secret_collection_new_item (collection, "the-identifier");
 	gck_secret_item_set_fields (item, fields);
 	gck_secret_item_set_fields (item, fields);
 
@@ -202,7 +284,7 @@ DEFINE_TEST(secret_item_collection_attr)
 	GckSecretItem *item;
 	CK_RV rv;
 
-	item = gck_secret_collection_create_item (collection, "the-identifier");
+	item = gck_secret_collection_new_item (collection, "the-identifier");
 	rv = gck_object_get_attribute (GCK_OBJECT (item), session, &check);
 	g_assert (rv == CKR_OK);
 	g_assert (check.ulValueLen == 4);
@@ -220,7 +302,7 @@ DEFINE_TEST(secret_item_secret_attr)
 
 	unlock_collection ();
 
-	item = gck_secret_collection_create_item (collection, "the-identifier");
+	item = gck_secret_collection_new_item (collection, "the-identifier");
 	gck_object_set_attribute (GCK_OBJECT (item), session, transaction, &attr);
 	g_assert (gck_transaction_get_failed (transaction) == FALSE);
 	gck_transaction_complete (transaction);
@@ -243,7 +325,7 @@ DEFINE_TEST(secret_item_secret_attr_locked)
 	GckSecretItem *item;
 	CK_RV rv;
 
-	item = gck_secret_collection_create_item (collection, "the-identifier");
+	item = gck_secret_collection_new_item (collection, "the-identifier");
 	gck_object_set_attribute (GCK_OBJECT (item), session, transaction, &attr);
 	g_assert (gck_transaction_get_failed (transaction) == TRUE);
 	gck_transaction_complete (transaction);
@@ -268,7 +350,7 @@ DEFINE_TEST(secret_item_fields_attr)
 
 	unlock_collection ();
 
-	item = gck_secret_collection_create_item (collection, "the-identifier");
+	item = gck_secret_collection_new_item (collection, "the-identifier");
 	gck_object_set_attribute (GCK_OBJECT (item), session, transaction, &attr);
 	g_assert (gck_transaction_get_failed (transaction) == FALSE);
 	gck_transaction_complete (transaction);
@@ -295,7 +377,7 @@ DEFINE_TEST(secret_item_fields_attr_locked)
 	CK_ATTRIBUTE attr = { CKA_G_FIELDS, "name1\0value1\0name2\0value2", 26 };
 	GckSecretItem *item;
 
-	item = gck_secret_collection_create_item (collection, "the-identifier");
+	item = gck_secret_collection_new_item (collection, "the-identifier");
 	gck_object_set_attribute (GCK_OBJECT (item), session, transaction, &attr);
 	g_assert (gck_transaction_get_failed (transaction) == TRUE);
 	gck_transaction_complete (transaction);
@@ -316,7 +398,7 @@ DEFINE_TEST(secret_item_fields_attr_reverts)
 
 	unlock_collection ();
 
-	item = gck_secret_collection_create_item (collection, "the-identifier");
+	item = gck_secret_collection_new_item (collection, "the-identifier");
 
 	/* Set the old value like so */
 	fields = gck_secret_fields_new ();
diff --git a/pkcs11/secret-store/tests/unit-test-secret-search.c b/pkcs11/secret-store/tests/unit-test-secret-search.c
index 2aafcb5..8a51591 100644
--- a/pkcs11/secret-store/tests/unit-test-secret-search.c
+++ b/pkcs11/secret-store/tests/unit-test-secret-search.c
@@ -64,7 +64,7 @@ DEFINE_SETUP(secret_search)
 	                           NULL);
 
 	/* Create an item */
-	item = gck_secret_collection_create_item (collection, "test-item");
+	item = gck_secret_collection_new_item (collection, "test-item");
 	fields = gck_secret_fields_new ();
 	gck_secret_fields_add (fields, "name1", "value1");
 	gck_secret_fields_add (fields, "name2", "value2");
@@ -331,7 +331,7 @@ DEFINE_TEST(create_search_for_collection_no_match)
 	                      "manager", gck_session_get_manager (session),
 	                      "identifier", "other-collection",
 	                      NULL);
-	oitem = gck_secret_collection_create_item (ocoll, "other-item");
+	oitem = gck_secret_collection_new_item (ocoll, "other-item");
 	gck_object_expose (GCK_OBJECT (ocoll), TRUE);
 
 	/* Make it match, but remember, wrong collection*/



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