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