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