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