Properly refer to the fact that multiple passwords may be removed.
[platform/upstream/libsecret.git] / libsecret / secret-collection.c
1 /* libsecret - GLib wrapper for Secret Service
2  *
3  * Copyright 2012 Red Hat Inc.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published
7  * by the Free Software Foundation; either version 2.1 of the licence or (at
8  * your option) any later version.
9  *
10  * See the included COPYING file for more information.
11  *
12  * Author: Stef Walter <stefw@gnome.org>
13  */
14
15 #include "config.h"
16
17 #include "secret-collection.h"
18 #include "secret-dbus-generated.h"
19 #include "secret-enum-types.h"
20 #include "secret-item.h"
21 #include "secret-paths.h"
22 #include "secret-private.h"
23 #include "secret-service.h"
24 #include "secret-types.h"
25
26 #include <glib/gi18n-lib.h>
27
28 /**
29  * SECTION:secret-collection
30  * @title: SecretCollection
31  * @short_description: A collection of secret items
32  *
33  * #SecretCollection represents a collection of secret items stored in the
34  * Secret Service.
35  *
36  * A collection can be in a locked or unlocked state. Use secret_service_lock()
37  * or secret_service_unlock() to lock or unlock the collection.
38  *
39  * Use the SecretCollection::items property or secret_collection_get_items() to
40  * lookup the items in the collection. There may not be any items exposed when
41  * the collection is locked.
42  *
43  * These functions have an unstable API and may change across versions. Use
44  * <literal>libsecret-unstable</literal> package to access them.
45  *
46  * Stability: Unstable
47  */
48
49 /**
50  * SecretCollection:
51  *
52  * A proxy object representing a collection of secrets in the Secret Service.
53  */
54
55 /**
56  * SecretCollectionClass:
57  * @parent_class: the parent class
58  *
59  * The class for #SecretCollection.
60  */
61
62 /**
63  * SecretCollectionFlags:
64  * @SECRET_COLLECTION_NONE: no flags
65  * @SECRET_COLLECTION_LOAD_ITEMS: items have or should be loaded
66  *
67  * Flags which determine which parts of the #SecretCollection proxy are initialized.
68  */
69
70 /**
71  * SecretCollectionCreateFlags:
72  * @SECRET_COLLECTION_CREATE_NONE: no flags
73  *
74  * Flags for secret_collection_create().
75  */
76
77 /**
78  * SECRET_COLLECTION_DEFAULT:
79  *
80  * An alias to the default collection. This can be passed to secret_password_store()
81  * secret_collection_for_alias().
82  */
83
84 /**
85  * SECRET_COLLECTION_SESSION:
86  *
87  * An alias to the session collection, which will be cleared when the user ends
88  * the session. This can be passed to secret_password_store(),
89  * secret_collection_for_alias() or similar functions.
90  */
91
92 enum {
93         PROP_0,
94         PROP_SERVICE,
95         PROP_FLAGS,
96         PROP_ITEMS,
97         PROP_LABEL,
98         PROP_LOCKED,
99         PROP_CREATED,
100         PROP_MODIFIED
101 };
102
103 struct _SecretCollectionPrivate {
104         /* Doesn't change between construct and finalize */
105         SecretService *service;
106         GCancellable *cancellable;
107         gboolean constructing;
108         SecretCollectionFlags init_flags;
109
110         /* Protected by mutex */
111         GMutex mutex;
112         GHashTable *items;
113 };
114
115 static GInitableIface *secret_collection_initable_parent_iface = NULL;
116
117 static GAsyncInitableIface *secret_collection_async_initable_parent_iface = NULL;
118
119 static void   secret_collection_initable_iface         (GInitableIface *iface);
120
121 static void   secret_collection_async_initable_iface   (GAsyncInitableIface *iface);
122
123 G_DEFINE_TYPE_WITH_CODE (SecretCollection, secret_collection, G_TYPE_DBUS_PROXY,
124                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, secret_collection_initable_iface);
125                          G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, secret_collection_async_initable_iface);
126 );
127
128 static GHashTable *
129 items_table_new (void)
130 {
131         return g_hash_table_new_full (g_str_hash, g_str_equal,
132                                       g_free, g_object_unref);
133 }
134
135 static void
136 secret_collection_init (SecretCollection *self)
137 {
138         self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, SECRET_TYPE_COLLECTION,
139                                                 SecretCollectionPrivate);
140
141         g_mutex_init (&self->pv->mutex);
142         self->pv->cancellable = g_cancellable_new ();
143         self->pv->items = items_table_new ();
144         self->pv->constructing = TRUE;
145 }
146
147 static void
148 on_set_label (GObject *source,
149               GAsyncResult *result,
150               gpointer user_data)
151 {
152         SecretCollection *self = SECRET_COLLECTION (user_data);
153         GError *error = NULL;
154
155         secret_collection_set_label_finish (self, result, &error);
156         if (error != NULL) {
157                 g_warning ("couldn't set SecretCollection Label: %s", error->message);
158                 g_error_free (error);
159         }
160
161         g_object_unref (self);
162 }
163
164 static void
165 collection_take_service (SecretCollection *self,
166                          SecretService *service)
167 {
168         if (service == NULL)
169                 return;
170
171         g_return_if_fail (self->pv->service == NULL);
172
173         self->pv->service = service;
174         g_object_add_weak_pointer (G_OBJECT (self->pv->service),
175                                    (gpointer *)&self->pv->service);
176
177         /* Yes, we expect that the service will stay around */
178         g_object_unref (service);
179 }
180
181 static void
182 secret_collection_set_property (GObject *obj,
183                                 guint prop_id,
184                                 const GValue *value,
185                                 GParamSpec *pspec)
186 {
187         SecretCollection *self = SECRET_COLLECTION (obj);
188
189         switch (prop_id) {
190         case PROP_SERVICE:
191                 collection_take_service (self, g_value_dup_object (value));
192                 break;
193         case PROP_FLAGS:
194                 self->pv->init_flags = g_value_get_flags (value);
195                 break;
196         case PROP_LABEL:
197                 secret_collection_set_label (self, g_value_get_string (value),
198                                              self->pv->cancellable, on_set_label,
199                                              g_object_ref (self));
200                 break;
201         default:
202                 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
203                 break;
204         }
205 }
206
207 static void
208 secret_collection_get_property (GObject *obj,
209                                 guint prop_id,
210                                 GValue *value,
211                                 GParamSpec *pspec)
212 {
213         SecretCollection *self = SECRET_COLLECTION (obj);
214
215         switch (prop_id) {
216         case PROP_SERVICE:
217                 g_value_set_object (value, self->pv->service);
218                 break;
219         case PROP_FLAGS:
220                 g_value_set_flags (value, secret_collection_get_flags (self));
221                 break;
222         case PROP_ITEMS:
223                 g_value_take_boxed (value, secret_collection_get_items (self));
224                 break;
225         case PROP_LABEL:
226                 g_value_take_string (value, secret_collection_get_label (self));
227                 break;
228         case PROP_LOCKED:
229                 g_value_set_boolean (value, secret_collection_get_locked (self));
230                 break;
231         case PROP_CREATED:
232                 g_value_set_uint64 (value, secret_collection_get_created (self));
233                 break;
234         case PROP_MODIFIED:
235                 g_value_set_uint64 (value, secret_collection_get_modified (self));
236                 break;
237         default:
238                 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
239                 break;
240         }
241 }
242
243 static void
244 secret_collection_dispose (GObject *obj)
245 {
246         SecretCollection *self = SECRET_COLLECTION (obj);
247
248         g_cancellable_cancel (self->pv->cancellable);
249
250         G_OBJECT_CLASS (secret_collection_parent_class)->dispose (obj);
251 }
252
253 static void
254 secret_collection_finalize (GObject *obj)
255 {
256         SecretCollection *self = SECRET_COLLECTION (obj);
257
258         if (self->pv->service)
259                 g_object_remove_weak_pointer (G_OBJECT (self->pv->service),
260                                               (gpointer *)&self->pv->service);
261
262         g_mutex_clear (&self->pv->mutex);
263         g_hash_table_destroy (self->pv->items);
264         g_object_unref (self->pv->cancellable);
265
266         G_OBJECT_CLASS (secret_collection_parent_class)->finalize (obj);
267 }
268
269 static SecretItem *
270 collection_lookup_item (SecretCollection *self,
271                         const gchar *path)
272 {
273         SecretItem *item = NULL;
274
275         g_mutex_lock (&self->pv->mutex);
276
277         item = g_hash_table_lookup (self->pv->items, path);
278         if (item != NULL)
279                 g_object_ref (item);
280
281         g_mutex_unlock (&self->pv->mutex);
282
283         return item;
284 }
285
286 static void
287 collection_update_items (SecretCollection *self,
288                          GHashTable *items)
289 {
290         GHashTable *previous;
291
292         g_hash_table_ref (items);
293
294         g_mutex_lock (&self->pv->mutex);
295         previous = self->pv->items;
296         self->pv->items = items;
297         g_mutex_unlock (&self->pv->mutex);
298
299         g_hash_table_unref (previous);
300         g_object_notify (G_OBJECT (self), "items");
301 }
302
303 static void
304 handle_property_changed (SecretCollection *self,
305                          const gchar *property_name,
306                          GVariant *value)
307 {
308         gboolean perform;
309
310         if (g_str_equal (property_name, "Label")) {
311                 g_object_notify (G_OBJECT (self), "label");
312
313         } else if (g_str_equal (property_name, "Locked")) {
314                 g_object_notify (G_OBJECT (self), "locked");
315
316         } else if (g_str_equal (property_name, "Created")) {
317                 g_object_notify (G_OBJECT (self), "created");
318
319         } else if (g_str_equal (property_name, "Modified")) {
320                 g_object_notify (G_OBJECT (self), "modified");
321
322         } else if (g_str_equal (property_name, "Items") && !self->pv->constructing) {
323                 g_mutex_lock (&self->pv->mutex);
324                 perform = self->pv->items != NULL;
325                 g_mutex_unlock (&self->pv->mutex);
326
327                 if (perform)
328                         secret_collection_load_items (self, self->pv->cancellable, NULL, NULL);
329         }
330 }
331
332 static void
333 secret_collection_properties_changed (GDBusProxy *proxy,
334                                       GVariant *changed_properties,
335                                       const gchar* const *invalidated_properties)
336 {
337         SecretCollection *self = SECRET_COLLECTION (proxy);
338         gchar *property_name;
339         GVariantIter iter;
340         GVariant *value;
341
342         g_object_freeze_notify (G_OBJECT (self));
343
344         g_variant_iter_init (&iter, changed_properties);
345         while (g_variant_iter_loop (&iter, "{sv}", &property_name, &value))
346                 handle_property_changed (self, property_name, value);
347
348         g_object_thaw_notify (G_OBJECT (self));
349 }
350
351 static void
352 secret_collection_signal (GDBusProxy *proxy,
353                           const gchar *sender_name,
354                           const gchar *signal_name,
355                           GVariant *parameters)
356 {
357         SecretCollection *self = SECRET_COLLECTION (proxy);
358         SecretItem *item;
359         const gchar *item_path;
360         GVariantBuilder builder;
361         gboolean found = FALSE;
362         GVariantIter iter;
363         GVariant *value;
364         GVariant *paths;
365         GVariant *path;
366
367         /*
368          * Remember that these signals come from a time before PropertiesChanged.
369          * We support them because they're in the spec, and ksecretservice uses them.
370          */
371
372         paths = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Items");
373
374         /* A new collection was added, add it to the Collections property */
375         if (g_str_equal (signal_name, SECRET_SIGNAL_ITEM_CREATED)) {
376                 g_variant_get (parameters, "(@o)", &value);
377                 g_variant_builder_init (&builder, G_VARIANT_TYPE ("ao"));
378                 g_variant_iter_init (&iter, paths);
379                 while ((path = g_variant_iter_next_value (&iter)) != NULL) {
380                         if (g_variant_equal (path, value)) {
381                                 found = TRUE;
382                                 break;
383                         }
384                         g_variant_builder_add_value (&builder, path);
385                         g_variant_unref (path);
386                 }
387                 if (!found) {
388                         g_variant_builder_add_value (&builder, value);
389                         handle_property_changed (self, "Items", g_variant_builder_end (&builder));
390                 }
391                 g_variant_builder_clear (&builder);
392                 g_variant_unref (value);
393
394         /* A collection was deleted, remove it from the Collections property */
395         } else if (g_str_equal (signal_name, SECRET_SIGNAL_ITEM_DELETED)) {
396                 g_variant_get (parameters, "(@o)", &value);
397                 g_variant_builder_init (&builder, G_VARIANT_TYPE ("ao"));
398                 g_variant_iter_init (&iter, paths);
399                 while ((path = g_variant_iter_next_value (&iter)) != NULL) {
400                         if (g_variant_equal (path, value))
401                                 found = TRUE;
402                         else
403                                 g_variant_builder_add_value (&builder, path);
404                         g_variant_unref (path);
405                 }
406                 if (found)
407                         handle_property_changed (self, "Items", g_variant_builder_end (&builder));
408                 g_variant_unref (value);
409
410         /* The collection changed, update it */
411         } else if (g_str_equal (signal_name, SECRET_SIGNAL_ITEM_CHANGED)) {
412                 g_variant_get (parameters, "(&o)", &item_path);
413
414                 g_mutex_lock (&self->pv->mutex);
415
416                 if (self->pv->items)
417                         item = g_hash_table_lookup (self->pv->items, item_path);
418                 else
419                         item = NULL;
420                 if (item)
421                         g_object_ref (item);
422
423                 g_mutex_unlock (&self->pv->mutex);
424
425                 if (item) {
426                         secret_item_refresh (item);
427                         g_object_unref (item);
428                 }
429         }
430
431         g_variant_unref (paths);
432 }
433
434 static void
435 secret_collection_class_init (SecretCollectionClass *klass)
436 {
437         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
438         GDBusProxyClass *proxy_class = G_DBUS_PROXY_CLASS (klass);
439
440         gobject_class->get_property = secret_collection_get_property;
441         gobject_class->set_property = secret_collection_set_property;
442         gobject_class->dispose = secret_collection_dispose;
443         gobject_class->finalize = secret_collection_finalize;
444
445         proxy_class->g_properties_changed = secret_collection_properties_changed;
446         proxy_class->g_signal = secret_collection_signal;
447
448         /**
449          * SecretCollection:service:
450          *
451          * The #SecretService object that this collection is associated with and
452          * uses to interact with the actual D-Bus Secret Service.
453          */
454         g_object_class_install_property (gobject_class, PROP_SERVICE,
455                     g_param_spec_object ("service", "Service", "Secret Service",
456                                          SECRET_TYPE_SERVICE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
457
458         /**
459          * SecretCollection:flags:
460          *
461          * A set of flags describing which parts of the secret collection have
462          * been initialized.
463          */
464         g_object_class_install_property (gobject_class, PROP_FLAGS,
465                      g_param_spec_flags ("flags", "Flags", "Collection flags",
466                                          secret_collection_flags_get_type (), SECRET_COLLECTION_NONE,
467                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
468
469         /**
470          * SecretCollection:items:
471          *
472          * A list of #SecretItem objects representing the items that are in
473          * this collection. This list will be empty if the collection is locked.
474          */
475         g_object_class_install_property (gobject_class, PROP_ITEMS,
476                      g_param_spec_boxed ("items", "Items", "Items in collection",
477                                          _secret_list_get_type (), G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
478
479         /**
480          * SecretCollection:label:
481          *
482          * The human readable label for the collection.
483          *
484          * Setting this property will result in the label of the collection being
485          * set asynchronously. To properly track the changing of the label use the
486          * secret_collection_set_label() function.
487          */
488         g_object_class_install_property (gobject_class, PROP_LABEL,
489                     g_param_spec_string ("label", "Label", "Item label",
490                                          NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
491
492         /**
493          * SecretCollection:locked:
494          *
495          * Whether the collection is locked or not.
496          *
497          * To lock or unlock a collection use the secret_service_lock() or
498          * secret_service_unlock() functions.
499          */
500         g_object_class_install_property (gobject_class, PROP_LOCKED,
501                    g_param_spec_boolean ("locked", "Locked", "Item locked",
502                                          TRUE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
503
504         /**
505          * SecretCollection:created:
506          *
507          * The date and time (in seconds since the UNIX epoch) that this
508          * collection was created.
509          */
510         g_object_class_install_property (gobject_class, PROP_CREATED,
511                     g_param_spec_uint64 ("created", "Created", "Item creation date",
512                                          0UL, G_MAXUINT64, 0UL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
513
514         /**
515          * SecretCollection:modified:
516          *
517          * The date and time (in seconds since the UNIX epoch) that this
518          * collection was last modified.
519          */
520         g_object_class_install_property (gobject_class, PROP_MODIFIED,
521                     g_param_spec_uint64 ("modified", "Modified", "Item modified date",
522                                          0UL, G_MAXUINT64, 0UL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
523
524         g_type_class_add_private (gobject_class, sizeof (SecretCollectionPrivate));
525 }
526
527 static gboolean
528 secret_collection_initable_init (GInitable *initable,
529                                  GCancellable *cancellable,
530                                  GError **error)
531 {
532         SecretCollection *self;
533         SecretService *service;
534         GDBusProxy *proxy;
535
536         if (!secret_collection_initable_parent_iface->init (initable, cancellable, error))
537                 return FALSE;
538
539         proxy = G_DBUS_PROXY (initable);
540
541         if (!_secret_util_have_cached_properties (proxy)) {
542                 g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD,
543                              "No such secret collection at path: %s",
544                              g_dbus_proxy_get_object_path (proxy));
545                 return FALSE;
546         }
547
548         self = SECRET_COLLECTION (initable);
549
550         if (self->pv->service == NULL) {
551                 service = secret_service_get_sync (SECRET_SERVICE_NONE, cancellable, error);
552                 if (service == NULL)
553                         return FALSE;
554                 else
555                         collection_take_service (self, service);
556         }
557
558         if (self->pv->init_flags & SECRET_COLLECTION_LOAD_ITEMS) {
559                 if (!secret_collection_load_items_sync (self, cancellable, error))
560                         return FALSE;
561         }
562
563         self->pv->constructing = FALSE;
564         return TRUE;
565 }
566
567 static void
568 secret_collection_initable_iface (GInitableIface *iface)
569 {
570         secret_collection_initable_parent_iface = g_type_interface_peek_parent (iface);
571
572         iface->init = secret_collection_initable_init;
573 }
574
575 typedef struct {
576         GCancellable *cancellable;
577 } InitClosure;
578
579 static void
580 init_closure_free (gpointer data)
581 {
582         InitClosure *closure = data;
583         g_clear_object (&closure->cancellable);
584         g_slice_free (InitClosure, closure);
585 }
586
587 static void
588 on_init_items (GObject *source,
589                GAsyncResult *result,
590                gpointer user_data)
591 {
592         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
593         SecretCollection *self = SECRET_COLLECTION (source);
594         GError *error = NULL;
595
596         if (!secret_collection_load_items_finish (self, result, &error))
597                 g_simple_async_result_take_error (res, error);
598
599         g_simple_async_result_complete (res);
600         g_object_unref (res);
601 }
602
603 static void
604 collection_ensure_for_flags_async (SecretCollection *self,
605                                    SecretCollectionFlags flags,
606                                    GSimpleAsyncResult *async)
607 {
608         InitClosure *init = g_simple_async_result_get_op_res_gpointer (async);
609
610         if (flags & SECRET_COLLECTION_LOAD_ITEMS)
611                 secret_collection_load_items (self, init->cancellable,
612                                               on_init_items, g_object_ref (async));
613
614         else
615                 g_simple_async_result_complete (async);
616 }
617
618 static void
619 on_init_service (GObject *source,
620                  GAsyncResult *result,
621                  gpointer user_data)
622 {
623         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
624         SecretCollection *self = SECRET_COLLECTION (g_async_result_get_source_object (user_data));
625         SecretService *service;
626         GError *error = NULL;
627
628         service = secret_service_get_finish (result, &error);
629         if (error == NULL) {
630                 collection_take_service (self, service);
631                 collection_ensure_for_flags_async (self, self->pv->init_flags, async);
632
633         } else {
634                 g_simple_async_result_take_error (async, error);
635                 g_simple_async_result_complete (async);
636         }
637
638         g_object_unref (self);
639         g_object_unref (async);
640 }
641
642 static void
643 on_init_base (GObject *source,
644               GAsyncResult *result,
645               gpointer user_data)
646 {
647         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
648         SecretCollection *self = SECRET_COLLECTION (source);
649         InitClosure *init = g_simple_async_result_get_op_res_gpointer (res);
650         GDBusProxy *proxy = G_DBUS_PROXY (self);
651         GError *error = NULL;
652
653         if (!secret_collection_async_initable_parent_iface->init_finish (G_ASYNC_INITABLE (self),
654                                                                          result, &error)) {
655                 g_simple_async_result_take_error (res, error);
656                 g_simple_async_result_complete (res);
657
658         } else if (!_secret_util_have_cached_properties (proxy)) {
659                 g_simple_async_result_set_error (res, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD,
660                                                  "No such secret collection at path: %s",
661                                                  g_dbus_proxy_get_object_path (proxy));
662                 g_simple_async_result_complete (res);
663
664         } else if (self->pv->service == NULL) {
665                 secret_service_get (SECRET_SERVICE_NONE, init->cancellable,
666                                     on_init_service, g_object_ref (res));
667
668         } else {
669                 collection_ensure_for_flags_async (self, self->pv->init_flags, res);
670         }
671
672         g_object_unref (res);
673 }
674
675 static void
676 secret_collection_async_initable_init_async (GAsyncInitable *initable,
677                                              int io_priority,
678                                              GCancellable *cancellable,
679                                              GAsyncReadyCallback callback,
680                                              gpointer user_data)
681 {
682         GSimpleAsyncResult *res;
683         InitClosure *closure;
684
685         res = g_simple_async_result_new (G_OBJECT (initable), callback, user_data,
686                                          secret_collection_async_initable_init_async);
687         closure = g_slice_new0 (InitClosure);
688         closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
689         g_simple_async_result_set_op_res_gpointer (res, closure, init_closure_free);
690
691         secret_collection_async_initable_parent_iface->init_async (initable, io_priority,
692                                                                    cancellable,
693                                                                    on_init_base,
694                                                                    g_object_ref (res));
695
696         g_object_unref (res);
697 }
698
699 static gboolean
700 secret_collection_async_initable_init_finish (GAsyncInitable *initable,
701                                               GAsyncResult *result,
702                                               GError **error)
703 {
704         SecretCollection *self = SECRET_COLLECTION (initable);
705
706         g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (initable),
707                               secret_collection_async_initable_init_async), FALSE);
708
709         if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
710                 return FALSE;
711
712         self->pv->constructing = FALSE;
713         return TRUE;
714 }
715
716 static void
717 secret_collection_async_initable_iface (GAsyncInitableIface *iface)
718 {
719         secret_collection_async_initable_parent_iface = g_type_interface_peek_parent (iface);
720
721         iface->init_async = secret_collection_async_initable_init_async;
722         iface->init_finish = secret_collection_async_initable_init_finish;
723 }
724
725 typedef struct {
726         GCancellable *cancellable;
727         GHashTable *items;
728         gint items_loading;
729 } ItemsClosure;
730
731 static void
732 items_closure_free (gpointer data)
733 {
734         ItemsClosure *closure = data;
735         g_clear_object (&closure->cancellable);
736         g_hash_table_unref (closure->items);
737         g_slice_free (ItemsClosure, closure);
738 }
739
740 static void
741 on_load_item (GObject *source,
742               GAsyncResult *result,
743               gpointer user_data)
744 {
745         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
746         ItemsClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
747         SecretCollection *self = SECRET_COLLECTION (g_async_result_get_source_object (user_data));
748         const gchar *path;
749         GError *error = NULL;
750         SecretItem *item;
751
752         closure->items_loading--;
753
754         item = secret_item_new_for_dbus_path_finish (result, &error);
755
756         if (error != NULL)
757                 g_simple_async_result_take_error (res, error);
758
759         if (item != NULL) {
760                 path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (item));
761                 g_hash_table_insert (closure->items, g_strdup (path), item);
762         }
763
764         if (closure->items_loading == 0) {
765                 collection_update_items (self, closure->items);
766                 g_simple_async_result_complete_in_idle (res);
767         }
768
769         g_object_unref (self);
770         g_object_unref (res);
771 }
772
773 /**
774  * secret_collection_load_items:
775  * @self: the secret collection
776  * @cancellable: optional cancellation object
777  * @callback: called when the operation completes
778  * @user_data: data to be passed to the callback
779  *
780  * Ensure that the #SecretCollection proxy has loaded all the items present
781  * in the Secret Service. This affects the result of
782  * secret_collection_get_items().
783  *
784  * For collections returned from secret_service_get_collections() the items
785  * will have already been loaded.
786  *
787  * This method will return immediately and complete asynchronously.
788  */
789 void
790 secret_collection_load_items (SecretCollection *self,
791                               GCancellable *cancellable,
792                               GAsyncReadyCallback callback,
793                               gpointer user_data)
794 {
795         ItemsClosure *closure;
796         SecretItem *item;
797         GSimpleAsyncResult *res;
798         const gchar *path;
799         GVariant *paths;
800         GVariantIter iter;
801
802         g_return_if_fail (SECRET_IS_COLLECTION (self));
803         g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
804
805         paths = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Items");
806         g_return_if_fail (paths != NULL);
807
808         res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
809                                          secret_collection_load_items);
810         closure = g_slice_new0 (ItemsClosure);
811         closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
812         closure->items = items_table_new ();
813         g_simple_async_result_set_op_res_gpointer (res, closure, items_closure_free);
814
815         g_variant_iter_init (&iter, paths);
816         while (g_variant_iter_loop (&iter, "&o", &path)) {
817                 item = collection_lookup_item (self, path);
818
819                 /* No such collection yet create a new one */
820                 if (item == NULL) {
821                         secret_item_new_for_dbus_path (self->pv->service, path, SECRET_ITEM_NONE,
822                                                        cancellable, on_load_item, g_object_ref (res));
823                         closure->items_loading++;
824
825                 } else {
826                         g_hash_table_insert (closure->items, g_strdup (path), item);
827                 }
828         }
829
830         if (closure->items_loading == 0) {
831                 collection_update_items (self, closure->items);
832                 g_simple_async_result_complete_in_idle (res);
833         }
834
835         g_variant_unref (paths);
836         g_object_unref (res);
837 }
838
839 /**
840  * secret_collection_load_items_finish:
841  * @self: the secret collection
842  * @result: the asynchronous result passed to the callback
843  * @error: location to place an error on failure
844  *
845  * Complete an asynchronous operation to ensure that the #SecretCollection proxy
846  * has loaded all the items present in the Secret Service.
847  *
848  * Returns: whether the load was successful or not
849  */
850 gboolean
851 secret_collection_load_items_finish (SecretCollection *self,
852                                      GAsyncResult *result,
853                                      GError **error)
854 {
855         g_return_val_if_fail (SECRET_IS_COLLECTION (self), FALSE);
856         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
857         g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
858                               secret_collection_load_items), FALSE);
859
860         if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
861                 return FALSE;
862
863         return TRUE;
864 }
865
866 /**
867  * secret_collection_load_items_sync:
868  * @self: the secret collection
869  * @cancellable: optional cancellation object
870  * @error: location to place an error on failure
871  *
872  * Ensure that the #SecretCollection proxy has loaded all the items present
873  * in the Secret Service. This affects the result of
874  * secret_collection_get_items().
875  *
876  * For collections returned from secret_service_get_collections() the items
877  * will have already been loaded.
878  *
879  * This method may block indefinitely and should not be used in user interface
880  * threads.
881  *
882  * Returns: whether the load was successful or not
883  */
884 gboolean
885 secret_collection_load_items_sync (SecretCollection *self,
886                                    GCancellable *cancellable,
887                                    GError **error)
888 {
889         SecretItem *item;
890         GHashTable *items;
891         GVariant *paths;
892         GVariantIter iter;
893         const gchar *path;
894         gboolean ret = TRUE;
895
896         g_return_val_if_fail (SECRET_IS_COLLECTION (self), FALSE);
897         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
898         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
899
900         paths = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Items");
901         g_return_val_if_fail (paths != NULL, FALSE);
902
903         items = items_table_new ();
904
905         g_variant_iter_init (&iter, paths);
906         while (g_variant_iter_next (&iter, "&o", &path)) {
907                 item = collection_lookup_item (self, path);
908
909                 /* No such collection yet create a new one */
910                 if (item == NULL) {
911                         item = secret_item_new_for_dbus_path_sync (self->pv->service, path,
912                                                                    SECRET_ITEM_NONE,
913                                                                    cancellable, error);
914                         if (item == NULL) {
915                                 ret = FALSE;
916                                 break;
917                         }
918                 }
919
920                 g_hash_table_insert (items, g_strdup (path), item);
921         }
922
923         if (ret)
924                 collection_update_items (self, items);
925
926         g_hash_table_unref (items);
927         g_variant_unref (paths);
928         return ret;
929 }
930
931 /**
932  * secret_collection_refresh:
933  * @self: the collection
934  *
935  * Refresh the properties on this collection. This fires off a request to
936  * refresh, and the properties will be updated later.
937  *
938  * Calling this method is not normally necessary, as the secret service
939  * will notify the client when properties change.
940  */
941 void
942 secret_collection_refresh (SecretCollection *self)
943 {
944         g_return_if_fail (SECRET_IS_COLLECTION (self));
945
946         _secret_util_get_properties (G_DBUS_PROXY (self),
947                                       secret_collection_refresh,
948                                       self->pv->cancellable, NULL, NULL);
949 }
950
951 typedef struct {
952         GCancellable *cancellable;
953         SecretCollection *collection;
954         GHashTable *properties;
955         gchar *alias;
956         SecretCollectionCreateFlags flags;
957 } CreateClosure;
958
959 static void
960 create_closure_free (gpointer data)
961 {
962         CreateClosure *closure = data;
963         g_clear_object (&closure->cancellable);
964         g_clear_object (&closure->collection);
965         g_hash_table_unref (closure->properties);
966         g_free (closure->alias);
967         g_slice_free (CreateClosure, closure);
968 }
969
970 static void
971 on_create_collection (GObject *source,
972                       GAsyncResult *result,
973                       gpointer user_data)
974 {
975         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
976         CreateClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
977         GError *error = NULL;
978
979         closure->collection = secret_collection_new_for_dbus_path_finish (result, &error);
980         if (error != NULL)
981                 g_simple_async_result_take_error (res, error);
982
983         g_simple_async_result_complete (res);
984         g_object_unref (res);
985 }
986
987 static void
988 on_create_path (GObject *source,
989                 GAsyncResult *result,
990                 gpointer user_data)
991 {
992         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
993         CreateClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
994         SecretService *service = SECRET_SERVICE (source);
995         GError *error = NULL;
996         gchar *path;
997
998         path = secret_service_create_collection_dbus_path_finish (service, result, &error);
999         if (error == NULL) {
1000                 secret_collection_new_for_dbus_path (service, path, SECRET_COLLECTION_LOAD_ITEMS,
1001                                                      closure->cancellable,
1002                                                      on_create_collection, g_object_ref (res));
1003         } else {
1004                 g_simple_async_result_take_error (res, error);
1005                 g_simple_async_result_complete (res);
1006         }
1007
1008         g_object_unref (res);
1009 }
1010
1011 static void
1012 on_create_service (GObject *source,
1013                    GAsyncResult *result,
1014                    gpointer user_data)
1015 {
1016         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
1017         CreateClosure *create = g_simple_async_result_get_op_res_gpointer (async);
1018         GError *error = NULL;
1019         SecretService *service;
1020
1021         service = secret_service_get_finish (result, &error);
1022         if (error == NULL) {
1023                 secret_service_create_collection_dbus_path (service, create->properties,
1024                                                             create->alias, create->flags,
1025                                                             create->cancellable,
1026                                                             on_create_path, g_object_ref (async));
1027                 g_object_unref (service);
1028
1029         } else {
1030                 g_simple_async_result_take_error (async, error);
1031                 g_simple_async_result_complete (async);
1032         }
1033
1034         g_object_unref (async);
1035 }
1036
1037 static GHashTable *
1038 collection_properties_new (const gchar *label)
1039 {
1040         GHashTable *properties;
1041         GVariant *value;
1042
1043         properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
1044                                             (GDestroyNotify)g_variant_unref);
1045         value = g_variant_new_string (label);
1046         g_hash_table_insert (properties,
1047                              SECRET_COLLECTION_INTERFACE ".Label",
1048                              g_variant_ref_sink (value));
1049
1050         return properties;
1051 }
1052
1053 /**
1054  * secret_collection_create:
1055  * @service: (allow-none): a secret service object
1056  * @label: label for the new collection
1057  * @alias: (allow-none): alias to assign to the collection
1058  * @flags: currently unused
1059  * @cancellable: optional cancellation object
1060  * @callback: called when the operation completes
1061  * @user_data: data to pass to the callback
1062  *
1063  * Create a new collection in the secret service.
1064  *
1065  * This method returns immediately and completes asynchronously. The secret
1066  * service may prompt the user. secret_service_prompt() will be used to handle
1067  * any prompts that are required.
1068  *
1069  * An @alias is a well-known tag for a collection, such as 'default' (ie: the
1070  * default collection to store items in). This allows other applications to
1071  * easily identify and share a collection. If you specify an @alias, and a
1072  * collection with that alias already exists, then a new collection will not
1073  * be created. The previous one will be returned instead.
1074  *
1075  * If @service is NULL, then secret_service_get() will be called to get
1076  * the default #SecretService proxy.
1077  *
1078  */
1079 void
1080 secret_collection_create (SecretService *service,
1081                           const gchar *label,
1082                           const gchar *alias,
1083                           SecretCollectionCreateFlags flags,
1084                           GCancellable *cancellable,
1085                           GAsyncReadyCallback callback,
1086                           gpointer user_data)
1087 {
1088         GSimpleAsyncResult *res;
1089         CreateClosure *closure;
1090
1091         g_return_if_fail (service == NULL || SECRET_IS_SERVICE (service));
1092         g_return_if_fail (label != NULL);
1093         g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1094
1095         res = g_simple_async_result_new (NULL, callback, user_data,
1096                                          secret_collection_create);
1097         closure = g_slice_new0 (CreateClosure);
1098         closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
1099         closure->properties = collection_properties_new (label);
1100         closure->alias = g_strdup (alias);
1101         closure->flags = flags;
1102         g_simple_async_result_set_op_res_gpointer (res, closure, create_closure_free);
1103
1104         if (service == NULL) {
1105                 secret_service_get (SECRET_SERVICE_NONE, cancellable,
1106                                     on_create_service, g_object_ref (res));
1107
1108         } else {
1109                 secret_service_create_collection_dbus_path (service, closure->properties,
1110                                                             closure->alias, closure->flags,
1111                                                             closure->cancellable,
1112                                                             on_create_path, g_object_ref (res));
1113         }
1114
1115         g_object_unref (res);
1116 }
1117
1118 /**
1119  * secret_collection_create_finish:
1120  * @result: the asynchronous result passed to the callback
1121  * @error: location to place an error on failure
1122  *
1123  * Finish operation to create a new collection in the secret service.
1124  *
1125  * Returns: (transfer full): the new collection, which should be unreferenced
1126  *          with g_object_unref()
1127  */
1128 SecretCollection *
1129 secret_collection_create_finish (GAsyncResult *result,
1130                                  GError **error)
1131 {
1132         GSimpleAsyncResult *res;
1133         CreateClosure *closure;
1134
1135         g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL,
1136                               secret_collection_create), NULL);
1137         g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1138
1139         res = G_SIMPLE_ASYNC_RESULT (result);
1140
1141         if (g_simple_async_result_propagate_error (res, error))
1142                 return NULL;
1143
1144         closure = g_simple_async_result_get_op_res_gpointer (res);
1145         if (closure->collection == NULL)
1146                 return NULL;
1147
1148         return g_object_ref (closure->collection);
1149 }
1150
1151 /**
1152  * secret_collection_create_sync:
1153  * @service: (allow-none): a secret service object
1154  * @label: label for the new collection
1155  * @alias: (allow-none): alias to assign to the collection
1156  * @flags: currently unused
1157  * @cancellable: optional cancellation object
1158  * @error: location to place an error on failure
1159  *
1160  * Create a new collection in the secret service.
1161  *
1162  * This method may block indefinitely and should not be used in user interface
1163  * threads. The secret service may prompt the user. secret_service_prompt()
1164  * will be used to handle any prompts that are required.
1165  *
1166  * An @alias is a well-known tag for a collection, such as 'default' (ie: the
1167  * default collection to store items in). This allows other applications to
1168  * easily identify and share a collection. If you specify an @alias, and a
1169  * collection with that alias already exists, then a new collection will not
1170  * be created. The previous one will be returned instead.
1171  *
1172  * If @service is NULL, then secret_service_get_sync() will be called to get
1173  * the default #SecretService proxy.
1174  *
1175  * Returns: (transfer full): the new collection, which should be unreferenced
1176  *          with g_object_unref()
1177  */
1178 SecretCollection *
1179 secret_collection_create_sync (SecretService *service,
1180                                const gchar *label,
1181                                const gchar *alias,
1182                                SecretCollectionCreateFlags flags,
1183                                GCancellable *cancellable,
1184                                GError **error)
1185 {
1186         SecretCollection *collection;
1187         GHashTable *properties;
1188         gchar *path;
1189
1190         g_return_val_if_fail (service == NULL || SECRET_IS_SERVICE (service), NULL);
1191         g_return_val_if_fail (label != NULL, NULL);
1192         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
1193         g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1194
1195         if (service == NULL) {
1196                 service = secret_service_get_sync (SECRET_SERVICE_NONE, cancellable, error);
1197                 if (service == NULL)
1198                         return NULL;
1199         } else {
1200                 g_object_ref (service);
1201         }
1202
1203         properties = collection_properties_new (label);
1204
1205         path = secret_service_create_collection_dbus_path_sync (service, properties, alias,
1206                                                                 flags, cancellable, error);
1207
1208         g_hash_table_unref (properties);
1209
1210         if (path == NULL) {
1211                 g_object_unref (service);
1212                 return NULL;
1213         }
1214
1215         collection = secret_collection_new_for_dbus_path_sync (service, path,
1216                                                                SECRET_COLLECTION_LOAD_ITEMS,
1217                                                                cancellable, error);
1218
1219         g_object_unref (service);
1220         g_free (path);
1221
1222         return collection;
1223 }
1224
1225 typedef struct {
1226         SecretCollection *collection;
1227         GCancellable *cancellable;
1228         GHashTable *items;
1229         gchar **paths;
1230         guint loading;
1231         SecretSearchFlags flags;
1232 } SearchClosure;
1233
1234 static void
1235 search_closure_free (gpointer data)
1236 {
1237         SearchClosure *closure = data;
1238         g_object_unref (closure->collection);
1239         g_clear_object (&closure->cancellable);
1240         g_hash_table_unref (closure->items);
1241         g_strfreev (closure->paths);
1242         g_slice_free (SearchClosure, closure);
1243 }
1244
1245 static void
1246 search_closure_take_item (SearchClosure *closure,
1247                           SecretItem *item)
1248 {
1249         const gchar *path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (item));
1250         g_hash_table_insert (closure->items, (gpointer)path, item);
1251 }
1252
1253 static void
1254 on_search_secrets (GObject *source,
1255                    GAsyncResult *result,
1256                    gpointer user_data)
1257 {
1258         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
1259
1260         /* Note that we ignore any unlock failure */
1261         secret_item_load_secrets_finish (result, NULL);
1262
1263         g_simple_async_result_complete (async);
1264         g_object_unref (async);
1265 }
1266
1267 static void
1268 on_search_unlocked (GObject *source,
1269                     GAsyncResult *result,
1270                     gpointer user_data)
1271 {
1272         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
1273         SearchClosure *search = g_simple_async_result_get_op_res_gpointer (async);
1274         GList *items;
1275
1276         /* Note that we ignore any unlock failure */
1277         secret_service_unlock_finish (SECRET_SERVICE (source), result, NULL, NULL);
1278
1279         /* If loading secrets ... locked items automatically ignored */
1280         if (search->flags & SECRET_SEARCH_LOAD_SECRETS) {
1281                 items = g_hash_table_get_values (search->items);
1282                 secret_item_load_secrets (items, search->cancellable,
1283                                           on_search_secrets, g_object_ref (async));
1284                 g_list_free (items);
1285
1286         /* No additional options, just complete */
1287         } else {
1288                 g_simple_async_result_complete (async);
1289         }
1290
1291         g_object_unref (async);
1292 }
1293
1294 static void
1295 secret_search_unlock_load_or_complete (GSimpleAsyncResult *async,
1296                                        SearchClosure *search)
1297 {
1298         GList *items;
1299
1300         /* If unlocking then unlock all the locked items */
1301         if (search->flags & SECRET_SEARCH_UNLOCK) {
1302                 items = g_hash_table_get_values (search->items);
1303                 secret_service_unlock (secret_collection_get_service (search->collection),
1304                                        items, search->cancellable,
1305                                        on_search_unlocked, g_object_ref (async));
1306                 g_list_free (items);
1307
1308         /* If loading secrets ... locked items automatically ignored */
1309         } else if (search->flags & SECRET_SEARCH_LOAD_SECRETS) {
1310                 items = g_hash_table_get_values (search->items);
1311                 secret_item_load_secrets (items, search->cancellable,
1312                                           on_search_secrets, g_object_ref (async));
1313                 g_list_free (items);
1314
1315         /* No additional options, just complete */
1316         } else {
1317                 g_simple_async_result_complete (async);
1318         }
1319 }
1320
1321 static void
1322 on_search_loaded (GObject *source,
1323                   GAsyncResult *result,
1324                   gpointer user_data)
1325 {
1326         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
1327         SearchClosure *search = g_simple_async_result_get_op_res_gpointer (async);
1328         GError *error = NULL;
1329         SecretItem *item;
1330
1331         search->loading--;
1332
1333         item = secret_item_new_for_dbus_path_finish (result, &error);
1334         if (error != NULL)
1335                 g_simple_async_result_take_error (async, error);
1336
1337         if (item != NULL)
1338                 search_closure_take_item (search, item);
1339
1340         /* We're done loading, lets go to the next step */
1341         if (search->loading == 0)
1342                 secret_search_unlock_load_or_complete (async, search);
1343
1344         g_object_unref (async);
1345 }
1346
1347 static void
1348 on_search_paths (GObject *source,
1349                  GAsyncResult *result,
1350                  gpointer user_data)
1351 {
1352         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
1353         SearchClosure *search = g_simple_async_result_get_op_res_gpointer (async);
1354         SecretCollection *self = search->collection;
1355         SecretService *service = secret_collection_get_service (self);
1356         GError *error = NULL;
1357         SecretItem *item;
1358         gint want = 1;
1359         gint i;
1360
1361         search->paths = secret_collection_search_for_dbus_paths_finish (self, result, &error);
1362         if (error == NULL) {
1363                 want = 1;
1364                 if (search->flags & SECRET_SEARCH_ALL)
1365                         want = G_MAXINT;
1366
1367                 for (i = 0; i < want && search->paths[i] != NULL; i++) {
1368                         item = _secret_collection_find_item_instance (self, search->paths[i]);
1369                         if (item == NULL) {
1370                                 secret_item_new_for_dbus_path (service, search->paths[i], SECRET_ITEM_NONE,
1371                                                                search->cancellable, on_search_loaded,
1372                                                                g_object_ref (async));
1373                                 search->loading++;
1374                         } else {
1375                                 search_closure_take_item (search, item);
1376                         }
1377
1378                 }
1379
1380                 /* No items loading, complete operation now */
1381                 if (search->loading == 0)
1382                         secret_search_unlock_load_or_complete (async, search);
1383
1384         } else {
1385                 g_simple_async_result_take_error (async, error);
1386                 g_simple_async_result_complete (async);
1387         }
1388
1389         g_object_unref (async);
1390 }
1391
1392 /**
1393  * secret_collection_search:
1394  * @self: a secret collection
1395  * @schema: (allow-none): the schema for the attributes
1396  * @attributes: (element-type utf8 utf8): search for items matching these attributes
1397  * @flags: search option flags
1398  * @cancellable: optional cancellation object
1399  * @callback: called when the operation completes
1400  * @user_data: data to pass to the callback
1401  *
1402  * Search for items matching the @attributes in the @collection.
1403  * The @attributes should be a table of string keys and string values.
1404  *
1405  * If %SECRET_SEARCH_ALL is set in @flags, then all the items matching the
1406  * search will be returned. Otherwise only the first item will be returned.
1407  * This is almost always the unlocked item that was most recently stored.
1408  *
1409  * If %SECRET_SEARCH_UNLOCK is set in @flags, then items will be unlocked
1410  * if necessary. In either case, locked and unlocked items will match the
1411  * search and be returned. If the unlock fails, the search does not fail.
1412  *
1413  * If %SECRET_SEARCH_LOAD_SECRETS is set in @flags, then the items will have
1414  * their secret values loaded and available via secret_item_get_secret().
1415  *
1416  * This function returns immediately and completes asynchronously.
1417  */
1418 void
1419 secret_collection_search (SecretCollection *self,
1420                           const SecretSchema *schema,
1421                           GHashTable *attributes,
1422                           SecretSearchFlags flags,
1423                           GCancellable *cancellable,
1424                           GAsyncReadyCallback callback,
1425                           gpointer user_data)
1426 {
1427         GSimpleAsyncResult *async;
1428         SearchClosure *search;
1429
1430         g_return_if_fail (SECRET_IS_COLLECTION (self));
1431         g_return_if_fail (attributes != NULL);
1432         g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1433
1434         /* Warnings raised already */
1435         if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, TRUE))
1436                 return;
1437
1438         async = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
1439                                            secret_collection_search);
1440         search = g_slice_new0 (SearchClosure);
1441         search->collection = g_object_ref (self);
1442         search->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
1443         search->items = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
1444         search->flags = flags;
1445         g_simple_async_result_set_op_res_gpointer (async, search, search_closure_free);
1446
1447         secret_collection_search_for_dbus_paths (self, schema, attributes,
1448                                                  cancellable, on_search_paths,
1449                                                  g_object_ref (async));
1450
1451         g_object_unref (async);
1452 }
1453
1454 /**
1455  * secret_collection_search_finish:
1456  * @self: the secret collection
1457  * @result: asynchronous result passed to callback
1458  * @error: location to place error on failure
1459  *
1460  * Complete asynchronous operation to search for items in a collection.
1461  *
1462  * Returns: (transfer full) (element-type SecretUnstable.Item):
1463  *          a list of items that matched the search
1464  */
1465 GList *
1466 secret_collection_search_finish (SecretCollection *self,
1467                                  GAsyncResult *result,
1468                                  GError **error)
1469 {
1470         GSimpleAsyncResult *async;
1471         SearchClosure *search;
1472         GList *items = NULL;
1473         SecretItem *item;
1474         guint i;
1475
1476         g_return_val_if_fail (SECRET_IS_COLLECTION (self), NULL);
1477         g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1478         g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
1479                               secret_collection_search), NULL);
1480
1481         async = G_SIMPLE_ASYNC_RESULT (result);
1482         if (g_simple_async_result_propagate_error (async, error))
1483                 return NULL;
1484
1485         search = g_simple_async_result_get_op_res_gpointer (async);
1486
1487         for (i = 0; search->paths[i]; i++) {
1488                 item = g_hash_table_lookup (search->items, search->paths[i]);
1489                 if (item != NULL)
1490                         items = g_list_prepend (items, g_object_ref (item));
1491         }
1492
1493         return g_list_reverse (items);
1494 }
1495
1496 static gboolean
1497 collection_load_items_sync (SecretCollection *self,
1498                             GCancellable *cancellable,
1499                             gchar **paths,
1500                             GList **items,
1501                             gint want,
1502                             GError **error)
1503 {
1504         SecretService *service = secret_collection_get_service (self);
1505         SecretItem *item;
1506         gint have = 0;
1507         guint i;
1508
1509         for (i = 0; have < want && paths[i] != NULL; i++) {
1510                 item = _secret_collection_find_item_instance (self, paths[i]);
1511                 if (item == NULL)
1512                         item = secret_item_new_for_dbus_path_sync (service, paths[i], SECRET_ITEM_NONE,
1513                                                                    cancellable, error);
1514                 if (item == NULL) {
1515                         return FALSE;
1516
1517                 } else {
1518                         *items = g_list_prepend (*items, item);
1519                         have++;
1520                 }
1521         }
1522
1523         return TRUE;
1524 }
1525
1526 /**
1527  * secret_collection_search_sync:
1528  * @self: a secret collection
1529  * @schema: (allow-none): the schema for the attributes
1530  * @attributes: (element-type utf8 utf8): search for items matching these attributes
1531  * @flags: search option flags
1532  * @cancellable: optional cancellation object
1533  * @error: location to place error on failure
1534  *
1535  * Search for items matching the @attributes in the @collection.
1536  * The @attributes should be a table of string keys and string values.
1537  *
1538  * If %SECRET_SEARCH_ALL is set in @flags, then all the items matching the
1539  * search will be returned. Otherwise only the first item will be returned.
1540  * This is almost always the unlocked item that was most recently stored.
1541  *
1542  * If %SECRET_SEARCH_UNLOCK is set in @flags, then items will be unlocked
1543  * if necessary. In either case, locked and unlocked items will match the
1544  * search and be returned. If the unlock fails, the search does not fail.
1545  *
1546  * If %SECRET_SEARCH_LOAD_SECRETS is set in @flags, then the items will have
1547  * their secret values loaded and available via secret_item_get_secret().
1548  *
1549  * This function may block indefinetely. Use the asynchronous version
1550  * in user interface threads.
1551  *
1552  * Returns: (transfer full) (element-type SecretUnstable.Item):
1553  *          a list of items that matched the search
1554  */
1555 GList *
1556 secret_collection_search_sync (SecretCollection *self,
1557                                const SecretSchema *schema,
1558                                GHashTable *attributes,
1559                                SecretSearchFlags flags,
1560                                GCancellable *cancellable,
1561                                GError **error)
1562 {
1563         gchar **paths = NULL;
1564         GList *items = NULL;
1565         gboolean ret;
1566         gint want;
1567
1568         g_return_val_if_fail (SECRET_IS_COLLECTION (self), NULL);
1569         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
1570         g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1571
1572         /* Warnings raised already */
1573         if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, TRUE))
1574                 return NULL;
1575
1576         paths = secret_collection_search_for_dbus_paths_sync (self, schema, attributes,
1577                                                               cancellable, error);
1578         if (paths == NULL)
1579                 return NULL;
1580
1581         ret = TRUE;
1582
1583         want = 1;
1584         if (flags & SECRET_SEARCH_ALL)
1585                 want = G_MAXINT;
1586
1587         ret = collection_load_items_sync (self, cancellable, paths,
1588                                           &items, want, error);
1589
1590         g_strfreev (paths);
1591
1592         if (!ret)
1593                 return NULL;
1594
1595         if (flags & SECRET_SEARCH_UNLOCK) {
1596                 secret_service_unlock_sync (secret_collection_get_service (self),
1597                                             items, cancellable, NULL, NULL);
1598         }
1599
1600         if (flags & SECRET_SEARCH_LOAD_SECRETS)
1601                 secret_item_load_secrets_sync (items, NULL, NULL);
1602
1603         return items;
1604 }
1605
1606 static void
1607 on_service_delete_path (GObject *source,
1608                         GAsyncResult *result,
1609                         gpointer user_data)
1610 {
1611         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
1612         GError *error = NULL;
1613
1614         _secret_service_delete_path_finish (SECRET_SERVICE (source), result, &error);
1615         if (error != NULL)
1616                 g_simple_async_result_take_error (async, error);
1617         g_simple_async_result_complete (async);
1618         g_object_unref (async);
1619 }
1620 /**
1621  * secret_collection_delete:
1622  * @self: a collection
1623  * @cancellable: optional cancellation object
1624  * @callback: called when the operation completes
1625  * @user_data: data to pass to the callback
1626  *
1627  * Delete this collection.
1628  *
1629  * This method returns immediately and completes asynchronously. The secret
1630  * service may prompt the user. secret_service_prompt() will be used to handle
1631  * any prompts that show up.
1632  */
1633 void
1634 secret_collection_delete (SecretCollection *self,
1635                           GCancellable *cancellable,
1636                           GAsyncReadyCallback callback,
1637                           gpointer user_data)
1638 {
1639         GSimpleAsyncResult *async;
1640         const gchar *object_path;
1641
1642         g_return_if_fail (SECRET_IS_COLLECTION (self));
1643         g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1644
1645         async = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
1646                                            secret_collection_delete);
1647
1648         object_path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (self));
1649         _secret_service_delete_path (self->pv->service, object_path, FALSE,
1650                                      cancellable, on_service_delete_path,
1651                                      g_object_ref (async));
1652
1653         g_object_unref (async);
1654 }
1655
1656 /**
1657  * secret_collection_delete_finish:
1658  * @self: a collection
1659  * @result: asynchronous result passed to the callback
1660  * @error: location to place an error on failure
1661  *
1662  * Complete operation to delete this collection.
1663  *
1664  * Returns: whether the collection was successfully deleted or not
1665  */
1666 gboolean
1667 secret_collection_delete_finish (SecretCollection *self,
1668                                  GAsyncResult *result,
1669                                  GError **error)
1670 {
1671         g_return_val_if_fail (SECRET_IS_COLLECTION (self), FALSE);
1672         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1673         g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
1674                               secret_collection_delete), FALSE);
1675
1676         if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
1677                 return FALSE;
1678
1679         return TRUE;
1680 }
1681
1682 /**
1683  * secret_collection_delete_sync:
1684  * @self: a collection
1685  * @cancellable: optional cancellation object
1686  * @error: location to place an error on failure
1687  *
1688  * Delete this collection.
1689  *
1690  * This method may block indefinitely and should not be used in user
1691  * interface threads. The secret service may prompt the user.
1692  * secret_service_prompt() will be used to handle any prompts that show up.
1693  *
1694  * Returns: whether the collection was successfully deleted or not
1695  */
1696 gboolean
1697 secret_collection_delete_sync (SecretCollection *self,
1698                                GCancellable *cancellable,
1699                                GError **error)
1700 {
1701         SecretSync *sync;
1702         gboolean ret;
1703
1704         g_return_val_if_fail (SECRET_IS_COLLECTION (self), FALSE);
1705         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
1706         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1707
1708         sync = _secret_sync_new ();
1709         g_main_context_push_thread_default (sync->context);
1710
1711         secret_collection_delete (self, cancellable, _secret_sync_on_result, sync);
1712
1713         g_main_loop_run (sync->loop);
1714
1715         ret = secret_collection_delete_finish (self, sync->result, error);
1716
1717         g_main_context_pop_thread_default (sync->context);
1718         _secret_sync_free (sync);
1719
1720         return ret;
1721 }
1722
1723 /**
1724  * secret_collection_get_service:
1725  * @self: a collection
1726  *
1727  * Get the Secret Service object that this collection was created with.
1728  *
1729  * Returns: (transfer none): the Secret Service object
1730  */
1731 SecretService *
1732 secret_collection_get_service (SecretCollection *self)
1733 {
1734         g_return_val_if_fail (SECRET_IS_COLLECTION (self), NULL);
1735         return self->pv->service;
1736 }
1737
1738 /**
1739  * secret_collection_get_flags:
1740  * @self: the secret collection proxy
1741  *
1742  * Get the flags representing what features of the #SecretCollection proxy
1743  * have been initialized.
1744  *
1745  * Use secret_collection_load_items()  to initialize further features
1746  * and change the flags.
1747  *
1748  * Returns: the flags for features initialized
1749  */
1750 SecretCollectionFlags
1751 secret_collection_get_flags (SecretCollection *self)
1752 {
1753         SecretCollectionFlags flags = 0;
1754
1755         g_return_val_if_fail (SECRET_IS_COLLECTION (self), SECRET_COLLECTION_NONE);
1756
1757         g_mutex_lock (&self->pv->mutex);
1758
1759         if (self->pv->items)
1760                 flags |= SECRET_COLLECTION_LOAD_ITEMS;
1761
1762         g_mutex_unlock (&self->pv->mutex);
1763
1764         return flags;
1765 }
1766
1767 /**
1768  * secret_collection_get_items:
1769  * @self: a collection
1770  *
1771  * Get the list of items in this collection.
1772  *
1773  * Returns: (transfer full) (element-type SecretUnstable.Item): a list of items,
1774  * when done, the list should be freed with g_list_free, and each item should
1775  * be released with g_object_unref()
1776  */
1777 GList *
1778 secret_collection_get_items (SecretCollection *self)
1779 {
1780         GList *l, *items;
1781
1782         g_return_val_if_fail (SECRET_IS_COLLECTION (self), NULL);
1783
1784         g_mutex_lock (&self->pv->mutex);
1785         items = g_hash_table_get_values (self->pv->items);
1786         for (l = items; l != NULL; l = g_list_next (l))
1787                 g_object_ref (l->data);
1788         g_mutex_unlock (&self->pv->mutex);
1789
1790         return items;
1791 }
1792
1793 SecretItem *
1794 _secret_collection_find_item_instance (SecretCollection *self,
1795                                        const gchar *item_path)
1796 {
1797         SecretItem *item;
1798
1799         g_mutex_lock (&self->pv->mutex);
1800         item = g_hash_table_lookup (self->pv->items, item_path);
1801         if (item != NULL)
1802                 g_object_ref (item);
1803         g_mutex_unlock (&self->pv->mutex);
1804
1805         return item;
1806 }
1807
1808 /**
1809  * secret_collection_get_label:
1810  * @self: a collection
1811  *
1812  * Get the label of this collection.
1813  *
1814  * Returns: (transfer full): the label, which should be freed with g_free()
1815  */
1816 gchar *
1817 secret_collection_get_label (SecretCollection *self)
1818 {
1819         GVariant *variant;
1820         gchar *label;
1821
1822         g_return_val_if_fail (SECRET_IS_COLLECTION (self), NULL);
1823
1824         variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Label");
1825         g_return_val_if_fail (variant != NULL, NULL);
1826
1827         label = g_variant_dup_string (variant, NULL);
1828         g_variant_unref (variant);
1829
1830         return label;
1831 }
1832
1833 /**
1834  * secret_collection_set_label:
1835  * @self: a collection
1836  * @label: a new label
1837  * @cancellable: optional cancellation object
1838  * @callback: called when the operation completes
1839  * @user_data: data to pass to the callback
1840  *
1841  * Set the label of this collection.
1842  *
1843  * This function returns immediately and completes asynchronously.
1844  */
1845 void
1846 secret_collection_set_label (SecretCollection *self,
1847                              const gchar *label,
1848                              GCancellable *cancellable,
1849                              GAsyncReadyCallback callback,
1850                              gpointer user_data)
1851 {
1852         g_return_if_fail (SECRET_IS_COLLECTION (self));
1853         g_return_if_fail (label != NULL);
1854
1855         _secret_util_set_property (G_DBUS_PROXY (self), "Label",
1856                                    g_variant_new_string (label),
1857                                    secret_collection_set_label,
1858                                    cancellable, callback, user_data);
1859 }
1860
1861 /**
1862  * secret_collection_set_label_finish:
1863  * @self: a collection
1864  * @result: asynchronous result passed to callback
1865  * @error: location to place error on failure
1866  *
1867  * Complete asynchronous operation to set the label of this collection.
1868  *
1869  * Returns: whether the change was successful or not
1870  */
1871 gboolean
1872 secret_collection_set_label_finish (SecretCollection *self,
1873                                     GAsyncResult *result,
1874                                     GError **error)
1875 {
1876         g_return_val_if_fail (SECRET_IS_COLLECTION (self), FALSE);
1877
1878         return _secret_util_set_property_finish (G_DBUS_PROXY (self),
1879                                                  secret_collection_set_label,
1880                                                  result, error);
1881 }
1882
1883 /**
1884  * secret_collection_set_label_sync:
1885  * @self: a collection
1886  * @label: a new label
1887  * @cancellable: optional cancellation object
1888  * @error: location to place error on failure
1889  *
1890  * Set the label of this collection.
1891  *
1892  * This function may block indefinetely. Use the asynchronous version
1893  * in user interface threads.
1894  *
1895  * Returns: whether the change was successful or not
1896  */
1897 gboolean
1898 secret_collection_set_label_sync (SecretCollection *self,
1899                                   const gchar *label,
1900                                   GCancellable *cancellable,
1901                                   GError **error)
1902 {
1903         g_return_val_if_fail (SECRET_IS_COLLECTION (self), FALSE);
1904         g_return_val_if_fail (label != NULL, FALSE);
1905
1906         return _secret_util_set_property_sync (G_DBUS_PROXY (self), "Label",
1907                                                g_variant_new_string (label),
1908                                                cancellable, error);
1909 }
1910
1911 /**
1912  * secret_collection_get_locked:
1913  * @self: a collection
1914  *
1915  * Get whether the collection is locked or not.
1916  *
1917  * Use secret_service_lock() or secret_service_unlock() to lock or unlock the
1918  * collection.
1919  *
1920  * Returns: whether the collection is locked or not
1921  */
1922 gboolean
1923 secret_collection_get_locked (SecretCollection *self)
1924 {
1925         GVariant *variant;
1926         gboolean locked;
1927
1928         g_return_val_if_fail (SECRET_IS_COLLECTION (self), TRUE);
1929
1930         variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Locked");
1931         g_return_val_if_fail (variant != NULL, TRUE);
1932
1933         locked = g_variant_get_boolean (variant);
1934         g_variant_unref (variant);
1935
1936         return locked;
1937 }
1938
1939 /**
1940  * secret_collection_get_created:
1941  * @self: a collection
1942  *
1943  * Get the created date and time of the collection. The return value is
1944  * the number of seconds since the unix epoch, January 1st 1970.
1945  *
1946  * Returns: the created date and time
1947  */
1948 guint64
1949 secret_collection_get_created (SecretCollection *self)
1950 {
1951         GVariant *variant;
1952         guint64 created;
1953
1954         g_return_val_if_fail (SECRET_IS_COLLECTION (self), TRUE);
1955
1956         variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Created");
1957         g_return_val_if_fail (variant != NULL, 0);
1958
1959         created = g_variant_get_uint64 (variant);
1960         g_variant_unref (variant);
1961
1962         return created;
1963 }
1964
1965 /**
1966  * secret_collection_get_modified:
1967  * @self: a collection
1968  *
1969  * Get the modified date and time of the collection. The return value is
1970  * the number of seconds since the unix epoch, January 1st 1970.
1971  *
1972  * Returns: the modified date and time
1973  */
1974 guint64
1975 secret_collection_get_modified (SecretCollection *self)
1976 {
1977         GVariant *variant;
1978         guint64 modified;
1979
1980         g_return_val_if_fail (SECRET_IS_COLLECTION (self), TRUE);
1981
1982         variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Modified");
1983         g_return_val_if_fail (variant != NULL, 0);
1984
1985         modified = g_variant_get_uint64 (variant);
1986         g_variant_unref (variant);
1987
1988         return modified;
1989 }
1990
1991
1992 typedef struct {
1993         GCancellable *cancellable;
1994         gchar *alias;
1995         SecretCollection *collection;
1996 } ReadClosure;
1997
1998 static void
1999 read_closure_free (gpointer data)
2000 {
2001         ReadClosure *read = data;
2002         g_free (read->alias);
2003         if (read->collection)
2004                 g_object_unref (read->collection);
2005         if (read->cancellable)
2006                 g_object_unref (read->cancellable);
2007         g_slice_free (ReadClosure, read);
2008 }
2009
2010 static void
2011 on_read_alias_collection (GObject *source,
2012                           GAsyncResult *result,
2013                           gpointer user_data)
2014 {
2015         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
2016         ReadClosure *read = g_simple_async_result_get_op_res_gpointer (async);
2017         GError *error = NULL;
2018
2019         read->collection = secret_collection_new_for_dbus_path_finish (result, &error);
2020         if (error != NULL)
2021                 g_simple_async_result_take_error (async, error);
2022
2023         g_simple_async_result_complete (async);
2024         g_object_unref (async);
2025 }
2026
2027 static void
2028 on_read_alias_path (GObject *source,
2029                     GAsyncResult *result,
2030                     gpointer user_data)
2031 {
2032         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
2033         ReadClosure *read = g_simple_async_result_get_op_res_gpointer (async);
2034         SecretService *self = SECRET_SERVICE (source);
2035         GError *error = NULL;
2036         gchar *collection_path;
2037
2038         collection_path = secret_service_read_alias_dbus_path_finish (self, result, &error);
2039         if (error == NULL) {
2040
2041                 /* No collection for this alias */
2042                 if (collection_path == NULL) {
2043                         g_simple_async_result_complete (async);
2044
2045                 } else {
2046                         read->collection = _secret_service_find_collection_instance (self,
2047                                                                                      collection_path);
2048                         if (read->collection != NULL) {
2049                                 g_simple_async_result_complete (async);
2050
2051                         /* No collection loaded, but valid path, load */
2052                         } else {
2053                                 secret_collection_new_for_dbus_path (self, collection_path,
2054                                                                      SECRET_COLLECTION_NONE,
2055                                                                      read->cancellable,
2056                                                                      on_read_alias_collection,
2057                                                                      g_object_ref (async));
2058                         }
2059                 }
2060
2061         } else {
2062                 g_simple_async_result_take_error (async, error);
2063                 g_simple_async_result_complete (async);
2064         }
2065
2066         g_free (collection_path);
2067         g_object_unref (async);
2068 }
2069
2070 static void
2071 on_read_alias_service (GObject *source,
2072                        GAsyncResult *result,
2073                        gpointer user_data)
2074 {
2075         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
2076         ReadClosure *read = g_simple_async_result_get_op_res_gpointer (async);
2077         SecretService *service;
2078         GError *error = NULL;
2079
2080         service = secret_service_get_finish (result, &error);
2081         if (error == NULL) {
2082                 secret_service_read_alias_dbus_path (service, read->alias, read->cancellable,
2083                                                      on_read_alias_path, g_object_ref (async));
2084                 g_object_unref (service);
2085
2086         } else {
2087                 g_simple_async_result_take_error (async, error);
2088                 g_simple_async_result_complete (async);
2089         }
2090
2091         g_object_unref (async);
2092 }
2093
2094 /**
2095  * secret_collection_for_alias:
2096  * @service: (allow-none): a secret service object
2097  * @alias: the alias to lookup
2098  * @cancellable: (allow-none): optional cancellation object
2099  * @callback: called when the operation completes
2100  * @user_data: data to pass to the callback
2101  *
2102  * Lookup which collection is assigned to this alias. Aliases help determine
2103  * well known collections, such as 'default'.
2104  *
2105  * If @service is NULL, then secret_service_get() will be called to get
2106  * the default #SecretService proxy.
2107  *
2108  * This method will return immediately and complete asynchronously.
2109  */
2110 void
2111 secret_collection_for_alias (SecretService *service,
2112                              const gchar *alias,
2113                              GCancellable *cancellable,
2114                              GAsyncReadyCallback callback,
2115                              gpointer user_data)
2116 {
2117         GSimpleAsyncResult *async;
2118         ReadClosure *read;
2119
2120         g_return_if_fail (service == NULL || SECRET_IS_SERVICE (service));
2121         g_return_if_fail (alias != NULL);
2122         g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
2123
2124         async = g_simple_async_result_new (NULL, callback, user_data,
2125                                            secret_collection_for_alias);
2126         read = g_slice_new0 (ReadClosure);
2127         read->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
2128         read->alias = g_strdup (alias);
2129         g_simple_async_result_set_op_res_gpointer (async, read, read_closure_free);
2130
2131         if (service == NULL) {
2132                 secret_service_get (SECRET_SERVICE_NONE, cancellable,
2133                                     on_read_alias_service, g_object_ref (async));
2134         } else {
2135                 secret_service_read_alias_dbus_path (service, read->alias, read->cancellable,
2136                                                      on_read_alias_path, g_object_ref (async));
2137         }
2138
2139         g_object_unref (async);
2140 }
2141
2142 /**
2143  * secret_collection_for_alias_finish:
2144  * @result: asynchronous result passed to callback
2145  * @error: location to place error on failure
2146  *
2147  * Finish an asynchronous operation to lookup which collection is assigned
2148  * to an alias.
2149  *
2150  * Returns: (transfer full): the collection, or %NULL if none assigned to the alias
2151  */
2152 SecretCollection *
2153 secret_collection_for_alias_finish (GAsyncResult *result,
2154                                     GError **error)
2155 {
2156         GSimpleAsyncResult *async;
2157         ReadClosure *read;
2158
2159         g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL,
2160                               secret_collection_for_alias), NULL);
2161         g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2162
2163         async = G_SIMPLE_ASYNC_RESULT (result);
2164         if (g_simple_async_result_propagate_error (async, error))
2165                 return NULL;
2166         read = g_simple_async_result_get_op_res_gpointer (async);
2167         if (read->collection)
2168                 g_object_ref (read->collection);
2169         return read->collection;
2170 }
2171
2172 /**
2173  * secret_collection_for_alias_sync:
2174  * @service: (allow-none): a secret service object
2175  * @alias: the alias to lookup
2176  * @cancellable: (allow-none): optional cancellation object
2177  * @error: location to place error on failure
2178  *
2179  * Lookup which collection is assigned to this alias. Aliases help determine
2180  * well known collections, such as 'default'.
2181  *
2182  * If @service is NULL, then secret_service_get_sync() will be called to get
2183  * the default #SecretService proxy.
2184  *
2185  * This method may block and should not be used in user interface threads.
2186  *
2187  * Returns: (transfer full): the collection, or %NULL if none assigned to the alias
2188  */
2189 SecretCollection *
2190 secret_collection_for_alias_sync (SecretService *service,
2191                                   const gchar *alias,
2192                                   GCancellable *cancellable,
2193                                   GError **error)
2194 {
2195         SecretCollection *collection;
2196         gchar *collection_path;
2197
2198         g_return_val_if_fail (service == NULL || SECRET_IS_SERVICE (service), NULL);
2199         g_return_val_if_fail (alias != NULL, NULL);
2200         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
2201         g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2202
2203         collection_path = secret_service_read_alias_dbus_path_sync (service, alias,
2204                                                                     cancellable, error);
2205         if (collection_path == NULL)
2206                 return NULL;
2207
2208         /* No collection for this alias */
2209         if (collection_path == NULL) {
2210                 collection = NULL;
2211
2212         } else {
2213                 collection = _secret_service_find_collection_instance (service,
2214                                                                        collection_path);
2215
2216                 /* No collection loaded, but valid path, load */
2217                 if (collection == NULL) {
2218                         collection = secret_collection_new_for_dbus_path_sync (service, collection_path,
2219                                                                                SECRET_COLLECTION_LOAD_ITEMS,
2220                                                                                cancellable, error);
2221                 }
2222         }
2223
2224         g_free (collection_path);
2225         return collection;
2226 }