Rework how the initialization work for various proxy objects
authorStef Walter <stefw@gnome.org>
Wed, 25 Jan 2012 13:26:52 +0000 (14:26 +0100)
committerStef Walter <stefw@gnome.org>
Fri, 3 Feb 2012 19:44:44 +0000 (20:44 +0100)
 * Fix bugs and tests

18 files changed:
.gitignore
configure.ac
library/Makefile.am
library/gsecret-collection.c
library/gsecret-enum-types.c.template [new file with mode: 0644]
library/gsecret-enum-types.h.template [new file with mode: 0644]
library/gsecret-item.c
library/gsecret-password.c
library/gsecret-private.h
library/gsecret-service.c
library/gsecret-service.h
library/gsecret-session.c
library/tests/test-collection.c
library/tests/test-item.c
library/tests/test-prompt.c
library/tests/test-service.c
library/tests/test-session.c
po/POTFILES.in

index 9b4c7c2..27ba13e 100644 (file)
@@ -40,6 +40,7 @@ stamp*
 !/egg/tests/test-*.c
 
 /library/gsecret-dbus-generated.[ch]
+/library/gsecret-enum-types.[ch]
 /library/tests/test-*
 !/library/tests/test-*.c
 
index 1a0426b..b20e5f5 100644 (file)
@@ -41,6 +41,8 @@ PKG_CHECK_MODULES(GLIB,
 LIBS="$LIBS $GLIB_LIBS"
 CFLAGS="$CFLAGS $GLIB_CFLAGS"
 
+AC_PATH_PROG(GLIB_MKENUMS, glib-mkenums)
+
 # --------------------------------------------------------------------
 # libgcrypt
 #
index 720b1e7..c8fbb7e 100644 (file)
@@ -9,10 +9,22 @@ module_flags = \
        -no-undefined \
        -export-symbols-regex '^gsecret_'
 
+INCLUDES = \
+       -DGSECRET_COMPILATION
+
 lib_LTLIBRARIES = libgsecret.la
 
+HEADER_FILES = \
+       gsecret-collection.h \
+       gsecret-item.h \
+       gsecret-password.h \
+       gsecret-prompt.h \
+       gsecret-service.h \
+       gsecret-value.h
+
 BUILT_SOURCES = \
-       gsecret-dbus-generated.c gsecret-dbus-generated.h
+       gsecret-dbus-generated.c gsecret-dbus-generated.h \
+       gsecret-enum-types.c gsecret-enum-types.h
 
 libgsecret_la_SOURCES = \
        gsecret-collection.h gsecret-collection.c \
@@ -45,5 +57,15 @@ gsecret-dbus-generated.c: $(DBUS_XML_DEFINITIONS) Makefile.am
        $(AM_V_GEN) sed -i -e '1i #define GLIB_DISABLE_DEPRECATION_WARNINGS' gsecret-dbus-generated.c
 gsecret-dbus-generated.h: gsecret-dbus-generated.c
 
+gsecret-enum-types.h: gsecret-enum-types.h.template $(HEADER_FILES)
+       $(AM_V_GEN) $(GLIB_MKENUMS) --template $^ > $@
+
+gsecret-enum-types.c: gsecret-enum-types.c.template $(HEADER_FILES)
+       $(AM_V_GEN) $(GLIB_MKENUMS) --template $^ > $@
+
+EXTRA_DIST = \
+       gsecret-enum-types.h.template \
+       gsecret-enum-types.c.template
+
 check-memory:
        make -C tests check-memory
\ No newline at end of file
index 3f1a8fa..6c4bc81 100644 (file)
@@ -42,7 +42,18 @@ struct _GSecretCollectionPrivate {
        GHashTable *items;
 };
 
-G_DEFINE_TYPE (GSecretCollection, gsecret_collection, G_TYPE_DBUS_PROXY);
+static GInitableIface *gsecret_collection_initable_parent_iface = NULL;
+
+static GAsyncInitableIface *gsecret_collection_async_initable_parent_iface = NULL;
+
+static void   gsecret_collection_initable_iface         (GInitableIface *iface);
+
+static void   gsecret_collection_async_initable_iface   (GAsyncInitableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GSecretCollection, gsecret_collection, G_TYPE_DBUS_PROXY,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gsecret_collection_initable_iface);
+                         G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, gsecret_collection_async_initable_iface);
+);
 
 static GHashTable *
 items_table_new (void)
@@ -166,69 +177,63 @@ gsecret_collection_finalize (GObject *obj)
        G_OBJECT_CLASS (gsecret_collection_parent_class)->finalize (obj);
 }
 
-typedef struct {
-       GSecretCollection *collection;
-       GCancellable *cancellable;
-       GHashTable *items;
-       gint items_loading;
-} LoadClosure;
-
-static void
-load_closure_free (gpointer data)
+static GSecretItem *
+collection_lookup_item (GSecretCollection *self,
+                        const gchar *path)
 {
-       LoadClosure *closure = data;
-       g_object_unref (closure->collection);
-       g_clear_object (&closure->cancellable);
-       g_hash_table_unref (closure->items);
-       g_slice_free (LoadClosure, closure);
-}
+       GSecretItem *item = NULL;
 
-static GSimpleAsyncResult *
-load_result_new (GCancellable *cancellable,
-                 GAsyncReadyCallback callback,
-                 gpointer user_data)
-{
-       GSimpleAsyncResult *res;
-       LoadClosure *closure;
+       g_mutex_lock (&self->pv->mutex);
 
-       res = g_simple_async_result_new (NULL, callback, user_data, load_result_new);
-       closure = g_slice_new0 (LoadClosure);
-       closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
-       closure->items = items_table_new ();
-       g_simple_async_result_set_op_res_gpointer (res, closure, load_closure_free);
+       item = g_hash_table_lookup (self->pv->items, path);
+       if (item != NULL)
+               g_object_ref (item);
+
+       g_mutex_unlock (&self->pv->mutex);
 
-       return res;
+       return item;
 }
 
 static void
-load_items_complete (GSimpleAsyncResult *res)
+collection_update_items (GSecretCollection *self,
+                         GHashTable *items)
 {
-       LoadClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
-       GSecretCollection *self = closure->collection;
-       GHashTable *items;
+       GHashTable *previous;
 
-       g_assert (closure->items_loading == 0);
-
-       g_hash_table_ref (closure->items);
+       g_hash_table_ref (items);
 
        g_mutex_lock (&self->pv->mutex);
-       items = self->pv->items;
-       self->pv->items = closure->items;
+       previous = self->pv->items;
+       self->pv->items = items;
        g_mutex_unlock (&self->pv->mutex);
 
-       g_hash_table_unref (items);
+       g_hash_table_unref (previous);
+}
 
-       g_simple_async_result_complete (res);
+typedef struct {
+       GCancellable *cancellable;
+       GHashTable *items;
+       gint items_loading;
+} ItemsClosure;
+
+static void
+items_closure_free (gpointer data)
+{
+       ItemsClosure *closure = data;
+       g_clear_object (&closure->cancellable);
+       g_hash_table_unref (closure->items);
+       g_slice_free (ItemsClosure, closure);
 }
 
 static void
-on_item_loading (GObject *source,
-                 GAsyncResult *result,
-                 gpointer user_data)
+on_load_item (GObject *source,
+              GAsyncResult *result,
+              gpointer user_data)
 {
        GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
-       LoadClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
-       const gchar *item_path;
+       ItemsClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
+       GSecretCollection *self = GSECRET_COLLECTION (g_async_result_get_source_object (user_data));
+       const gchar *path;
        GError *error = NULL;
        GSecretItem *item;
 
@@ -240,57 +245,118 @@ on_item_loading (GObject *source,
                g_simple_async_result_take_error (res, error);
 
        if (item != NULL) {
-               item_path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (item));
-               g_hash_table_insert (closure->items, g_strdup (item_path), item);
+               path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (item));
+               g_hash_table_insert (closure->items, g_strdup (path), item);
        }
 
-       if (closure->items_loading == 0)
-               load_items_complete (res);
+       if (closure->items_loading == 0) {
+               collection_update_items (self, closure->items);
+               g_simple_async_result_complete_in_idle (res);
+       }
 
+       g_object_unref (self);
        g_object_unref (res);
 }
 
 static void
-load_items_perform (GSecretCollection *self,
-                    GSimpleAsyncResult *res,
-                    GVariant *item_paths)
+collection_load_items_async (GSecretCollection *self,
+                             GCancellable *cancellable,
+                             GAsyncReadyCallback callback,
+                             gpointer user_data)
 {
-       LoadClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
+       ItemsClosure *closure;
        GSecretItem *item;
+       GSimpleAsyncResult *res;
+       const gchar *path;
+       GVariant *paths;
        GVariantIter iter;
-       gchar *item_path;
-
-       g_assert (GSECRET_IS_COLLECTION (self));
-       g_assert (item_paths != NULL);
-       g_assert (closure->collection == NULL);
 
-       closure->collection = g_object_ref (self);
+       paths = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Items");
+       g_return_if_fail (paths != NULL);
 
-       g_variant_iter_init (&iter, item_paths);
-       while (g_variant_iter_loop (&iter, "o", &item_path)) {
+       res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
+                                        collection_load_items_async);
+       closure = g_slice_new0 (ItemsClosure);
+       closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+       closure->items = items_table_new ();
+       g_simple_async_result_set_op_res_gpointer (res, closure, items_closure_free);
 
-               g_mutex_lock (&self->pv->mutex);
-               item = g_hash_table_lookup (self->pv->items, item_path);
-               if (item != NULL)
-                       g_object_ref (item);
-               g_mutex_unlock (&self->pv->mutex);
+       g_variant_iter_init (&iter, paths);
+       while (g_variant_iter_loop (&iter, "&o", &path)) {
+               item = collection_lookup_item (self, path);
 
+               /* No such collection yet create a new one */
                if (item == NULL) {
-                       // TODO: xxxxxxxxxxxx;
-                       gsecret_item_new (self->pv->service, item_path,
-                                         closure->cancellable, on_item_loading,
-                                         g_object_ref (res));
+                       gsecret_item_new (self->pv->service, path, cancellable,
+                                         on_load_item, g_object_ref (res));
                        closure->items_loading++;
 
                } else {
-                       g_hash_table_insert (closure->items,
-                                            g_strdup (item_path), item);
+                       g_hash_table_insert (closure->items, g_strdup (path), item);
+               }
+       }
+
+       if (closure->items_loading == 0) {
+               collection_update_items (self, closure->items);
+               g_simple_async_result_complete_in_idle (res);
+       }
+
+       g_variant_unref (paths);
+       g_object_unref (res);
+}
+
+static gboolean
+collection_load_items_finish (GSecretCollection *self,
+                              GAsyncResult *result,
+                              GError **error)
+{
+       if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+               return FALSE;
+
+       return TRUE;
+}
+
+
+static gboolean
+collection_load_items_sync (GSecretCollection *self,
+                            GCancellable *cancellable,
+                            GError **error)
+{
+       GSecretItem *item;
+       GHashTable *items;
+       GVariant *paths;
+       GVariantIter iter;
+       const gchar *path;
+       gboolean ret = TRUE;
+
+       paths = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Items");
+       g_return_val_if_fail (paths != NULL, FALSE);
+
+       items = items_table_new ();
+
+       g_variant_iter_init (&iter, paths);
+       while (g_variant_iter_next (&iter, "&o", &path)) {
+               item = collection_lookup_item (self, path);
+
+               /* No such collection yet create a new one */
+               if (item == NULL) {
+                       item = gsecret_item_new_sync (self->pv->service, path,
+                                                     cancellable, error);
+                       if (item == NULL) {
+                               ret = FALSE;
+                               break;
+                       }
                }
 
+               g_hash_table_insert (items, g_strdup (path), item);
        }
 
-       if (closure->items_loading == 0)
-               load_items_complete (res);
+       if (ret)
+               collection_update_items (self, items);
+
+       g_hash_table_unref (items);
+       g_variant_unref (paths);
+       return ret;
 }
 
 static void
@@ -298,8 +364,6 @@ handle_property_changed (GSecretCollection *self,
                          const gchar *property_name,
                          GVariant *value)
 {
-       GSimpleAsyncResult *res;
-
        if (g_str_equal (property_name, "Label"))
                g_object_notify (G_OBJECT (self), "label");
 
@@ -312,24 +376,8 @@ handle_property_changed (GSecretCollection *self,
        else if (g_str_equal (property_name, "Modified"))
                g_object_notify (G_OBJECT (self), "modified");
 
-       else if (g_str_equal (property_name, "Items") && !self->pv->constructing) {
-               res = load_result_new (self->pv->cancellable, NULL, NULL);
-
-               if (value == NULL)
-                       value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Items");
-               else
-                       g_variant_ref (value);
-               if (value == NULL) {
-                       g_warning ("couldn't retrieve Collection Items property");
-                       g_simple_async_result_complete (res);
-               } else {
-                       // TODO: yyyy;
-                       load_items_perform (self, res, value);
-                       g_variant_unref (value);
-               }
-
-               g_object_unref (res);
-       }
+       else if (g_str_equal (property_name, "Items") && !self->pv->constructing)
+               collection_load_items_async (self, self->pv->cancellable, NULL, NULL);
 }
 
 static void
@@ -392,49 +440,147 @@ gsecret_collection_class_init (GSecretCollectionClass *klass)
        g_type_class_add_private (gobject_class, sizeof (GSecretCollectionPrivate));
 }
 
-static void
-on_collection_new (GObject *source,
-                   GAsyncResult *result,
-                   gpointer user_data)
+static gboolean
+gsecret_collection_initable_init (GInitable *initable,
+                                  GCancellable *cancellable,
+                                  GError **error)
 {
-       GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
        GSecretCollection *self;
-       GObject *source_object;
-       GError *error = NULL;
-       GVariant *item_paths;
-       GObject *object;
        GDBusProxy *proxy;
 
-       source_object = g_async_result_get_source_object (result);
-       object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
-                                             result, &error);
-       g_object_unref (source_object);
+       if (!gsecret_collection_initable_parent_iface->init (initable, cancellable, error))
+               return FALSE;
 
-       proxy = G_DBUS_PROXY (object);
-       if (error == NULL && !_gsecret_util_have_cached_properties (proxy)) {
-               g_set_error (&error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD,
-                            "No such secret collection at path: %s", g_dbus_proxy_get_object_path (proxy));
+       proxy = G_DBUS_PROXY (initable);
+
+       if (!_gsecret_util_have_cached_properties (proxy)) {
+               g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD,
+                            "No such secret collection at path: %s",
+                            g_dbus_proxy_get_object_path (proxy));
+               return FALSE;
        }
 
-       if (error == NULL) {
-               self = GSECRET_COLLECTION (object);
-               self->pv->constructing = FALSE;
+       self = GSECRET_COLLECTION (initable);
 
-               item_paths = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (object), "Items");
-               g_return_if_fail (item_paths != NULL);
-               // TODO: yyyy;
-               load_items_perform (self, res, item_paths);
-               g_variant_unref (item_paths);
+       if (!collection_load_items_sync (self, cancellable, error))
+               return FALSE;
 
-       } else {
+       return TRUE;
+}
+
+static void
+gsecret_collection_initable_iface (GInitableIface *iface)
+{
+       gsecret_collection_initable_parent_iface = g_type_interface_peek_parent (iface);
+
+       iface->init = gsecret_collection_initable_init;
+}
+
+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 void
+on_init_items (GObject *source,
+               GAsyncResult *result,
+               gpointer user_data)
+{
+       GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+       GSecretCollection *self = GSECRET_COLLECTION (source);
+       GError *error = NULL;
+
+       if (!collection_load_items_finish (self, result, &error))
                g_simple_async_result_take_error (res, error);
+
+       g_simple_async_result_complete (res);
+       g_object_unref (res);
+}
+
+static void
+on_init_base (GObject *source,
+              GAsyncResult *result,
+              gpointer user_data)
+{
+       GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+       GSecretCollection *self = GSECRET_COLLECTION (source);
+       InitClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
+       GDBusProxy *proxy = G_DBUS_PROXY (self);
+       GError *error = NULL;
+
+       if (!gsecret_collection_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 (!_gsecret_util_have_cached_properties (proxy)) {
+               g_simple_async_result_set_error (res, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD,
+                                                "No such secret collection at path: %s",
+                                                g_dbus_proxy_get_object_path (proxy));
                g_simple_async_result_complete (res);
+
+       } else {
+               collection_load_items_async (self, closure->cancellable,
+                                            on_init_items, g_object_ref (res));
        }
 
-       g_clear_object (&object);
        g_object_unref (res);
 }
 
+static void
+gsecret_collection_async_initable_init_async (GAsyncInitable *initable,
+                                              int io_priority,
+                                              GCancellable *cancellable,
+                                              GAsyncReadyCallback callback,
+                                              gpointer user_data)
+{
+       GSimpleAsyncResult *res;
+       InitClosure *closure;
+
+       res = g_simple_async_result_new (G_OBJECT (initable), callback, user_data,
+                                        gsecret_collection_async_initable_init_async);
+       closure = g_slice_new0 (InitClosure);
+       closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+       g_simple_async_result_set_op_res_gpointer (res, closure, init_closure_free);
+
+       gsecret_collection_async_initable_parent_iface->init_async (initable, io_priority,
+                                                                   cancellable,
+                                                                   on_init_base,
+                                                                   g_object_ref (res));
+
+       g_object_unref (res);
+}
+
+static gboolean
+gsecret_collection_async_initable_init_finish (GAsyncInitable *initable,
+                                               GAsyncResult *result,
+                                               GError **error)
+{
+       g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (initable),
+                             gsecret_collection_async_initable_init_async), FALSE);
+
+       if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+               return FALSE;
+
+       return TRUE;
+}
+
+static void
+gsecret_collection_async_initable_iface (GAsyncInitableIface *iface)
+{
+       gsecret_collection_async_initable_parent_iface = g_type_interface_peek_parent (iface);
+
+       iface->init_async = gsecret_collection_async_initable_init_async;
+       iface->init_finish = gsecret_collection_async_initable_init_finish;
+}
+
 void
 gsecret_collection_new (GSecretService *service,
                         const gchar *collection_path,
@@ -442,22 +588,16 @@ gsecret_collection_new (GSecretService *service,
                         GAsyncReadyCallback callback,
                         gpointer user_data)
 {
-       GSimpleAsyncResult *res;
        GDBusProxy *proxy;
 
        g_return_if_fail (GSECRET_IS_SERVICE (service));
        g_return_if_fail (collection_path != NULL);
        g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
 
-       res = load_result_new (cancellable, callback, user_data);
        proxy = G_DBUS_PROXY (service);
 
        g_async_initable_new_async (GSECRET_SERVICE_GET_CLASS (service)->collection_gtype,
-                                   G_PRIORITY_DEFAULT,
-                                   cancellable,
-                                   // TODO: zzzz;
-                                   on_collection_new,
-                                   g_object_ref (res),
+                                   G_PRIORITY_DEFAULT, cancellable, callback, user_data,
                                    "g-flags", G_DBUS_CALL_FLAGS_NONE,
                                    "g-interface-info", _gsecret_gen_collection_interface_info (),
                                    "g-name", g_dbus_proxy_get_name (proxy),
@@ -466,27 +606,27 @@ gsecret_collection_new (GSecretService *service,
                                    "g-interface-name", GSECRET_COLLECTION_INTERFACE,
                                    "service", service,
                                    NULL);
-
-       g_object_unref (res);
 }
 
 GSecretCollection *
 gsecret_collection_new_finish (GAsyncResult *result,
                                GError **error)
 {
-       GSimpleAsyncResult *res;
-       LoadClosure *closure;
+       GObject *source_object;
+       GObject *object;
 
-       g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL, load_result_new), NULL);
+       g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
        g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-       res = G_SIMPLE_ASYNC_RESULT (result);
+       source_object = g_async_result_get_source_object (result);
+       object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
+                                             result, error);
+       g_object_unref (source_object);
 
-       if (g_simple_async_result_propagate_error (res, error))
+       if (object == NULL)
                return NULL;
 
-       closure = g_simple_async_result_get_op_res_gpointer (res);
-       return g_object_ref (closure->collection);
+       return GSECRET_COLLECTION (object);
 }
 
 GSecretCollection *
@@ -495,29 +635,25 @@ gsecret_collection_new_sync (GSecretService *service,
                              GCancellable *cancellable,
                              GError **error)
 {
-       GSecretSync *sync;
-       GSecretCollection *collection;
+       GDBusProxy *proxy;
 
        g_return_val_if_fail (GSECRET_IS_SERVICE (service), NULL);
        g_return_val_if_fail (collection_path != NULL, NULL);
        g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
        g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-       sync = _gsecret_sync_new ();
-       g_main_context_push_thread_default (sync->context);
-
-       // TODO: xxxxx;
-       gsecret_collection_new (service, collection_path, cancellable,
-                               _gsecret_sync_on_result, sync);
-
-       g_main_loop_run (sync->loop);
-
-       collection = gsecret_collection_new_finish (sync->result, error);
-
-       g_main_context_pop_thread_default (sync->context);
-       _gsecret_sync_free (sync);
+       proxy = G_DBUS_PROXY (service);
 
-       return collection;
+       return g_initable_new (GSECRET_SERVICE_GET_CLASS (service)->collection_gtype,
+                              cancellable, error,
+                              "g-flags", G_DBUS_CALL_FLAGS_NONE,
+                              "g-interface-info", _gsecret_gen_collection_interface_info (),
+                              "g-name", g_dbus_proxy_get_name (proxy),
+                              "g-connection", g_dbus_proxy_get_connection (proxy),
+                              "g-object-path", collection_path,
+                              "g-interface-name", GSECRET_COLLECTION_INTERFACE,
+                              "service", service,
+                              NULL);
 }
 
 void
diff --git a/library/gsecret-enum-types.c.template b/library/gsecret-enum-types.c.template
new file mode 100644 (file)
index 0000000..5560289
--- /dev/null
@@ -0,0 +1,43 @@
+/*** BEGIN file-header ***/
+
+#include <glib-object.h>
+
+#ifndef GSECRET_COMPILATION
+#define GSECRET_COMPILATION
+#endif
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+#include "@filename@"
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name@_get_type (void) G_GNUC_CONST;
+
+GType
+@enum_name@_get_type (void)
+{
+    static GType etype = 0;
+    if (G_UNLIKELY(etype == 0)) {
+        static const G@Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+            { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+            { 0, NULL, NULL }
+        };
+        etype = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
+    }
+    return etype;
+}
+
+/*** END value-tail ***/
+
+/*** BEGIN file-tail ***/
+ /**/
+/*** END file-tail ***/
diff --git a/library/gsecret-enum-types.h.template b/library/gsecret-enum-types.h.template
new file mode 100644 (file)
index 0000000..3ec8564
--- /dev/null
@@ -0,0 +1,28 @@
+/*** BEGIN file-header ***/
+#if !defined (__GSECRET_INSIDE_HEADER__) && !defined (GSECRET_COMPILATION)
+#error "Only <gsecret/gsecret.h> can be included directly."
+#endif
+
+#ifndef __GSECRET_ENUM_TYPES_H__
+#define __GSECRET_ENUM_TYPES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name@_get_type (void) G_GNUC_CONST;
+#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ())
+/*** END value-header ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+#endif /* __GSECRET_ENUM_TYPES_H__ */
+/*** END file-tail ***/
index 9522dbf..0190629 100644 (file)
@@ -37,7 +37,18 @@ typedef struct _GSecretItemPrivate {
        GCancellable *cancellable;
 } GSecretItemPrivate;
 
-G_DEFINE_TYPE (GSecretItem, gsecret_item, G_TYPE_DBUS_PROXY);
+static GInitableIface *gsecret_item_initable_parent_iface = NULL;
+
+static GAsyncInitableIface *gsecret_item_async_initable_parent_iface = NULL;
+
+static void   gsecret_item_initable_iface         (GInitableIface *iface);
+
+static void   gsecret_item_async_initable_iface   (GAsyncInitableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GSecretItem, gsecret_item, G_TYPE_DBUS_PROXY,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gsecret_item_initable_iface);
+                         G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, gsecret_item_async_initable_iface);
+);
 
 static void
 gsecret_item_init (GSecretItem *self)
@@ -248,20 +259,102 @@ gsecret_item_class_init (GSecretItemClass *klass)
        g_type_class_add_private (gobject_class, sizeof (GSecretItemPrivate));
 }
 
-#if 0
+static gboolean
+gsecret_item_initable_init (GInitable *initable,
+                            GCancellable *cancellable,
+                            GError **error)
+{
+       GDBusProxy *proxy;
+
+       if (!gsecret_item_initable_parent_iface->init (initable, cancellable, error))
+               return FALSE;
+
+       proxy = G_DBUS_PROXY (initable);
+
+       if (!_gsecret_util_have_cached_properties (proxy)) {
+               g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD,
+                            "No such secret item at path: %s",
+                            g_dbus_proxy_get_object_path (proxy));
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
 static void
-all_properties_changed (GSecretItem *item)
+gsecret_item_initable_iface (GInitableIface *iface)
 {
-       GObject *obj = G_OBJECT (item);
-       gchar **property_names;
-       guint i;
+       gsecret_item_initable_parent_iface = g_type_interface_peek_parent (iface);
 
-       property_names = g_dbus_proxy_get_cached_property_names (G_DBUS_PROXY (item));
-       for (i = 0; property_names != NULL && property_names[i] != NULL; i++)
-               handle_property_changed (obj, property_names[i]);
-       g_strfreev (property_names);
+       iface->init = gsecret_item_initable_init;
+}
+
+static void
+on_init_base (GObject *source,
+              GAsyncResult *result,
+              gpointer user_data)
+{
+       GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+       GSecretItem *self = GSECRET_ITEM (source);
+       GDBusProxy *proxy = G_DBUS_PROXY (self);
+       GError *error = NULL;
+
+       if (!gsecret_item_async_initable_parent_iface->init_finish (G_ASYNC_INITABLE (self),
+                                                                   result, &error)) {
+               g_simple_async_result_take_error (res, error);
+
+       } else if (!_gsecret_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);
+       g_object_unref (res);
+}
+
+static void
+gsecret_item_async_initable_init_async (GAsyncInitable *initable,
+                                        int io_priority,
+                                        GCancellable *cancellable,
+                                        GAsyncReadyCallback callback,
+                                        gpointer user_data)
+{
+       GSimpleAsyncResult *res;
+
+       res = g_simple_async_result_new (G_OBJECT (initable), callback, user_data,
+                                        gsecret_item_async_initable_init_async);
+
+       gsecret_item_async_initable_parent_iface->init_async (initable, io_priority,
+                                                             cancellable,
+                                                             on_init_base,
+                                                             g_object_ref (res));
+
+       g_object_unref (res);
+}
+
+static gboolean
+gsecret_item_async_initable_init_finish (GAsyncInitable *initable,
+                                         GAsyncResult *result,
+                                         GError **error)
+{
+       g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (initable),
+                             gsecret_item_async_initable_init_async), FALSE);
+
+       if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+               return FALSE;
+
+       return TRUE;
+}
+
+static void
+gsecret_item_async_initable_iface (GAsyncInitableIface *iface)
+{
+       gsecret_item_async_initable_parent_iface = g_type_interface_peek_parent (iface);
+
+       iface->init_async = gsecret_item_async_initable_init_async;
+       iface->init_finish = gsecret_item_async_initable_init_finish;
 }
-#endif
 
 void
 gsecret_item_new (GSecretService *service,
@@ -279,10 +372,7 @@ gsecret_item_new (GSecretService *service,
        proxy = G_DBUS_PROXY (service);
 
        g_async_initable_new_async (GSECRET_SERVICE_GET_CLASS (service)->item_gtype,
-                                   G_PRIORITY_DEFAULT,
-                                   cancellable,
-                                   callback,
-                                   user_data,
+                                   G_PRIORITY_DEFAULT, cancellable, callback, user_data,
                                    "g-flags", G_DBUS_CALL_FLAGS_NONE,
                                    "g-interface-info", _gsecret_gen_item_interface_info (),
                                    "g-name", g_dbus_proxy_get_name (proxy),
@@ -299,7 +389,6 @@ gsecret_item_new_finish (GAsyncResult *result,
 {
        GObject *object;
        GObject *source_object;
-       GDBusProxy *proxy;
 
        source_object = g_async_result_get_source_object (result);
        object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
@@ -309,14 +398,6 @@ gsecret_item_new_finish (GAsyncResult *result,
        if (object == NULL)
                return NULL;
 
-       proxy = G_DBUS_PROXY (object);
-       if (!_gsecret_util_have_cached_properties (proxy)) {
-               g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD,
-                            "No such secret item at path: %s", g_dbus_proxy_get_object_path (proxy));
-               g_object_unref (object);
-               return NULL;
-       }
-
        return GSECRET_ITEM (object);
 }
 
@@ -326,29 +407,25 @@ gsecret_item_new_sync (GSecretService *service,
                        GCancellable *cancellable,
                        GError **error)
 {
-       GSecretSync *sync;
-       GSecretItem *item;
+       GDBusProxy *proxy;
 
        g_return_val_if_fail (GSECRET_IS_SERVICE (service), NULL);
        g_return_val_if_fail (item_path != NULL, NULL);
        g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
        g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-       sync = _gsecret_sync_new ();
-       g_main_context_push_thread_default (sync->context);
-
-       // TODO: xxxxx;
-       gsecret_item_new (service, item_path, cancellable,
-                         _gsecret_sync_on_result, sync);
-
-       g_main_loop_run (sync->loop);
-
-       item = gsecret_item_new_finish (sync->result, error);
-
-       g_main_context_pop_thread_default (sync->context);
-       _gsecret_sync_free (sync);
+       proxy = G_DBUS_PROXY (service);
 
-       return item;
+       return g_initable_new (GSECRET_SERVICE_GET_CLASS (service)->item_gtype,
+                              cancellable, error,
+                              "g-flags", G_DBUS_CALL_FLAGS_NONE,
+                              "g-interface-info", _gsecret_gen_item_interface_info (),
+                              "g-name", g_dbus_proxy_get_name (proxy),
+                              "g-connection", g_dbus_proxy_get_connection (proxy),
+                              "g-object-path", item_path,
+                              "g-interface-name", GSECRET_ITEM_INTERFACE,
+                              "service", service,
+                              NULL);
 }
 
 void
index 1629c73..08ac755 100644 (file)
@@ -68,7 +68,7 @@ on_store_connected (GObject *source,
        GSecretService *service;
        GError *error = NULL;
 
-       service = _gsecret_service_bare_connect_finish (result, &error);
+       service = gsecret_service_get_finish (result, &error);
        if (error == NULL) {
                gsecret_service_storev (service, closure->schema,
                                        closure->attributes,
@@ -147,8 +147,8 @@ gsecret_password_storev (const GSecretSchema *schema,
        closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
        g_simple_async_result_set_op_res_gpointer (res, closure, store_closure_free);
 
-       _gsecret_service_bare_connect (NULL, TRUE, cancellable,
-                                      on_store_connected, g_object_ref (res));
+       gsecret_service_get (GSECRET_SERVICE_OPEN_SESSION, cancellable,
+                            on_store_connected, g_object_ref (res));
 
        g_object_unref (res);
 }
@@ -306,7 +306,7 @@ on_lookup_connected (GObject *source,
        GSecretService *service;
        GError *error = NULL;
 
-       service = _gsecret_service_bare_connect_finish (result, &error);
+       service = gsecret_service_get_finish (result, &error);
        if (error != NULL) {
                g_simple_async_result_take_error (res, error);
                g_simple_async_result_complete (res);
@@ -338,8 +338,8 @@ gsecret_password_lookupv (GHashTable *attributes,
        closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
        g_simple_async_result_set_op_res_gpointer (res, closure, lookup_closure_free);
 
-       _gsecret_service_bare_connect (NULL, TRUE, cancellable,
-                                      on_lookup_connected, g_object_ref (res));
+       gsecret_service_get (GSECRET_SERVICE_OPEN_SESSION, cancellable,
+                            on_lookup_connected, g_object_ref (res));
 
        g_object_unref (res);
 }
@@ -490,7 +490,7 @@ on_delete_connect (GObject *source,
        GSecretService *service;
        GError *error = NULL;
 
-       service = _gsecret_service_bare_connect_finish (result, &error);
+       service = gsecret_service_get_finish (result, &error);
        if (error == NULL) {
                gsecret_service_removev (service, closure->attributes,
                                         closure->cancellable, on_delete_complete,
@@ -524,9 +524,8 @@ gsecret_password_removev (GHashTable *attributes,
        closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
        g_simple_async_result_set_op_res_gpointer (res, closure, delete_closure_free);
 
-       _gsecret_service_bare_connect (NULL, FALSE, cancellable,
-                                      on_delete_connect,
-                                      g_object_ref (res));
+       gsecret_service_get (GSECRET_SERVICE_NONE, cancellable,
+                            on_delete_connect, g_object_ref (res));
 
        g_object_unref (res);
 }
index 8ebf4b0..1a74f35 100644 (file)
@@ -99,6 +99,7 @@ gboolean            _gsecret_util_have_cached_properties       (GDBusProxy *prox
 
 void                _gsecret_service_set_default_bus_name      (const gchar *bus_name);
 
+#if 0
 GSecretService *    _gsecret_service_bare_instance    (GDBusConnection *connection,
                                                        const gchar *bus_name);
 
@@ -110,6 +111,7 @@ void                _gsecret_service_bare_connect              (const gchar *bus
 
 GSecretService *    _gsecret_service_bare_connect_finish       (GAsyncResult *result,
                                                                 GError **error);
+#endif
 
 GSecretSession *    _gsecret_service_get_session               (GSecretService *self);
 
@@ -135,7 +137,7 @@ void                _gsecret_session_open                      (GSecretService *
                                                                 GAsyncReadyCallback callback,
                                                                 gpointer user_data);
 
-GSecretSession *    _gsecret_session_open_finish               (GAsyncResult *result,
+gboolean            _gsecret_session_open_finish               (GAsyncResult *result,
                                                                 GError **error);
 
 GVariant *          _gsecret_session_encode_secret             (GSecretSession *session,
index be26d88..1cbc5ac 100644 (file)
@@ -15,6 +15,7 @@
 
 #include "gsecret-collection.h"
 #include "gsecret-dbus-generated.h"
+#include "gsecret-enum-types.h"
 #include "gsecret-item.h"
 #include "gsecret-private.h"
 #include "gsecret-service.h"
@@ -36,12 +37,14 @@ static const gchar *default_bus_name = GSECRET_SERVICE_BUS_NAME;
 
 enum {
        PROP_0,
+       PROP_FLAGS,
        PROP_COLLECTIONS
 };
 
 typedef struct _GSecretServicePrivate {
        /* No change between construct and finalize */
        GCancellable *cancellable;
+       GSecretServiceFlags init_flags;
 
        /* Locked by mutex */
        GMutex mutex;
@@ -52,14 +55,18 @@ typedef struct _GSecretServicePrivate {
 G_LOCK_DEFINE (service_instance);
 static gpointer service_instance = NULL;
 
-G_DEFINE_TYPE (GSecretService, gsecret_service, G_TYPE_DBUS_PROXY);
+static GInitableIface *gsecret_service_initable_parent_iface = NULL;
 
-static GHashTable *
-collections_table_new (void)
-{
-       return g_hash_table_new_full (g_str_hash, g_str_equal,
-                                     g_free, g_object_unref);
-}
+static GAsyncInitableIface *gsecret_service_async_initable_parent_iface = NULL;
+
+static void   gsecret_service_initable_iface         (GInitableIface *iface);
+
+static void   gsecret_service_async_initable_iface   (GAsyncInitableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GSecretService, gsecret_service, G_TYPE_DBUS_PROXY,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gsecret_service_initable_iface);
+                         G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, gsecret_service_async_initable_iface);
+);
 
 static void
 gsecret_service_init (GSecretService *self)
@@ -69,7 +76,6 @@ gsecret_service_init (GSecretService *self)
 
        g_mutex_init (&self->pv->mutex);
        self->pv->cancellable = g_cancellable_new ();
-       self->pv->collections = collections_table_new ();
 }
 
 static void
@@ -81,6 +87,9 @@ gsecret_service_get_property (GObject *obj,
        GSecretService *self = GSECRET_SERVICE (obj);
 
        switch (prop_id) {
+       case PROP_FLAGS:
+               g_value_set_flags (value, gsecret_service_get_flags (self));
+               break;
        case PROP_COLLECTIONS:
                g_value_take_boxed (value, gsecret_service_get_collections (self));
                break;
@@ -91,6 +100,24 @@ gsecret_service_get_property (GObject *obj,
 }
 
 static void
+gsecret_service_set_property (GObject *obj,
+                              guint prop_id,
+                              const GValue *value,
+                              GParamSpec *pspec)
+{
+       GSecretService *self = GSECRET_SERVICE (obj);
+
+       switch (prop_id) {
+       case PROP_FLAGS:
+               self->pv->init_flags = g_value_get_flags (value);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+               break;
+       }
+}
+
+static void
 gsecret_service_dispose (GObject *obj)
 {
        GSecretService *self = GSECRET_SERVICE (obj);
@@ -106,7 +133,8 @@ gsecret_service_finalize (GObject *obj)
        GSecretService *self = GSECRET_SERVICE (obj);
 
        _gsecret_session_free (self->pv->session);
-       g_hash_table_destroy (self->pv->collections);
+       if (self->pv->collections)
+               g_hash_table_destroy (self->pv->collections);
        g_clear_object (&self->pv->cancellable);
 
        G_OBJECT_CLASS (gsecret_service_parent_class)->finalize (obj);
@@ -171,152 +199,21 @@ gsecret_service_real_prompt_finish (GSecretService *self,
        return g_simple_async_result_get_op_res_gboolean (res);
 }
 
-typedef struct {
-       GCancellable *cancellable;
-       GHashTable *collections;
-       gint collections_loading;
-} LoadClosure;
-
-static void
-load_closure_free (gpointer data)
-{
-       LoadClosure *closure = data;
-       g_clear_object (&closure->cancellable);
-       g_hash_table_unref (closure->collections);
-       g_slice_free (LoadClosure, closure);
-}
-
-static GSimpleAsyncResult *
-load_result_new (GCancellable *cancellable,
-                 GAsyncReadyCallback callback,
-                 gpointer user_data)
-{
-       GSimpleAsyncResult *res;
-       LoadClosure *closure;
-
-       res = g_simple_async_result_new (NULL, callback, user_data, load_result_new);
-       closure = g_slice_new (LoadClosure);
-       closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
-       closure->collections = collections_table_new ();
-       g_simple_async_result_set_op_res_gpointer (res, closure, load_closure_free);
-
-       return res;
-}
-
-static void
-load_collections_complete (GSecretService *self,
-                           GSimpleAsyncResult *res)
-{
-       LoadClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
-       GHashTable *collections;
-
-       g_assert (closure->collections_loading == 0);
-
-       g_hash_table_ref (closure->collections);
-
-       g_mutex_lock (&self->pv->mutex);
-       collections = self->pv->collections;
-       self->pv->collections = closure->collections;
-       g_mutex_unlock (&self->pv->mutex);
-
-       g_hash_table_unref (collections);
-
-       g_simple_async_result_complete (res);
-}
-
-static void
-on_collection_loading (GObject *source,
-                       GAsyncResult *result,
-                       gpointer user_data)
-{
-       GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
-       GSecretService *self = GSECRET_SERVICE (g_async_result_get_source_object (user_data));
-       LoadClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
-       GSecretCollection *collection;
-       const gchar *collection_path;
-       GError *error = NULL;
-
-       closure->collections_loading--;
-
-       collection = gsecret_collection_new_finish (result, &error);
-
-       if (error != NULL)
-               g_simple_async_result_take_error (res, error);
-
-       if (collection != NULL) {
-               collection_path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (self));
-               g_hash_table_insert (closure->collections, g_strdup (collection_path), collection);
-       }
-
-       if (closure->collections_loading == 0)
-               load_collections_complete (self, res);
-
-       g_object_unref (self);
-       g_object_unref (res);
-}
-
-static void
-load_collections_perform (GSecretService *self,
-                          GSimpleAsyncResult *res,
-                          GVariant *collection_paths)
-{
-       LoadClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
-       GSecretCollection *collection;
-       gchar *collection_path;
-       GVariantIter iter;
-
-       g_variant_iter_init (&iter, collection_paths);
-       while (g_variant_iter_loop (&iter, "o", &collection_path)) {
-
-               /* Lookup the collection */
-               g_mutex_lock (&self->pv->mutex);
-               collection = g_hash_table_lookup (self->pv->collections, collection_path);
-               if (collection != NULL)
-                       g_object_ref (collection);
-               g_mutex_unlock (&self->pv->mutex);
-
-               if (collection == NULL) {
-                       // TODO: xxxxx;
-                       gsecret_collection_new (self, collection_path, closure->cancellable,
-                                               on_collection_loading, g_object_ref (res));
-                       closure->collections_loading++;
-               } else {
-                       g_hash_table_insert (closure->collections,
-                                            g_strdup (collection_path),
-                                            collection);
-               }
-       }
-
-       if (closure->collections_loading == 0)
-               load_collections_complete (self, res);
-
-       g_variant_unref (collection_paths);
-}
-
 static void
 handle_property_changed (GSecretService *self,
                          const gchar *property_name,
                          GVariant *value)
 {
-       GSimpleAsyncResult *res;
+       gboolean perform;
 
        if (g_str_equal (property_name, "Collections")) {
-               res = load_result_new (self->pv->cancellable, NULL, NULL);
-
-               if (value == NULL)
-                       value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Collections");
-               else
-                       g_variant_ref (value);
-               if (value == NULL) {
-                       g_warning ("couldn't retrieve Service Collections property");
-                       g_simple_async_result_complete (res);
-               } else {
-                       // TODO: yyyy;
-                       load_collections_perform (self, res, value);
-                       g_variant_unref (value);
-               }
 
-               g_object_unref (res);
+               g_mutex_lock (&self->pv->mutex);
+               perform = self->pv->collections != NULL;
+               g_mutex_unlock (&self->pv->mutex);
+
+               if (perform)
+                       gsecret_service_ensure_collections (self, self->pv->cancellable, NULL, NULL);
        }
 }
 
@@ -346,6 +243,7 @@ gsecret_service_class_init (GSecretServiceClass *klass)
        GDBusProxyClass *proxy_class = G_DBUS_PROXY_CLASS (klass);
 
        object_class->get_property = gsecret_service_get_property;
+       object_class->set_property = gsecret_service_set_property;
        object_class->dispose = gsecret_service_dispose;
        object_class->finalize = gsecret_service_finalize;
 
@@ -358,313 +256,417 @@ gsecret_service_class_init (GSecretServiceClass *klass)
        klass->item_gtype = GSECRET_TYPE_ITEM;
        klass->collection_gtype = GSECRET_TYPE_COLLECTION;
 
+       g_object_class_install_property (object_class, PROP_FLAGS,
+                    g_param_spec_flags ("flags", "Flags", "Service flags",
+                                        g_secret_service_flags_get_type (), GSECRET_SERVICE_NONE,
+                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (object_class, PROP_COLLECTIONS,
+                    g_param_spec_boxed ("collections", "Collections", "Secret Service Collections",
+                                        _gsecret_list_get_type (), G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
        g_type_class_add_private (klass, sizeof (GSecretServicePrivate));
 }
 
-void
-_gsecret_service_set_default_bus_name (const gchar *bus_name)
+typedef struct {
+       GCancellable *cancellable;
+       GSecretServiceFlags flags;
+} InitClosure;
+
+static void
+init_closure_free (gpointer data)
 {
-       g_return_if_fail (bus_name != NULL);
-       default_bus_name = bus_name;
+       InitClosure *closure = data;
+       g_clear_object (&closure->cancellable);
+       g_slice_free (InitClosure, closure);
 }
 
-static void
-on_service_instance_gone (gpointer user_data,
-                          GObject *where_the_object_was)
+static gboolean
+service_ensure_for_flags_sync (GSecretService *self,
+                               GSecretServiceFlags flags,
+                               GCancellable *cancellable,
+                               GError **error)
 {
-       G_LOCK (service_instance);
+       if (flags & GSECRET_SERVICE_OPEN_SESSION)
+               if (!gsecret_service_ensure_session_sync (self, cancellable, error))
+                       return FALSE;
 
-               g_assert (service_instance == where_the_object_was);
-               service_instance = NULL;
+       if (flags & GSECRET_SERVICE_LOAD_COLLECTIONS)
+               if (!gsecret_service_ensure_collections_sync (self, cancellable, error))
+                       return FALSE;
 
-       G_UNLOCK (service_instance);
+       return TRUE;
 }
 
-GSecretService *
-_gsecret_service_bare_instance (GDBusConnection *connection,
-                                const gchar *bus_name)
+static void
+on_ensure_collections (GObject *source,
+                       GAsyncResult *result,
+                       gpointer user_data)
 {
-       GSecretService *service = NULL;
+       GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+       GSecretService *self = GSECRET_SERVICE (source);
        GError *error = NULL;
 
-       g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
-
-       G_LOCK (service_instance);
-
-               if (service_instance != NULL)
-                       service = g_object_ref (service_instance);
+       if (!gsecret_service_ensure_collections_finish (self, result, &error))
+               g_simple_async_result_take_error (res, error);
 
-       G_UNLOCK (service_instance);
+       g_simple_async_result_complete (res);
+       g_object_unref (res);
+}
 
-       if (service != NULL)
-               return service;
+static void
+on_ensure_session (GObject *source,
+                   GAsyncResult *result,
+                   gpointer user_data)
+{
+       GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+       InitClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
+       GSecretService *self = GSECRET_SERVICE (source);
+       GError *error = NULL;
 
-       /* Alternate bus name is only used for testing */
-       if (bus_name == NULL)
-               bus_name = default_bus_name;
+       if (!gsecret_service_ensure_session_finish (self, result, &error)) {
+               g_simple_async_result_take_error (res, error);
+               g_simple_async_result_complete (res);
 
-       service = g_initable_new (GSECRET_TYPE_SERVICE, NULL, &error,
-                                 "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
-                                 "g-interface-info", _gsecret_gen_service_interface_info (),
-                                 "g-name", bus_name,
-                                 "g-connection", connection,
-                                 "g-object-path", GSECRET_SERVICE_PATH,
-                                 "g-interface-name", GSECRET_SERVICE_INTERFACE,
-                                 NULL);
+       } else if (closure->flags & GSECRET_SERVICE_LOAD_COLLECTIONS) {
+               gsecret_service_ensure_collections (self, closure->cancellable,
+                                                   on_ensure_collections, g_object_ref (res));
 
-       if (error != NULL) {
-               g_warning ("couldn't create GSecretService object: %s", error->message);
-               g_clear_error (&error);
-               return NULL;
+       } else {
+               g_simple_async_result_complete_in_idle (res);
        }
 
-       g_assert (GSECRET_IS_SERVICE (service));
+       g_object_unref (res);
+}
 
-       G_LOCK (service_instance);
+static void
+service_ensure_for_flags_async (GSecretService *self,
+                                GSecretServiceFlags flags,
+                                GSimpleAsyncResult *res)
+{
+       InitClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
 
-               if (service_instance == NULL) {
-                       service_instance = service;
-                       g_object_weak_ref (G_OBJECT (service), on_service_instance_gone, NULL);
-               } else {
-                       g_object_unref (service);
-                       service = g_object_ref (service_instance);
-               }
+       closure->flags = flags;
 
-       G_UNLOCK (service_instance);
+       if (closure->flags & GSECRET_SERVICE_OPEN_SESSION)
+               gsecret_service_ensure_session (self, closure->cancellable,
+                                               on_ensure_session, g_object_ref (res));
 
-       return service;
-}
+       else if (closure->flags & GSECRET_SERVICE_LOAD_COLLECTIONS)
+               gsecret_service_ensure_collections (self, closure->cancellable,
+                                                   on_ensure_collections, g_object_ref (res));
 
-typedef struct {
-       GCancellable *cancellable;
-       GSecretService *service;
-       gboolean ensure_session;
-       gchar *bus_name;
-} ConnectClosure;
+       else
+               g_simple_async_result_complete_in_idle (res);
+}
 
-static void
-connect_closure_free (gpointer data)
+static gboolean
+gsecret_service_initable_init (GInitable *initable,
+                               GCancellable *cancellable,
+                               GError **error)
 {
-       ConnectClosure *closure = data;
-       g_clear_object (&closure->cancellable);
-       g_clear_object (&closure->service);
-       g_slice_free (ConnectClosure, closure);
+       GSecretService *self;
+
+       if (!gsecret_service_initable_parent_iface->init (initable, cancellable, error))
+               return FALSE;
+
+       self = GSECRET_SERVICE (initable);
+       return service_ensure_for_flags_sync (self, self->pv->init_flags, cancellable, error);
 }
 
 static void
-on_connect_ensure (GObject *source,
-                   GAsyncResult *result,
-                   gpointer user_data)
+gsecret_service_initable_iface (GInitableIface *iface)
 {
-       GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
-       GError *error = NULL;
-
-       gsecret_service_ensure_session_finish (GSECRET_SERVICE (source), result, &error);
-       if (error != NULL)
-               g_simple_async_result_take_error (res, error);
+       gsecret_service_initable_parent_iface = g_type_interface_peek_parent (iface);
 
-       g_simple_async_result_complete (res);
-       g_object_unref (res);
+       iface->init = gsecret_service_initable_init;
 }
 
 static void
-on_connect_bus (GObject *source,
-                GAsyncResult *result,
-                gpointer user_data)
+on_init_base (GObject *source,
+              GAsyncResult *result,
+              gpointer user_data)
 {
        GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
-       ConnectClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
-       GDBusConnection *connection;
+       GSecretService *self = GSECRET_SERVICE (source);
        GError *error = NULL;
 
-       connection = g_bus_get_finish (result, &error);
-       if (error == NULL) {
-               closure->service = _gsecret_service_bare_instance (connection, closure->bus_name);
-               if (closure->ensure_session)
-                       gsecret_service_ensure_session (closure->service, closure->cancellable,
-                                                       on_connect_ensure, g_object_ref (res));
-
-               else
-                       g_simple_async_result_complete (res);
-
-               g_object_unref (connection);
-
-       } else {
+       if (!gsecret_service_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);
        }
 
+       service_ensure_for_flags_async (self, self->pv->init_flags, res);
        g_object_unref (res);
 }
 
-void
-_gsecret_service_bare_connect (const gchar *bus_name,
-                               gboolean ensure_session,
-                               GCancellable *cancellable,
-                               GAsyncReadyCallback callback,
-                               gpointer user_data)
+static void
+gsecret_service_async_initable_init_async (GAsyncInitable *initable,
+                                           int io_priority,
+                                           GCancellable *cancellable,
+                                           GAsyncReadyCallback callback,
+                                           gpointer user_data)
 {
        GSimpleAsyncResult *res;
-       ConnectClosure *closure;
-
-       g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
-
-       if (bus_name == NULL)
-               bus_name = default_bus_name;
+       InitClosure *closure;
 
-       res = g_simple_async_result_new (NULL, callback, user_data,
-                                        _gsecret_service_bare_connect);
-       closure = g_slice_new0 (ConnectClosure);
-       closure->bus_name = g_strdup (bus_name);
+       res = g_simple_async_result_new (G_OBJECT (initable), callback, user_data,
+                                      gsecret_service_async_initable_init_async);
+       closure = g_slice_new0 (InitClosure);
        closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
-       closure->ensure_session = ensure_session;
-       g_simple_async_result_set_op_res_gpointer (res, closure, connect_closure_free);
+       g_simple_async_result_set_op_res_gpointer (res, closure, init_closure_free);
 
-       g_bus_get (G_BUS_TYPE_SESSION, cancellable, on_connect_bus, g_object_ref (res));
+       gsecret_service_async_initable_parent_iface->init_async (initable, io_priority,
+                                                                cancellable,
+                                                                on_init_base,
+                                                                g_object_ref (res));
 
        g_object_unref (res);
 }
 
-GSecretService *
-_gsecret_service_bare_connect_finish (GAsyncResult *result,
-                                      GError **error)
+static gboolean
+gsecret_service_async_initable_init_finish (GAsyncInitable *initable,
+                                            GAsyncResult *result,
+                                            GError **error)
 {
-       ConnectClosure *closure;
-       GSimpleAsyncResult *res;
+       g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (initable),
+                             gsecret_service_async_initable_init_async), FALSE);
 
-       g_return_val_if_fail (error == NULL || *error == NULL, NULL);
-       g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL,
-                             _gsecret_service_bare_connect), NULL);
+       if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+               return FALSE;
 
-       res = G_SIMPLE_ASYNC_RESULT (result);
-       if (g_simple_async_result_propagate_error (res, error))
-               return NULL;
+       return TRUE;
+}
 
-       closure = g_simple_async_result_get_op_res_gpointer (res);
-       return g_object_ref (closure->service);
+static void
+gsecret_service_async_initable_iface (GAsyncInitableIface *iface)
+{
+       gsecret_service_async_initable_parent_iface = g_type_interface_peek_parent (iface);
+
+       iface->init_async = gsecret_service_async_initable_init_async;
+       iface->init_finish = gsecret_service_async_initable_init_finish;
 }
 
-typedef struct {
-       GCancellable *cancellable;
-       GSecretService *service;
-} ServiceClosure;
+void
+_gsecret_service_set_default_bus_name (const gchar *bus_name)
+{
+       g_return_if_fail (bus_name != NULL);
+       default_bus_name = bus_name;
+}
 
 static void
-service_closure_free (gpointer data)
+on_service_instance_gone (gpointer user_data,
+                          GObject *where_the_object_was)
 {
-       ServiceClosure *closure = data;
-       g_clear_object (&closure->cancellable);
-       g_clear_object (&closure->service);
-       g_slice_free (ServiceClosure, closure);
+       G_LOCK (service_instance);
+
+               g_assert (service_instance == where_the_object_was);
+               service_instance = NULL;
+
+       G_UNLOCK (service_instance);
 }
 
-static void
-on_service_properties (GObject *source,
-                       GAsyncResult *result,
-                       gpointer user_data)
+void
+gsecret_service_get (GSecretServiceFlags flags,
+                     GCancellable *cancellable,
+                     GAsyncReadyCallback callback,
+                     gpointer user_data)
 {
-       GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
-       GError *error = NULL;
+       GSecretService *service = NULL;
+       GSimpleAsyncResult *res;
+       InitClosure *closure;
 
-       _gsecret_util_get_properties_finish (G_DBUS_PROXY (source),
-                                            gsecret_service_get,
-                                            result, &error);
+       G_LOCK (service_instance);
+       if (service_instance != NULL)
+               service = g_object_ref (service_instance);
+       G_UNLOCK (service_instance);
 
-       if (error != NULL)
-               g_simple_async_result_take_error (res, error);
-       g_simple_async_result_complete (res);
+       /* Create a whole new service */
+       if (service == NULL) {
+               g_async_initable_new_async (GSECRET_TYPE_SERVICE, G_PRIORITY_DEFAULT,
+                                           cancellable, callback, user_data,
+                                           "g-flags", G_DBUS_PROXY_FLAGS_NONE,
+                                           "g-interface-info", _gsecret_gen_service_interface_info (),
+                                           "g-name", default_bus_name,
+                                           "g-bus-type", G_BUS_TYPE_SESSION,
+                                           "g-object-path", GSECRET_SERVICE_PATH,
+                                           "g-interface-name", GSECRET_SERVICE_INTERFACE,
+                                           "flags", flags,
+                                           NULL);
+
+       /* Just have to ensure that the service matches flags */
+       } else {
+               res = g_simple_async_result_new (G_OBJECT (service), callback,
+                                                user_data, gsecret_service_get);
+               closure = g_slice_new0 (InitClosure);
+               closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+               closure->flags = flags;
+               g_simple_async_result_set_op_res_gpointer (res, closure, init_closure_free);
 
-       g_object_unref (res);
+               service_ensure_for_flags_async (service, flags, res);
+
+               g_object_unref (res);
+       }
 }
 
-static void
-on_service_connected (GObject *source,
-                      GAsyncResult *result,
-                      gpointer user_data)
+GSecretService *
+gsecret_service_get_finish (GAsyncResult *result,
+                            GError **error)
 {
-       GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
-       ServiceClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
-       GError *error = NULL;
+       GObject *service = NULL;
+       GObject *source_object;
 
-       closure->service = _gsecret_service_bare_connect_finish (result, &error);
-       if (error != NULL)
-               g_simple_async_result_take_error (res, error);
+       g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+       g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-       _gsecret_util_get_properties (G_DBUS_PROXY (closure->service),
-                                     gsecret_service_get, closure->cancellable,
-                                     on_service_properties, g_object_ref (res));
+       source_object = g_async_result_get_source_object (result);
 
-       g_object_unref (res);
+       /* Just ensuring that the service matches flags */
+       if (g_simple_async_result_is_valid (result, source_object, gsecret_service_get)) {
+               if (!g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+                       service = g_object_ref (source_object);
+
+       /* Creating a whole new service */
+       } else {
+               service = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), result, error);
+       }
+
+       if (source_object)
+               g_object_unref (source_object);
+
+       if (service == NULL)
+               return NULL;
+
+       return GSECRET_SERVICE (service);
+}
+
+GSecretService *
+gsecret_service_get_sync (GSecretServiceFlags flags,
+                          GCancellable *cancellable,
+                          GError **error)
+{
+       GSecretService *service = NULL;
+
+       G_LOCK (service_instance);
+       if (service_instance != NULL)
+               service = g_object_ref (service_instance);
+       G_UNLOCK (service_instance);
+
+       if (service == NULL) {
+               service = g_initable_new (GSECRET_TYPE_SERVICE, cancellable, error,
+                                         "g-flags", G_DBUS_PROXY_FLAGS_NONE,
+                                         "g-interface-info", _gsecret_gen_service_interface_info (),
+                                         "g-name", default_bus_name,
+                                         "g-bus-type", G_BUS_TYPE_SESSION,
+                                         "g-object-path", GSECRET_SERVICE_PATH,
+                                         "g-interface-name", GSECRET_SERVICE_INTERFACE,
+                                         "flags", flags,
+                                         NULL);
+               if (service == NULL)
+                       return NULL;
+
+               G_LOCK (service_instance);
+               if (service_instance == NULL) {
+                       service_instance = service;
+                       g_object_weak_ref (G_OBJECT (service), on_service_instance_gone, NULL);
+               }
+               G_UNLOCK (service_instance);
+
+       } else {
+               if (!service_ensure_for_flags_sync (service, flags, cancellable, error)) {
+                       g_object_unref (service);
+                       return NULL;
+               }
+       }
+
+       return service;
 }
 
 void
-gsecret_service_get (GCancellable *cancellable,
+gsecret_service_new (const gchar *service_bus_name,
+                     GSecretServiceFlags flags,
+                     GCancellable *cancellable,
                      GAsyncReadyCallback callback,
                      gpointer user_data)
 {
-       GSimpleAsyncResult *res;
-       ServiceClosure *closure;
-
        g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
 
-       res = g_simple_async_result_new (NULL, callback, user_data,
-                                        gsecret_service_get);
-       closure = g_slice_new0 (ServiceClosure);
-       closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
-       g_simple_async_result_set_op_res_gpointer (res, closure, service_closure_free);
-
-       _gsecret_service_bare_connect (NULL, FALSE, cancellable,
-                                      on_service_connected, g_object_ref (res));
+       if (service_bus_name == NULL)
+               service_bus_name = default_bus_name;
 
-       g_object_unref (res);
+       g_async_initable_new_async (GSECRET_TYPE_SERVICE, G_PRIORITY_DEFAULT,
+                                   cancellable, callback, user_data,
+                                   "g-flags", G_DBUS_PROXY_FLAGS_NONE,
+                                   "g-interface-info", _gsecret_gen_service_interface_info (),
+                                   "g-name", service_bus_name,
+                                   "g-bus-type", G_BUS_TYPE_SESSION,
+                                   "g-object-path", GSECRET_SERVICE_PATH,
+                                   "g-interface-name", GSECRET_SERVICE_INTERFACE,
+                                   "flags", flags,
+                                   NULL);
 }
 
 GSecretService *
-gsecret_service_get_finish (GAsyncResult *result,
+gsecret_service_new_finish (GAsyncResult *result,
                             GError **error)
 {
-       GSimpleAsyncResult *res;
-       GSecretService *service;
+       GObject *source_object;
+       GObject *object;
 
        g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
        g_return_val_if_fail (error == NULL || *error == NULL, NULL);
-       g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL,
-                             gsecret_service_get), NULL);
 
-       res = G_SIMPLE_ASYNC_RESULT (result);
+       source_object = g_async_result_get_source_object (result);
+       object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
+                                             result, error);
+       g_object_unref (source_object);
 
-       if (g_simple_async_result_propagate_error (res, error))
+       if (object == NULL)
                return NULL;
 
-       service = g_simple_async_result_get_op_res_gpointer (res);
-       if (service != NULL)
-               g_object_ref (service);
-       return service;
+       return GSECRET_SERVICE (object);
 }
 
 GSecretService *
-gsecret_service_get_sync (GCancellable *cancellable,
+gsecret_service_new_sync (const gchar *service_bus_name,
+                          GSecretServiceFlags flags,
+                          GCancellable *cancellable,
                           GError **error)
 {
-       GSecretService *service;
-       GSecretSync *sync;
+       g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
 
-       g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
-       g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+       if (service_bus_name == NULL)
+               service_bus_name = default_bus_name;
 
-       sync = _gsecret_sync_new ();
-       g_main_context_push_thread_default (sync->context);
+       return g_initable_new (GSECRET_TYPE_SERVICE, cancellable, error,
+                              "g-flags", G_DBUS_PROXY_FLAGS_NONE,
+                              "g-interface-info", _gsecret_gen_service_interface_info (),
+                              "g-name", service_bus_name,
+                              "g-bus-type", G_BUS_TYPE_SESSION,
+                              "g-object-path", GSECRET_SERVICE_PATH,
+                              "g-interface-name", GSECRET_SERVICE_INTERFACE,
+                              "flags", flags,
+                              NULL);
+}
 
-       gsecret_service_get (cancellable, _gsecret_sync_on_result, sync);
+GSecretServiceFlags
+gsecret_service_get_flags (GSecretService *self)
+{
+       GSecretServiceFlags flags = 0;
 
-       g_main_loop_run (sync->loop);
+       g_return_val_if_fail (GSECRET_IS_SERVICE (self), GSECRET_SERVICE_NONE);
 
-       service = gsecret_service_get_finish (sync->result, error);
+       g_mutex_lock (&self->pv->mutex);
 
-       g_main_context_pop_thread_default (sync->context);
-       _gsecret_sync_free (sync);
+       if (self->pv->session)
+               flags |= GSECRET_SERVICE_OPEN_SESSION;
+       if (self->pv->collections)
+               flags |= GSECRET_SERVICE_LOAD_COLLECTIONS;
 
-       return service;
+       g_mutex_unlock (&self->pv->mutex);
+
+       return flags;
 }
 
 GList *
@@ -675,9 +677,16 @@ gsecret_service_get_collections (GSecretService *self)
        g_return_val_if_fail (GSECRET_IS_SERVICE (self), NULL);
 
        g_mutex_lock (&self->pv->mutex);
-       collections = g_hash_table_get_values (self->pv->collections);
-       for (l = collections; l != NULL; l = g_list_next (l))
-               g_object_ref (l->data);
+
+       if (self->pv->collections == NULL) {
+               collections = NULL;
+
+       } else {
+               collections = g_hash_table_get_values (self->pv->collections);
+               for (l = collections; l != NULL; l = g_list_next (l))
+                       g_object_ref (l->data);
+       }
+
        g_mutex_unlock (&self->pv->mutex);
 
        return collections;
@@ -719,7 +728,7 @@ _gsecret_service_get_session (GSecretService *self)
 
        g_mutex_lock (&self->pv->mutex);
        session = self->pv->session;
-       g_mutex_lock (&self->pv->mutex);
+       g_mutex_unlock (&self->pv->mutex);
 
        return session;
 }
@@ -815,7 +824,7 @@ gsecret_service_ensure_session_finish (GSecretService *self,
        }
 
        g_return_val_if_fail (self->pv->session != NULL, NULL);
-       return gsecret_service_get_session_path (self->pv->session);
+       return gsecret_service_get_session_path (self);
 }
 
 const gchar *
@@ -846,19 +855,146 @@ gsecret_service_ensure_session_sync (GSecretService *self,
        return path;
 }
 
-#if 0
+static GSecretCollection *
+service_lookup_collection (GSecretService *self,
+                           const gchar *path)
+{
+       GSecretCollection *collection = NULL;
+
+       g_mutex_lock (&self->pv->mutex);
+
+       if (self->pv->collections) {
+               collection = g_hash_table_lookup (self->pv->collections, path);
+               if (collection != NULL)
+                       g_object_ref (collection);
+       }
+
+       g_mutex_unlock (&self->pv->mutex);
+
+       return collection;
+}
+
+static void
+service_update_collections (GSecretService *self,
+                            GHashTable *collections)
+{
+       GHashTable *previous;
+
+       g_hash_table_ref (collections);
+
+       g_mutex_lock (&self->pv->mutex);
+
+       previous = self->pv->collections;
+       self->pv->collections = collections;
+
+       g_mutex_unlock (&self->pv->mutex);
+
+       if (previous != NULL)
+               g_hash_table_unref (previous);
+}
+
+typedef struct {
+       GCancellable *cancellable;
+       GHashTable *collections;
+       gint collections_loading;
+} EnsureClosure;
+
+static GHashTable *
+collections_table_new (void)
+{
+       return g_hash_table_new_full (g_str_hash, g_str_equal,
+                                     g_free, g_object_unref);
+}
+
+static void
+ensure_closure_free (gpointer data)
+{
+       EnsureClosure *closure = data;
+       g_clear_object (&closure->cancellable);
+       g_hash_table_unref (closure->collections);
+       g_slice_free (EnsureClosure, closure);
+}
+
+static void
+on_ensure_collection (GObject *source,
+                      GAsyncResult *result,
+                      gpointer user_data)
+{
+       GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+       GSecretService *self = GSECRET_SERVICE (g_async_result_get_source_object (user_data));
+       EnsureClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
+       GSecretCollection *collection;
+       const gchar *path;
+       GError *error = NULL;
+
+       closure->collections_loading--;
+
+       collection = gsecret_collection_new_finish (result, &error);
+
+       if (error != NULL)
+               g_simple_async_result_take_error (res, error);
+
+       if (collection != NULL) {
+               path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (self));
+               g_hash_table_insert (closure->collections, g_strdup (path), collection);
+       }
+
+       if (closure->collections_loading == 0) {
+               service_update_collections (self, closure->collections);
+               g_simple_async_result_complete (res);
+       }
+
+       g_object_unref (self);
+       g_object_unref (res);
+}
+
 void
 gsecret_service_ensure_collections (GSecretService *self,
                                     GCancellable *cancellable,
                                     GAsyncReadyCallback callback,
                                     gpointer user_data)
 {
+       EnsureClosure *closure;
+       GSecretCollection *collection;
        GSimpleAsyncResult *res;
+       const gchar *path;
+       GVariant *paths;
+       GVariantIter iter;
 
        g_return_if_fail (GSECRET_IS_SERVICE (self));
        g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
 
+       paths = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Collections");
+       g_return_if_fail (paths == NULL);
+
+       res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
+                                        gsecret_service_ensure_collections);
+       closure = g_slice_new0 (EnsureClosure);
+       closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+       closure->collections = collections_table_new ();
+       g_simple_async_result_set_op_res_gpointer (res, closure, ensure_closure_free);
+
+       g_variant_iter_init (&iter, paths);
+       while (g_variant_iter_loop (&iter, "&o", &path)) {
+               collection = service_lookup_collection (self, path);
 
+               /* No such collection yet create a new one */
+               if (collection == NULL) {
+                       gsecret_collection_new (self, path, cancellable,
+                                               on_ensure_collection, g_object_ref (res));
+                       closure->collections_loading++;
+               } else {
+                       g_hash_table_insert (closure->collections, g_strdup (path), collection);
+               }
+       }
+
+       if (closure->collections_loading == 0) {
+               service_update_collections (self, closure->collections);
+               g_simple_async_result_complete_in_idle (res);
+       }
+
+       g_variant_unref (paths);
+       g_object_unref (res);
 }
 
 gboolean
@@ -871,7 +1007,10 @@ gsecret_service_ensure_collections_finish (GSecretService *self,
        g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
                              gsecret_service_ensure_collections), FALSE);
 
+       if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+               return FALSE;
 
+       return TRUE;
 }
 
 gboolean
@@ -879,13 +1018,45 @@ gsecret_service_ensure_collections_sync (GSecretService *self,
                                          GCancellable *cancellable,
                                          GError **error)
 {
+       GSecretCollection *collection;
+       GHashTable *collections;
+       GVariant *paths;
+       GVariantIter iter;
+       const gchar *path;
+       gboolean ret = TRUE;
+
        g_return_val_if_fail (GSECRET_IS_SERVICE (self), FALSE);
        g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
        g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
+       paths = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Collections");
+       g_return_val_if_fail (paths == NULL, FALSE);
+
+       collections = collections_table_new ();
 
+       g_variant_iter_init (&iter, paths);
+       while (g_variant_iter_next (&iter, "&o", &path)) {
+               collection = service_lookup_collection (self, path);
+
+               /* No such collection yet create a new one */
+               if (collection == NULL) {
+                       collection = gsecret_collection_new_sync (self, path, cancellable, error);
+                       if (collection == NULL) {
+                               ret = FALSE;
+                               break;
+                       }
+               }
+
+               g_hash_table_insert (collections, g_strdup (path), collection);
+       }
+
+       if (ret)
+               service_update_collections (self, collections);
+
+       g_hash_table_unref (collections);
+       g_variant_unref (paths);
+       return ret;
 }
-#endif
 
 static void
 on_search_items_complete (GObject *source,
index 197a1fa..b2eb012 100644 (file)
 
 G_BEGIN_DECLS
 
+typedef enum {
+       GSECRET_SERVICE_NONE,
+       GSECRET_SERVICE_OPEN_SESSION = 1 << 1,
+       GSECRET_SERVICE_LOAD_COLLECTIONS = 1 << 2,
+} GSecretServiceFlags;
+
 #define GSECRET_TYPE_SERVICE            (gsecret_service_get_type ())
 #define GSECRET_SERVICE(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), GSECRET_TYPE_SERVICE, GSecretService))
 #define GSECRET_SERVICE_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), GSECRET_TYPE_SERVICE, GSecretServiceClass))
@@ -63,16 +69,34 @@ struct _GSecretServiceClass {
 
 GType                gsecret_service_get_type                      (void) G_GNUC_CONST;
 
-void                 gsecret_service_get                           (GCancellable *cancellable,
+void                 gsecret_service_get                           (GSecretServiceFlags flags,
+                                                                    GCancellable *cancellable,
                                                                     GAsyncReadyCallback callback,
                                                                     gpointer user_data);
 
 GSecretService *     gsecret_service_get_finish                    (GAsyncResult *result,
                                                                     GError **error);
 
-GSecretService *     gsecret_service_get_sync                      (GCancellable *cancellable,
+GSecretService *     gsecret_service_get_sync                      (GSecretServiceFlags flags,
+                                                                    GCancellable *cancellable,
                                                                     GError **error);
 
+void                 gsecret_service_new                           (const gchar *service_bus_name,
+                                                                    GSecretServiceFlags flags,
+                                                                    GCancellable *cancellable,
+                                                                    GAsyncReadyCallback callback,
+                                                                    gpointer user_data);
+
+GSecretService *     gsecret_service_new_finish                    (GAsyncResult *result,
+                                                                    GError **error);
+
+GSecretService *     gsecret_service_new_sync                      (const gchar *service_bus_name,
+                                                                    GSecretServiceFlags flags,
+                                                                    GCancellable *cancellable,
+                                                                    GError **error);
+
+GSecretServiceFlags  gsecret_service_get_flags                     (GSecretService *self);
+
 const gchar *        gsecret_service_get_session_algorithms        (GSecretService *self);
 
 const gchar *        gsecret_service_get_session_path              (GSecretService *self);
index 7548ad7..e691459 100644 (file)
@@ -334,20 +334,14 @@ _gsecret_session_open (GSecretService *service,
        g_object_unref (res);
 }
 
-GSecretSession *
+gboolean
 _gsecret_session_open_finish (GAsyncResult *result,
                               GError **error)
 {
-       OpenSessionClosure *closure;
-       GSecretSession *session;
-
        if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
-               return NULL;
+               return FALSE;
 
-       closure = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
-       session = closure->session;
-       closure->session = NULL;
-       return session;
+       return TRUE;
 }
 
 #ifdef WITH_GCRYPT
index 7b0f3c3..1ac00d0 100644 (file)
@@ -29,7 +29,6 @@
 #include <stdlib.h>
 
 typedef struct {
-       GDBusConnection *connection;
        GSecretService *service;
 } Test;
 
@@ -43,26 +42,18 @@ setup (Test *test,
        mock_service_start (mock_script, &error);
        g_assert_no_error (error);
 
-       test->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+       test->service = gsecret_service_get_sync (GSECRET_SERVICE_NONE, NULL, &error);
        g_assert_no_error (error);
-
-       test->service = _gsecret_service_bare_instance (test->connection, NULL);
 }
 
 static void
 teardown (Test *test,
           gconstpointer unused)
 {
-       GError *error = NULL;
-
        g_object_unref (test->service);
        egg_assert_not_object (test->service);
 
        mock_service_stop ();
-
-       g_dbus_connection_flush_sync (test->connection, NULL, &error);
-       g_assert_no_error (error);
-       g_object_unref (test->connection);
 }
 
 static void
index 9bf239d..3cf8565 100644 (file)
@@ -29,7 +29,6 @@
 #include <stdlib.h>
 
 typedef struct {
-       GDBusConnection *connection;
        GSecretService *service;
 } Test;
 
@@ -43,26 +42,18 @@ setup (Test *test,
        mock_service_start (mock_script, &error);
        g_assert_no_error (error);
 
-       test->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+       test->service = gsecret_service_get_sync (GSECRET_SERVICE_NONE, NULL, &error);
        g_assert_no_error (error);
-
-       test->service = _gsecret_service_bare_instance (test->connection, NULL);
 }
 
 static void
 teardown (Test *test,
           gconstpointer unused)
 {
-       GError *error = NULL;
-
        g_object_unref (test->service);
        egg_assert_not_object (test->service);
 
        mock_service_stop ();
-
-       g_dbus_connection_flush_sync (test->connection, NULL, &error);
-       g_assert_no_error (error);
-       g_object_unref (test->connection);
 }
 
 static void
@@ -87,7 +78,6 @@ on_notify_stop (GObject *obj,
        g_assert (*sigs > 0);
        if (--(*sigs) == 0)
                egg_test_wait_stop ();
-g_printerr ("sigs: %u\n", *sigs);
 }
 
 static void
@@ -520,15 +510,14 @@ main (int argc, char **argv)
        g_test_add ("/item/properties", Test, "mock-service-normal.py", setup, test_properties, teardown);
        g_test_add ("/item/set-label-sync", Test, "mock-service-normal.py", setup, test_set_label_sync, teardown);
        g_test_add ("/item/set-label-async", Test, "mock-service-normal.py", setup, test_set_label_async, teardown);
+       g_test_add ("/item/set-label-prop", Test, "mock-service-normal.py", setup, test_set_label_prop, teardown);
        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/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);
 
-       g_test_add ("/item/set-attributes-prop", Test, "mock-service-normal.py", setup, test_set_attributes_prop, teardown);
-       g_test_add ("/item/set-label-prop", Test, "mock-service-normal.py", setup, test_set_label_prop, teardown);
-
        return egg_tests_run_with_loop ();
 }
index 7283076..d01a41a 100644 (file)
@@ -30,7 +30,6 @@
 #include <stdlib.h>
 
 typedef struct {
-       GDBusConnection *connection;
        GSecretService *service;
 } Test;
 
@@ -44,26 +43,18 @@ setup (Test *test,
        mock_service_start (mock_script, &error);
        g_assert_no_error (error);
 
-       test->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+       test->service = gsecret_service_get_sync (GSECRET_SERVICE_NONE, NULL, &error);
        g_assert_no_error (error);
-
-       test->service = _gsecret_service_bare_instance (test->connection, NULL);
 }
 
 static void
 teardown (Test *test,
           gconstpointer unused)
 {
-       GError *error = NULL;
-
        g_object_unref (test->service);
        egg_assert_not_object (test->service);
 
        mock_service_stop ();
-
-       g_dbus_connection_flush_sync (test->connection, NULL, &error);
-       g_assert_no_error (error);
-       g_object_unref (test->connection);
 }
 
 
index 61f7b8e..619d66a 100644 (file)
@@ -25,8 +25,6 @@
 #include <errno.h>
 #include <stdlib.h>
 
-static gchar *MOCK_NAME = "org.mock.Service";
-
 static const GSecretSchema DELETE_SCHEMA = {
        "org.mock.schema.Delete",
        {
@@ -37,8 +35,6 @@ static const GSecretSchema DELETE_SCHEMA = {
 };
 
 typedef struct {
-       GPid pid;
-       GDBusConnection *connection;
        GSecretService *service;
 } Test;
 
@@ -61,10 +57,8 @@ setup (Test *test,
 
        setup_mock (test, data);
 
-       test->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+       test->service = gsecret_service_get_sync (GSECRET_SERVICE_NONE, NULL, &error);
        g_assert_no_error (error);
-
-       test->service = _gsecret_service_bare_instance (test->connection, NULL);
 }
 
 static void
@@ -83,8 +77,6 @@ teardown (Test *test,
        g_object_unref (test->service);
        egg_assert_not_object (test->service);
 
-       g_clear_object (&test->connection);
-
        teardown_mock (test, unused);
 }
 
@@ -107,15 +99,14 @@ test_instance (void)
        GSecretService *service2;
        GSecretService *service3;
        GError *error = NULL;
-       GDBusConnection *connection;
-
-       connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
-       g_assert_no_error (error);
 
        /* Both these sohuld point to the same thing */
 
-       service1 = _gsecret_service_bare_instance (connection, MOCK_NAME);
-       service2 = _gsecret_service_bare_instance (connection, MOCK_NAME);
+       service1 = gsecret_service_get_sync (GSECRET_SERVICE_NONE, NULL, &error);
+       g_assert_no_error (error);
+
+       service2 = gsecret_service_get_sync (GSECRET_SERVICE_NONE, NULL, &error);
+       g_assert_no_error (error);
 
        g_assert (GSECRET_IS_SERVICE (service1));
        g_assert (service1 == service2);
@@ -127,18 +118,17 @@ test_instance (void)
        egg_assert_not_object (service2);
 
        /* Services were unreffed, so this should create a new one */
-       service3 = _gsecret_service_bare_instance (connection, MOCK_NAME);
+       service3 = gsecret_service_get_sync (GSECRET_SERVICE_NONE, NULL, &error);
        g_assert (GSECRET_IS_SERVICE (service3));
+       g_assert_no_error (error);
 
        g_object_unref (service3);
        egg_assert_not_object (service3);
-
-       g_object_unref (connection);
 }
 
 static void
-test_connect_sync (Test *test,
-                   gconstpointer used)
+test_connect_async (Test *test,
+                    gconstpointer used)
 {
        GError *error = NULL;
        GAsyncResult *result = NULL;
@@ -146,12 +136,12 @@ test_connect_sync (Test *test,
        const gchar *path;
 
        /* Passing false, not session */
-       _gsecret_service_bare_connect (MOCK_NAME, FALSE, NULL, on_complete_get_result, &result);
+       gsecret_service_get (GSECRET_SERVICE_NONE, NULL, on_complete_get_result, &result);
        g_assert (result == NULL);
 
        egg_test_wait ();
 
-       service = _gsecret_service_bare_connect_finish (result, &error);
+       service = gsecret_service_get_finish (result, &error);
        g_assert (GSECRET_IS_SERVICE (service));
        g_assert_no_error (error);
        g_object_unref (result);
@@ -164,8 +154,8 @@ test_connect_sync (Test *test,
 }
 
 static void
-test_connect_ensure_sync (Test *test,
-                          gconstpointer used)
+test_connect_ensure_async (Test *test,
+                           gconstpointer used)
 {
        GError *error = NULL;
        GAsyncResult *result = NULL;
@@ -173,12 +163,12 @@ test_connect_ensure_sync (Test *test,
        const gchar *path;
 
        /* Passing true, ensures session is established */
-       _gsecret_service_bare_connect (MOCK_NAME, TRUE, NULL, on_complete_get_result, &result);
+       gsecret_service_get (GSECRET_SERVICE_OPEN_SESSION, NULL, on_complete_get_result, &result);
        g_assert (result == NULL);
 
        egg_test_wait ();
 
-       service = _gsecret_service_bare_connect_finish (result, &error);
+       service = gsecret_service_get_finish (result, &error);
        g_assert_no_error (error);
        g_assert (GSECRET_IS_SERVICE (service));
        g_object_unref (result);
@@ -593,8 +583,8 @@ main (int argc, char **argv)
 
        g_test_add_func ("/service/instance", test_instance);
 
-       g_test_add ("/service/connect-sync", Test, "mock-service-normal.py", setup_mock, test_connect_sync, teardown_mock);
-       g_test_add ("/service/connect-ensure-sync", Test, "mock-service-normal.py", setup_mock, test_connect_ensure_sync, teardown_mock);
+       g_test_add ("/service/connect-sync", Test, "mock-service-normal.py", setup_mock, test_connect_async, teardown_mock);
+       g_test_add ("/service/connect-ensure-sync", Test, "mock-service-normal.py", setup_mock, test_connect_ensure_async, teardown_mock);
 
        g_test_add ("/service/search-for-paths", Test, "mock-service-normal.py", setup, test_search_paths, teardown);
        g_test_add ("/service/search-for-paths-async", Test, "mock-service-normal.py", setup, test_search_paths_async, teardown);
index 08c689d..125c78b 100644 (file)
@@ -27,8 +27,6 @@
 #include <stdlib.h>
 
 typedef struct {
-       GPid pid;
-       GDBusConnection *connection;
        GSecretService *service;
 } Test;
 
@@ -42,26 +40,18 @@ setup (Test *test,
        mock_service_start (mock_script, &error);
        g_assert_no_error (error);
 
-       test->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+       test->service = gsecret_service_get_sync (GSECRET_SERVICE_NONE, NULL, &error);
        g_assert_no_error (error);
-
-       test->service = _gsecret_service_bare_instance (test->connection, NULL);
 }
 
 static void
 teardown (Test *test,
           gconstpointer unused)
 {
-       GError *error = NULL;
-
        g_object_unref (test->service);
        egg_assert_not_object (test->service);
 
        mock_service_stop ();
-
-       g_dbus_connection_flush_sync (test->connection, NULL, &error);
-       g_assert_no_error (error);
-       g_object_unref (test->connection);
 }
 
 static void
index ce1ace3..1ac9a1d 100644 (file)
@@ -1,2 +1,2 @@
 library/gsecret-item.c
-library/gsecret-service.c
+library/gsecret-session.c