Rename the library subdirectory to libsecret
[platform/upstream/libsecret.git] / libsecret / secret-service.c
1 /* libsecret - GLib wrapper for Secret Service
2  *
3  * Copyright 2011 Collabora Ltd.
4  * Copyright 2012 Red Hat Inc.
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published
8  * by the Free Software Foundation; either version 2.1 of the licence or (at
9  * your option) any later version.
10  *
11  * See the included COPYING file for more information.
12  *
13  * Author: Stef Walter <stefw@gnome.org>
14  */
15
16 #include "config.h"
17
18 #include "secret-collection.h"
19 #include "secret-dbus-generated.h"
20 #include "secret-enum-types.h"
21 #include "secret-item.h"
22 #include "secret-paths.h"
23 #include "secret-private.h"
24 #include "secret-service.h"
25 #include "secret-types.h"
26 #include "secret-value.h"
27
28 #include "egg/egg-secure-memory.h"
29
30 /**
31  * SECTION:secret-service
32  * @title: SecretService
33  * @short_description: the Secret Service
34  *
35  * A #SecretService object represents the Secret Service implementation which
36  * runs as a D-Bus service.
37  *
38  * Normally a single #SecretService object can be shared between multiple
39  * callers. The secret_service_get() method is used to access this #SecretService
40  * object. If a new independent #SecretService object is required, use
41  * secret_service_new().
42  *
43  * In order to securely transfer secrets to the Sercret Service, an session
44  * is established. This session can be established while initializing a
45  * #SecretService object by passing the %SECRET_SERVICE_OPEN_SESSION flag
46  * to the secret_service_get() or secret_service_new() functions. In order to
47  * establish a session on an already existing #SecretService, use the
48  * secret_service_ensure_session() function.
49  *
50  * To search for items, use the secret_service_search() method.
51  *
52  * Multiple collections can exist in the Secret Service, each of which contains
53  * secret items. In order to instantiate #SecretCollection objects which
54  * represent those collections while initializing a #SecretService then pass
55  * the %SECRET_SERVICE_LOAD_COLLECTIONS flag to the secret_service_get() or
56  * secret_service_new() functions. In order to establish a session on an already
57  * existing #SecretService, use the secret_service_load_collections() function.
58  * To access the list of collections use secret_service_get_collections().
59  *
60  * Certain actions on the Secret Service require user prompting to complete,
61  * such as creating a collection, or unlocking a collection. When such a prompt
62  * is necessary, then a #SecretPrompt object is created by this library, and
63  * passed to the secret_service_prompt() method. In this way it is handled
64  * automatically.
65  *
66  * In order to customize prompt handling, override the <literal>prompt_async</literal>
67  * and <literal>prompt_finish</literal> virtual methods of the #SecretService class.
68  *
69  * Stability: Unstable
70  */
71
72 /**
73  * SecretService:
74  *
75  * A proxy object representing the Secret Service.
76  */
77
78 /**
79  * SecretServiceClass:
80  * @parent_class: the parent class
81  * @collection_gtype: the #GType of the #SecretCollection objects instantiated
82  *                    by the #SecretService proxy
83  * @item_gtype: the #GType of the #SecretItem objects instantiated by the
84  *              #SecretService proxy
85  * @prompt_async: called to perform asynchronous prompting when necessary
86  * @prompt_finish: called to complete an asynchronous prompt operation
87  * @prompt_sync: called to perform synchronous prompting when necessary
88  *
89  * The class for #SecretService.
90  */
91
92 /**
93  * SecretServiceFlags:
94  * @SECRET_SERVICE_NONE: no flags for initializing the #SecretService
95  * @SECRET_SERVICE_OPEN_SESSION: establish a session for transfer of secrets
96  *                               while initializing the #SecretService
97  * @SECRET_SERVICE_LOAD_COLLECTIONS: load collections while initializing the
98  *                                   #SecretService
99  *
100  * Flags which determine which parts of the #SecretService proxy are initialized
101  * during a secret_service_get() or secret_service_new() operation.
102  */
103
104 EGG_SECURE_GLIB_DEFINITIONS ();
105
106 GQuark _secret_error_quark = 0;
107 static const gchar *default_bus_name = SECRET_SERVICE_BUS_NAME;
108
109 enum {
110         PROP_0,
111         PROP_FLAGS,
112         PROP_COLLECTIONS
113 };
114
115 struct _SecretServicePrivate {
116         /* No change between construct and finalize */
117         GCancellable *cancellable;
118         SecretServiceFlags init_flags;
119
120         /* Locked by mutex */
121         GMutex mutex;
122         gpointer session;
123         GHashTable *collections;
124 };
125
126 G_LOCK_DEFINE (service_instance);
127 static gpointer service_instance = NULL;
128 static guint service_watch = 0;
129
130 static GInitableIface *secret_service_initable_parent_iface = NULL;
131
132 static GAsyncInitableIface *secret_service_async_initable_parent_iface = NULL;
133
134 static void   secret_service_initable_iface         (GInitableIface *iface);
135
136 static void   secret_service_async_initable_iface   (GAsyncInitableIface *iface);
137
138 G_DEFINE_TYPE_WITH_CODE (SecretService, secret_service, G_TYPE_DBUS_PROXY,
139                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, secret_service_initable_iface);
140                          G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, secret_service_async_initable_iface);
141 );
142
143 static SecretService *
144 service_get_instance (void)
145 {
146         SecretService *instance = NULL;
147
148         G_LOCK (service_instance);
149         if (service_instance != NULL)
150                 instance = g_object_ref (service_instance);
151         G_UNLOCK (service_instance);
152
153         return instance;
154 }
155
156 static gboolean
157 service_uncache_instance (SecretService *which)
158 {
159         SecretService *instance = NULL;
160         guint watch = 0;
161         gboolean matched = FALSE;
162
163         G_LOCK (service_instance);
164         if (which == NULL || service_instance == which) {
165                 instance = service_instance;
166                 service_instance = NULL;
167                 watch = service_watch;
168                 service_watch = 0;
169                 matched = TRUE;
170         }
171         G_UNLOCK (service_instance);
172
173         if (instance != NULL)
174                 g_object_unref (instance);
175         if (watch != 0)
176                 g_bus_unwatch_name (watch);
177
178         return matched;
179 }
180
181 static void
182 on_service_instance_vanished (GDBusConnection *connection,
183                               const gchar *name,
184                               gpointer user_data)
185 {
186         if (!service_uncache_instance (user_data)) {
187                 g_warning ("Global default SecretService instance out of sync "
188                            "with the watch for its DBus name");
189         }
190 }
191
192 static void
193 service_cache_instance (SecretService *instance)
194 {
195         GDBusProxy *proxy;
196         guint watch;
197
198         g_object_ref (instance);
199         proxy = G_DBUS_PROXY (instance);
200         watch = g_bus_watch_name_on_connection (g_dbus_proxy_get_connection (proxy),
201                                                 g_dbus_proxy_get_name (proxy),
202                                                 G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
203                                                 NULL, on_service_instance_vanished,
204                                                 instance, NULL);
205
206         G_LOCK (service_instance);
207         if (service_instance == NULL) {
208                 service_instance = instance;
209                 instance = NULL;
210                 service_watch = watch;
211                 watch = 0;
212         }
213         G_UNLOCK (service_instance);
214
215         if (instance != NULL)
216                 g_object_unref (instance);
217         if (watch != 0)
218                 g_bus_unwatch_name (watch);
219 }
220
221 static void
222 secret_service_init (SecretService *self)
223 {
224         self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, SECRET_TYPE_SERVICE,
225                                                 SecretServicePrivate);
226
227         g_mutex_init (&self->pv->mutex);
228         self->pv->cancellable = g_cancellable_new ();
229 }
230
231 static void
232 secret_service_get_property (GObject *obj,
233                              guint prop_id,
234                              GValue *value,
235                              GParamSpec *pspec)
236 {
237         SecretService *self = SECRET_SERVICE (obj);
238
239         switch (prop_id) {
240         case PROP_FLAGS:
241                 g_value_set_flags (value, secret_service_get_flags (self));
242                 break;
243         case PROP_COLLECTIONS:
244                 g_value_take_boxed (value, secret_service_get_collections (self));
245                 break;
246         default:
247                 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
248                 break;
249         }
250 }
251
252 static void
253 secret_service_set_property (GObject *obj,
254                              guint prop_id,
255                              const GValue *value,
256                              GParamSpec *pspec)
257 {
258         SecretService *self = SECRET_SERVICE (obj);
259
260         switch (prop_id) {
261         case PROP_FLAGS:
262                 self->pv->init_flags = g_value_get_flags (value);
263                 break;
264         default:
265                 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
266                 break;
267         }
268 }
269
270 static void
271 secret_service_dispose (GObject *obj)
272 {
273         SecretService *self = SECRET_SERVICE (obj);
274
275         g_cancellable_cancel (self->pv->cancellable);
276
277         G_OBJECT_CLASS (secret_service_parent_class)->dispose (obj);
278 }
279
280 static void
281 secret_service_finalize (GObject *obj)
282 {
283         SecretService *self = SECRET_SERVICE (obj);
284
285         _secret_session_free (self->pv->session);
286         if (self->pv->collections)
287                 g_hash_table_destroy (self->pv->collections);
288         g_clear_object (&self->pv->cancellable);
289         g_mutex_clear (&self->pv->mutex);
290
291         G_OBJECT_CLASS (secret_service_parent_class)->finalize (obj);
292 }
293
294 static GVariant *
295 secret_service_real_prompt_sync (SecretService *self,
296                                  SecretPrompt *prompt,
297                                  GCancellable *cancellable,
298                                  const GVariantType *return_type,
299                                  GError **error)
300 {
301         return secret_prompt_perform_sync (prompt, 0, cancellable, return_type, error);
302 }
303
304 static void
305 on_real_prompt_completed (GObject *source,
306                           GAsyncResult *result,
307                           gpointer user_data)
308 {
309         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
310         GError *error = NULL;
311         GVariant *retval;
312
313         retval = secret_prompt_perform_finish (SECRET_PROMPT (source), result, NULL, &error);
314         if (retval != NULL)
315                 g_simple_async_result_set_op_res_gpointer (res, retval, (GDestroyNotify)g_variant_unref);
316         if (error != NULL)
317                 g_simple_async_result_take_error (res, error);
318         g_simple_async_result_complete (res);
319         g_object_unref (res);
320 }
321
322 static void
323 secret_service_real_prompt_async (SecretService *self,
324                                   SecretPrompt *prompt,
325                                   GCancellable *cancellable,
326                                   GAsyncReadyCallback callback,
327                                   gpointer user_data)
328 {
329         GSimpleAsyncResult *res;
330
331         res =  g_simple_async_result_new (G_OBJECT (self), callback, user_data,
332                                           secret_service_real_prompt_async);
333
334         secret_prompt_perform (prompt, 0, cancellable,
335                                on_real_prompt_completed,
336                                g_object_ref (res));
337
338         g_object_unref (res);
339 }
340
341 static GVariant *
342 secret_service_real_prompt_finish (SecretService *self,
343                                    GAsyncResult *result,
344                                    const GVariantType *return_type,
345                                    GError **error)
346 {
347         GSimpleAsyncResult *res;
348         GVariant *retval;
349         gchar *string;
350
351         g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
352                               secret_service_real_prompt_async), NULL);
353
354         res = G_SIMPLE_ASYNC_RESULT (result);
355         if (g_simple_async_result_propagate_error (res, error))
356                 return NULL;
357
358         retval = g_simple_async_result_get_op_res_gpointer (res);
359         if (retval == NULL)
360                 return NULL;
361
362         if (return_type != NULL && !g_variant_is_of_type (retval, return_type)) {
363                 string = g_variant_type_dup_string (return_type);
364                 g_warning ("received unexpected result type %s from prompt's Completed signal instead of expected %s",
365                            g_variant_get_type_string (retval), string);
366                 g_free (string);
367                 return NULL;
368         }
369
370         return g_variant_ref (retval);
371 }
372
373 static void
374 handle_property_changed (SecretService *self,
375                          const gchar *property_name,
376                          GVariant *value)
377 {
378         gboolean perform;
379
380         g_variant_ref_sink (value);
381
382         if (g_str_equal (property_name, "Collections")) {
383
384                 g_mutex_lock (&self->pv->mutex);
385                 perform = self->pv->collections != NULL;
386                 g_mutex_unlock (&self->pv->mutex);
387
388                 if (perform)
389                         secret_service_load_collections (self, self->pv->cancellable, NULL, NULL);
390         }
391
392         g_variant_unref (value);
393 }
394
395 static void
396 secret_service_properties_changed (GDBusProxy *proxy,
397                                    GVariant *changed_properties,
398                                    const gchar* const *invalidated_properties)
399 {
400         SecretService *self = SECRET_SERVICE (proxy);
401         gchar *property_name;
402         GVariantIter iter;
403         GVariant *value;
404
405         g_object_freeze_notify (G_OBJECT (self));
406
407         g_variant_iter_init (&iter, changed_properties);
408         while (g_variant_iter_loop (&iter, "{sv}", &property_name, &value))
409                 handle_property_changed (self, property_name, value);
410
411         g_object_thaw_notify (G_OBJECT (self));
412 }
413
414 static void
415 secret_service_signal (GDBusProxy *proxy,
416                        const gchar *sender_name,
417                        const gchar *signal_name,
418                        GVariant *parameters)
419 {
420         SecretService *self = SECRET_SERVICE (proxy);
421         SecretCollection *collection;
422         const gchar *collection_path;
423         GVariantBuilder builder;
424         gboolean found = FALSE;
425         GVariantIter iter;
426         GVariant *value;
427         GVariant *paths;
428         GVariant *path;
429
430         /*
431          * Remember that these signals come from a time before PropertiesChanged.
432          * We support them because they're in the spec, and ksecretservice uses them.
433          */
434
435         paths = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Collections");
436
437         /* A new collection was added, add it to the Collections property */
438         if (g_str_equal (signal_name, SECRET_SIGNAL_COLLECTION_CREATED)) {
439                 g_variant_get (parameters, "(@o)", &value);
440                 g_variant_builder_init (&builder, G_VARIANT_TYPE ("ao"));
441                 g_variant_iter_init (&iter, paths);
442                 while ((path = g_variant_iter_next_value (&iter)) != NULL) {
443                         if (g_variant_equal (path, value)) {
444                                 found = TRUE;
445                                 break;
446                         }
447                         g_variant_builder_add_value (&builder, path);
448                         g_variant_unref (path);
449                 }
450                 if (!found) {
451                         g_variant_builder_add_value (&builder, value);
452                         handle_property_changed (self, "Collections", g_variant_builder_end (&builder));
453                 }
454                 g_variant_builder_clear (&builder);
455                 g_variant_unref (value);
456
457         /* A collection was deleted, remove it from the Collections property */
458         } else if (g_str_equal (signal_name, SECRET_SIGNAL_COLLECTION_DELETED)) {
459                 g_variant_get (parameters, "(@o)", &value);
460                 g_variant_builder_init (&builder, G_VARIANT_TYPE ("ao"));
461                 g_variant_iter_init (&iter, paths);
462                 while ((path = g_variant_iter_next_value (&iter)) != NULL) {
463                         if (g_variant_equal (path, value))
464                                 found = TRUE;
465                         else
466                                 g_variant_builder_add_value (&builder, path);
467                         g_variant_unref (path);
468                 }
469                 if (found)
470                         handle_property_changed (self, "Collections", g_variant_builder_end (&builder));
471                 g_variant_unref (value);
472
473         /* The collection changed, update it */
474         } else if (g_str_equal (signal_name, SECRET_SIGNAL_COLLECTION_CHANGED)) {
475                 g_variant_get (parameters, "(&o)", &collection_path);
476
477                 g_mutex_lock (&self->pv->mutex);
478
479                 if (self->pv->collections)
480                         collection = g_hash_table_lookup (self->pv->collections, collection_path);
481                 else
482                         collection = NULL;
483                 if (collection)
484                         g_object_ref (collection);
485
486                 g_mutex_unlock (&self->pv->mutex);
487
488                 if (collection) {
489                         secret_collection_refresh (collection);
490                         g_object_unref (collection);
491                 }
492         }
493
494         g_variant_unref (paths);
495 }
496
497 static void
498 secret_service_class_init (SecretServiceClass *klass)
499 {
500         GObjectClass *object_class = G_OBJECT_CLASS (klass);
501         GDBusProxyClass *proxy_class = G_DBUS_PROXY_CLASS (klass);
502
503         object_class->get_property = secret_service_get_property;
504         object_class->set_property = secret_service_set_property;
505         object_class->dispose = secret_service_dispose;
506         object_class->finalize = secret_service_finalize;
507
508         proxy_class->g_properties_changed = secret_service_properties_changed;
509         proxy_class->g_signal = secret_service_signal;
510
511         klass->prompt_sync = secret_service_real_prompt_sync;
512         klass->prompt_async = secret_service_real_prompt_async;
513         klass->prompt_finish = secret_service_real_prompt_finish;
514
515         klass->item_gtype = SECRET_TYPE_ITEM;
516         klass->collection_gtype = SECRET_TYPE_COLLECTION;
517
518         /**
519          * SecretService:flags:
520          *
521          * A set of flags describing which parts of the secret service have
522          * been initialized.
523          */
524         g_object_class_install_property (object_class, PROP_FLAGS,
525                      g_param_spec_flags ("flags", "Flags", "Service flags",
526                                          secret_service_flags_get_type (), SECRET_SERVICE_NONE,
527                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
528
529         /**
530          * SecretService:collections:
531          *
532          * A list of #SecretCollection objects representing the collections in
533          * the Secret Service. This list may be %NULL if the collections have
534          * not been loaded.
535          *
536          * To load the collections, specify the %SECRET_SERVICE_LOAD_COLLECTIONS
537          * initialization flag when calling the secret_service_get() or
538          * secret_service_new() functions. Or call the secret_service_load_collections()
539          * method.
540          */
541         g_object_class_install_property (object_class, PROP_COLLECTIONS,
542                      g_param_spec_boxed ("collections", "Collections", "Secret Service Collections",
543                                          _secret_list_get_type (), G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
544
545         g_type_class_add_private (klass, sizeof (SecretServicePrivate));
546
547         /* Initialize this error domain, registers dbus errors */
548         _secret_error_quark = secret_error_get_quark ();
549 }
550
551 typedef struct {
552         GCancellable *cancellable;
553         SecretServiceFlags flags;
554 } InitClosure;
555
556 static void
557 init_closure_free (gpointer data)
558 {
559         InitClosure *closure = data;
560         g_clear_object (&closure->cancellable);
561         g_slice_free (InitClosure, closure);
562 }
563
564 static gboolean
565 service_ensure_for_flags_sync (SecretService *self,
566                                SecretServiceFlags flags,
567                                GCancellable *cancellable,
568                                GError **error)
569 {
570         if (flags & SECRET_SERVICE_OPEN_SESSION)
571                 if (!secret_service_ensure_session_sync (self, cancellable, error))
572                         return FALSE;
573
574         if (flags & SECRET_SERVICE_LOAD_COLLECTIONS)
575                 if (!secret_service_load_collections_sync (self, cancellable, error))
576                         return FALSE;
577
578         return TRUE;
579 }
580
581 static void
582 on_load_collections (GObject *source,
583                      GAsyncResult *result,
584                      gpointer user_data)
585 {
586         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
587         SecretService *self = SECRET_SERVICE (source);
588         GError *error = NULL;
589
590         if (!secret_service_load_collections_finish (self, result, &error))
591                 g_simple_async_result_take_error (res, error);
592
593         g_simple_async_result_complete (res);
594         g_object_unref (res);
595 }
596
597 static void
598 on_ensure_session (GObject *source,
599                    GAsyncResult *result,
600                    gpointer user_data)
601 {
602         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
603         InitClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
604         SecretService *self = SECRET_SERVICE (source);
605         GError *error = NULL;
606
607         if (!secret_service_ensure_session_finish (self, result, &error)) {
608                 g_simple_async_result_take_error (res, error);
609                 g_simple_async_result_complete (res);
610
611         } else if (closure->flags & SECRET_SERVICE_LOAD_COLLECTIONS) {
612                 secret_service_load_collections (self, closure->cancellable,
613                                                  on_load_collections, g_object_ref (res));
614
615         } else {
616                 g_simple_async_result_complete_in_idle (res);
617         }
618
619         g_object_unref (res);
620 }
621
622 static void
623 service_ensure_for_flags_async (SecretService *self,
624                                 SecretServiceFlags flags,
625                                 GSimpleAsyncResult *res)
626 {
627         InitClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
628
629         closure->flags = flags;
630
631         if (closure->flags & SECRET_SERVICE_OPEN_SESSION)
632                 secret_service_ensure_session (self, closure->cancellable,
633                                                on_ensure_session, g_object_ref (res));
634
635         else if (closure->flags & SECRET_SERVICE_LOAD_COLLECTIONS)
636                 secret_service_load_collections (self, closure->cancellable,
637                                                  on_load_collections, g_object_ref (res));
638
639         else
640                 g_simple_async_result_complete_in_idle (res);
641 }
642
643 static gboolean
644 secret_service_initable_init (GInitable *initable,
645                               GCancellable *cancellable,
646                               GError **error)
647 {
648         SecretService *self;
649
650         if (!secret_service_initable_parent_iface->init (initable, cancellable, error))
651                 return FALSE;
652
653         self = SECRET_SERVICE (initable);
654         return service_ensure_for_flags_sync (self, self->pv->init_flags, cancellable, error);
655 }
656
657 static void
658 secret_service_initable_iface (GInitableIface *iface)
659 {
660         secret_service_initable_parent_iface = g_type_interface_peek_parent (iface);
661
662         iface->init = secret_service_initable_init;
663 }
664
665 static void
666 on_init_base (GObject *source,
667               GAsyncResult *result,
668               gpointer user_data)
669 {
670         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
671         SecretService *self = SECRET_SERVICE (source);
672         GError *error = NULL;
673
674         if (!secret_service_async_initable_parent_iface->init_finish (G_ASYNC_INITABLE (self),
675                                                                       result, &error)) {
676                 g_simple_async_result_take_error (res, error);
677                 g_simple_async_result_complete (res);
678         } else {
679                 service_ensure_for_flags_async (self, self->pv->init_flags, res);
680         }
681
682         g_object_unref (res);
683 }
684
685 static void
686 secret_service_async_initable_init_async (GAsyncInitable *initable,
687                                           int io_priority,
688                                           GCancellable *cancellable,
689                                           GAsyncReadyCallback callback,
690                                           gpointer user_data)
691 {
692         GSimpleAsyncResult *res;
693         InitClosure *closure;
694
695         res = g_simple_async_result_new (G_OBJECT (initable), callback, user_data,
696                                          secret_service_async_initable_init_async);
697         closure = g_slice_new0 (InitClosure);
698         closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
699         g_simple_async_result_set_op_res_gpointer (res, closure, init_closure_free);
700
701         secret_service_async_initable_parent_iface->init_async (initable, io_priority,
702                                                                 cancellable,
703                                                                 on_init_base,
704                                                                 g_object_ref (res));
705
706         g_object_unref (res);
707 }
708
709 static gboolean
710 secret_service_async_initable_init_finish (GAsyncInitable *initable,
711                                            GAsyncResult *result,
712                                            GError **error)
713 {
714         g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (initable),
715                               secret_service_async_initable_init_async), FALSE);
716
717         if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
718                 return FALSE;
719
720         return TRUE;
721 }
722
723 static void
724 secret_service_async_initable_iface (GAsyncInitableIface *iface)
725 {
726         secret_service_async_initable_parent_iface = g_type_interface_peek_parent (iface);
727
728         iface->init_async = secret_service_async_initable_init_async;
729         iface->init_finish = secret_service_async_initable_init_finish;
730 }
731
732 void
733 _secret_service_set_default_bus_name (const gchar *bus_name)
734 {
735         g_return_if_fail (bus_name != NULL);
736         default_bus_name = bus_name;
737 }
738
739 /**
740  * secret_service_get:
741  * @flags: flags for which service functionality to ensure is initialized
742  * @cancellable: optional cancellation object
743  * @callback: called when the operation completes
744  * @user_data: data to be passed to the callback
745  *
746  * Get a #SecretService proxy for the Secret Service. If such a proxy object
747  * already exists, then the same proxy is returned.
748  *
749  * If @flags contains any flags of which parts of the secret service to
750  * ensure are initialized, then those will be initialized before completing.
751  *
752  * This method will return immediately and complete asynchronously.
753  */
754 void
755 secret_service_get (SecretServiceFlags flags,
756                     GCancellable *cancellable,
757                     GAsyncReadyCallback callback,
758                     gpointer user_data)
759 {
760         SecretService *service = NULL;
761         GSimpleAsyncResult *res;
762         InitClosure *closure;
763
764         service = service_get_instance ();
765
766         /* Create a whole new service */
767         if (service == NULL) {
768                 g_async_initable_new_async (SECRET_TYPE_SERVICE, G_PRIORITY_DEFAULT,
769                                             cancellable, callback, user_data,
770                                             "g-flags", G_DBUS_PROXY_FLAGS_NONE,
771                                             "g-interface-info", _secret_gen_service_interface_info (),
772                                             "g-name", default_bus_name,
773                                             "g-bus-type", G_BUS_TYPE_SESSION,
774                                             "g-object-path", SECRET_SERVICE_PATH,
775                                             "g-interface-name", SECRET_SERVICE_INTERFACE,
776                                             "flags", flags,
777                                             NULL);
778
779         /* Just have to ensure that the service matches flags */
780         } else {
781                 res = g_simple_async_result_new (G_OBJECT (service), callback,
782                                                  user_data, secret_service_get);
783                 closure = g_slice_new0 (InitClosure);
784                 closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
785                 closure->flags = flags;
786                 g_simple_async_result_set_op_res_gpointer (res, closure, init_closure_free);
787
788                 service_ensure_for_flags_async (service, flags, res);
789
790                 g_object_unref (service);
791                 g_object_unref (res);
792         }
793 }
794
795 /**
796  * secret_service_get_finish:
797  * @result: the asynchronous result passed to the callback
798  * @error: location to place an error on failure
799  *
800  * Complete an asynchronous operation to get a #SecretService proxy for the
801  * Secret Service.
802  *
803  * Returns: (transfer full): a new reference to a #SecretService proxy, which
804  *          should be released with g_object_unref().
805  */
806 SecretService *
807 secret_service_get_finish (GAsyncResult *result,
808                            GError **error)
809 {
810         GObject *service = NULL;
811         GObject *source_object;
812
813         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
814         g_return_val_if_fail (error == NULL || *error == NULL, NULL);
815
816         source_object = g_async_result_get_source_object (result);
817
818         /* Just ensuring that the service matches flags */
819         if (g_simple_async_result_is_valid (result, source_object, secret_service_get)) {
820                 if (!g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
821                         service = g_object_ref (source_object);
822
823         /* Creating a whole new service */
824         } else {
825                 service = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), result, error);
826                 if (service)
827                         service_cache_instance (SECRET_SERVICE (service));
828         }
829
830         if (source_object)
831                 g_object_unref (source_object);
832
833         if (service == NULL)
834                 return NULL;
835
836         return SECRET_SERVICE (service);
837 }
838
839 /**
840  * secret_service_get_sync:
841  * @flags: flags for which service functionality to ensure is initialized
842  * @cancellable: optional cancellation object
843  * @error: location to place an error on failure
844  *
845  * Get a #SecretService proxy for the Secret Service. If such a proxy object
846  * already exists, then the same proxy is returned.
847  *
848  * If @flags contains any flags of which parts of the secret service to
849  * ensure are initialized, then those will be initialized before returning.
850  *
851  * This method may block indefinitely and should not be used in user interface
852  * threads.
853  *
854  * Returns: (transfer full): a new reference to a #SecretService proxy, which
855  *          should be released with g_object_unref().
856  */
857 SecretService *
858 secret_service_get_sync (SecretServiceFlags flags,
859                          GCancellable *cancellable,
860                          GError **error)
861 {
862         SecretService *service = NULL;
863
864         service = service_get_instance ();
865
866         if (service == NULL) {
867                 service = g_initable_new (SECRET_TYPE_SERVICE, cancellable, error,
868                                           "g-flags", G_DBUS_PROXY_FLAGS_NONE,
869                                           "g-interface-info", _secret_gen_service_interface_info (),
870                                           "g-name", default_bus_name,
871                                           "g-bus-type", G_BUS_TYPE_SESSION,
872                                           "g-object-path", SECRET_SERVICE_PATH,
873                                           "g-interface-name", SECRET_SERVICE_INTERFACE,
874                                           "flags", flags,
875                                           NULL);
876
877                 if (service != NULL)
878                         service_cache_instance (service);
879
880         } else {
881                 if (!service_ensure_for_flags_sync (service, flags, cancellable, error)) {
882                         g_object_unref (service);
883                         return NULL;
884                 }
885         }
886
887         return service;
888 }
889
890 /**
891  * secret_service_disconnect:
892  *
893  * Disconnect the default #SecretService proxy returned by secret_service_get()
894  * and secret_service_get_sync().
895  *
896  * It is not necessary to call this function, but you may choose to do so at
897  * program exit. It is useful for testing that memory is not leaked.
898  *
899  * This function is safe to call at any time. But if other objects in this
900  * library are still referenced, then this will not result in all memory
901  * being freed.
902  */
903 void
904 secret_service_disconnect (void)
905 {
906         service_uncache_instance (NULL);
907 }
908
909 /**
910  * secret_service_new:
911  * @service_gtype: the GType of the new secret service
912  * @service_bus_name: (allow-none): the D-Bus service name of the secret service
913  * @flags: flags for which service functionality to ensure is initialized
914  * @cancellable: optional cancellation object
915  * @callback: called when the operation completes
916  * @user_data: data to be passed to the callback
917  *
918  * Create a new #SecretService proxy for the Secret Service.
919  *
920  * This function is rarely used, see secret_service_get() instead.
921  *
922  * The @service_gtype argument should be set to %SECRET_TYPE_SERVICE or a the type
923  * of a derived class.
924  *
925  * If @flags contains any flags of which parts of the secret service to
926  * ensure are initialized, then those will be initialized before returning.
927  *
928  * If @service_bus_name is %NULL then the default is used.
929  *
930  * This method will return immediately and complete asynchronously.
931  */
932 void
933 secret_service_new (GType service_gtype,
934                     const gchar *service_bus_name,
935                     SecretServiceFlags flags,
936                     GCancellable *cancellable,
937                     GAsyncReadyCallback callback,
938                     gpointer user_data)
939 {
940         g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
941         g_return_if_fail (g_type_is_a (service_gtype, SECRET_TYPE_SERVICE));
942
943         if (service_bus_name == NULL)
944                 service_bus_name = default_bus_name;
945
946         g_async_initable_new_async (service_gtype, G_PRIORITY_DEFAULT,
947                                     cancellable, callback, user_data,
948                                     "g-flags", G_DBUS_PROXY_FLAGS_NONE,
949                                     "g-interface-info", _secret_gen_service_interface_info (),
950                                     "g-name", service_bus_name,
951                                     "g-bus-type", G_BUS_TYPE_SESSION,
952                                     "g-object-path", SECRET_SERVICE_PATH,
953                                     "g-interface-name", SECRET_SERVICE_INTERFACE,
954                                     "flags", flags,
955                                     NULL);
956 }
957
958 /**
959  * secret_service_new_finish:
960  * @result: the asynchronous result passed to the callback
961  * @error: location to place an error on failure
962  *
963  * Complete an asynchronous operation to create a new #SecretService proxy for
964  * the Secret Service.
965  *
966  * Returns: (transfer full): a new reference to a #SecretService proxy, which
967  *          should be released with g_object_unref().
968  */
969 SecretService *
970 secret_service_new_finish (GAsyncResult *result,
971                            GError **error)
972 {
973         GObject *source_object;
974         GObject *object;
975
976         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
977         g_return_val_if_fail (error == NULL || *error == NULL, NULL);
978
979         source_object = g_async_result_get_source_object (result);
980         object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
981                                               result, error);
982         g_object_unref (source_object);
983
984         if (object == NULL)
985                 return NULL;
986
987         return SECRET_SERVICE (object);
988 }
989
990 /**
991  * secret_service_new_sync:
992  * @service_gtype: the GType of the new secret service
993  * @service_bus_name: (allow-none): the D-Bus service name of the secret service
994  * @flags: flags for which service functionality to ensure is initialized
995  * @cancellable: optional cancellation object
996  * @error: location to place an error on failure
997  *
998  * Create a new #SecretService proxy for the Secret Service.
999  *
1000  * This function is rarely used, see secret_service_get_sync() instead.
1001  *
1002  * The @service_gtype argument should be set to %SECRET_TYPE_SERVICE or a the
1003  * type of a derived class.
1004  *
1005  * If @flags contains any flags of which parts of the secret service to
1006  * ensure are initialized, then those will be initialized before returning.
1007  *
1008  * If @service_bus_name is %NULL then the default is used.
1009  *
1010  * This method may block indefinitely and should not be used in user interface
1011  * threads.
1012  *
1013  * Returns: (transfer full): a new reference to a #SecretService proxy, which
1014  *          should be released with g_object_unref().
1015  */
1016 SecretService *
1017 secret_service_new_sync (GType service_gtype,
1018                          const gchar *service_bus_name,
1019                          SecretServiceFlags flags,
1020                          GCancellable *cancellable,
1021                          GError **error)
1022 {
1023         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
1024         g_return_val_if_fail (g_type_is_a (service_gtype, SECRET_TYPE_SERVICE), NULL);
1025
1026         if (service_bus_name == NULL)
1027                 service_bus_name = default_bus_name;
1028
1029         return g_initable_new (service_gtype, cancellable, error,
1030                                "g-flags", G_DBUS_PROXY_FLAGS_NONE,
1031                                "g-interface-info", _secret_gen_service_interface_info (),
1032                                "g-name", service_bus_name,
1033                                "g-bus-type", G_BUS_TYPE_SESSION,
1034                                "g-object-path", SECRET_SERVICE_PATH,
1035                                "g-interface-name", SECRET_SERVICE_INTERFACE,
1036                                "flags", flags,
1037                                NULL);
1038 }
1039
1040 /**
1041  * secret_service_get_flags:
1042  * @self: the secret service proxy
1043  *
1044  * Get the flags representing what features of the #SecretService proxy
1045  * have been initialized.
1046  *
1047  * Use secret_service_ensure_session() or secret_service_load_collections()
1048  * to initialize further features and change the flags.
1049  *
1050  * Returns: the flags for features initialized
1051  */
1052 SecretServiceFlags
1053 secret_service_get_flags (SecretService *self)
1054 {
1055         SecretServiceFlags flags = 0;
1056
1057         g_return_val_if_fail (SECRET_IS_SERVICE (self), SECRET_SERVICE_NONE);
1058
1059         g_mutex_lock (&self->pv->mutex);
1060
1061         if (self->pv->session)
1062                 flags |= SECRET_SERVICE_OPEN_SESSION;
1063         if (self->pv->collections)
1064                 flags |= SECRET_SERVICE_LOAD_COLLECTIONS;
1065
1066         g_mutex_unlock (&self->pv->mutex);
1067
1068         return flags;
1069 }
1070
1071 /**
1072  * secret_service_get_collections:
1073  * @self: the secret service proxy
1074  *
1075  * Get a list of #SecretCollection objects representing all the collections
1076  * in the secret service.
1077  *
1078  * If the %SECRET_SERVICE_LOAD_COLLECTIONS flag was not specified when
1079  * initializing #SecretService proxy object, then this method will return
1080  * %NULL. Use secret_service_load_collections() to load the collections.
1081  *
1082  * Returns: (transfer full) (element-type Secret.Collection) (allow-none): a
1083  *          list of the collections in the secret service
1084  */
1085 GList *
1086 secret_service_get_collections (SecretService *self)
1087 {
1088         GList *l, *collections;
1089
1090         g_return_val_if_fail (SECRET_IS_SERVICE (self), NULL);
1091
1092         g_mutex_lock (&self->pv->mutex);
1093
1094         if (self->pv->collections == NULL) {
1095                 collections = NULL;
1096
1097         } else {
1098                 collections = g_hash_table_get_values (self->pv->collections);
1099                 for (l = collections; l != NULL; l = g_list_next (l))
1100                         g_object_ref (l->data);
1101         }
1102
1103         g_mutex_unlock (&self->pv->mutex);
1104
1105         return collections;
1106 }
1107
1108 SecretItem *
1109 _secret_service_find_item_instance (SecretService *self,
1110                                     const gchar *item_path)
1111 {
1112         SecretCollection *collection = NULL;
1113         gchar *collection_path;
1114         SecretItem *item;
1115
1116         collection_path = _secret_util_parent_path (item_path);
1117
1118         collection = _secret_service_find_collection_instance (self, collection_path);
1119
1120         g_free (collection_path);
1121
1122         if (collection == NULL)
1123                 return NULL;
1124
1125         item = _secret_collection_find_item_instance (collection, item_path);
1126         g_object_unref (collection);
1127
1128         return item;
1129 }
1130
1131 SecretCollection *
1132 _secret_service_find_collection_instance (SecretService *self,
1133                                           const gchar *collection_path)
1134 {
1135         SecretCollection *collection = NULL;
1136
1137         g_mutex_lock (&self->pv->mutex);
1138         if (self->pv->collections) {
1139                 collection = g_hash_table_lookup (self->pv->collections, collection_path);
1140                 if (collection != NULL)
1141                         g_object_ref (collection);
1142         }
1143         g_mutex_unlock (&self->pv->mutex);
1144
1145         return collection;
1146 }
1147
1148 SecretSession *
1149 _secret_service_get_session (SecretService *self)
1150 {
1151         SecretSession *session;
1152
1153         g_return_val_if_fail (SECRET_IS_SERVICE (self), NULL);
1154
1155         g_mutex_lock (&self->pv->mutex);
1156         session = self->pv->session;
1157         g_mutex_unlock (&self->pv->mutex);
1158
1159         return session;
1160 }
1161
1162 void
1163 _secret_service_take_session (SecretService *self,
1164                               SecretSession *session)
1165 {
1166         g_return_if_fail (SECRET_IS_SERVICE (self));
1167         g_return_if_fail (session != NULL);
1168
1169         g_mutex_lock (&self->pv->mutex);
1170         if (self->pv->session == NULL)
1171                 self->pv->session = session;
1172         else
1173                 _secret_session_free (session);
1174         g_mutex_unlock (&self->pv->mutex);
1175 }
1176
1177 /**
1178  * secret_service_get_session_algorithms:
1179  * @self: the secret service proxy
1180  *
1181  * Get the set of algorithms being used to transfer secrets between this
1182  * secret service proxy and the Secret Service itself.
1183  *
1184  * This will be %NULL if no session has been established. Use
1185  * secret_service_ensure_session() to establish a session.
1186  *
1187  * Returns: (allow-none): a string representing the algorithms for transferring
1188  *          secrets
1189  */
1190 const gchar *
1191 secret_service_get_session_algorithms (SecretService *self)
1192 {
1193         SecretSession *session;
1194         const gchar *algorithms;
1195
1196         g_return_val_if_fail (SECRET_IS_SERVICE (self), NULL);
1197
1198         g_mutex_lock (&self->pv->mutex);
1199         session = self->pv->session;
1200         algorithms = session ? _secret_session_get_algorithms (session) : NULL;
1201         g_mutex_unlock (&self->pv->mutex);
1202
1203         /* Session never changes once established, so can return const */
1204         return algorithms;
1205 }
1206
1207 /**
1208  * secret_service_get_session_dbus_path:
1209  * @self: the secret service proxy
1210  *
1211  * Get the D-Bus object path of the session object being used to transfer
1212  * secrets between this secret service proxy and the Secret Service itself.
1213  *
1214  * This will be %NULL if no session has been established. Use
1215  * secret_service_ensure_session() to establish a session.
1216  *
1217  * Returns: (allow-none): a string representing the D-Bus object path of the
1218  *          session
1219  */
1220 const gchar *
1221 secret_service_get_session_dbus_path (SecretService *self)
1222 {
1223         SecretSession *session;
1224         const gchar *path;
1225
1226         g_return_val_if_fail (SECRET_IS_SERVICE (self), NULL);
1227
1228         g_mutex_lock (&self->pv->mutex);
1229         session = self->pv->session;
1230         path = session ? _secret_session_get_path (session) : NULL;
1231         g_mutex_unlock (&self->pv->mutex);
1232
1233         /* Session never changes once established, so can return const */
1234         return path;
1235 }
1236
1237 /**
1238  * secret_service_ensure_session:
1239  * @self: the secret service
1240  * @cancellable: optional cancellation object
1241  * @callback: called when the operation completes
1242  * @user_data: data to be passed to the callback
1243  *
1244  * Ensure that the #SecretService proxy has established a session with the
1245  * Secret Service. This session is used to transfer secrets.
1246  *
1247  * It is not normally necessary to call this method, as the session is
1248  * established as necessary. You can also pass the %SECRET_SERVICE_OPEN_SESSION
1249  * to secret_service_get() in order to ensure that a session has been established
1250  * by the time you get the #SecretService proxy.
1251  *
1252  * This method will return immediately and complete asynchronously.
1253  */
1254 void
1255 secret_service_ensure_session (SecretService *self,
1256                                GCancellable *cancellable,
1257                                GAsyncReadyCallback callback,
1258                                gpointer user_data)
1259 {
1260         GSimpleAsyncResult *res;
1261         SecretSession *session;
1262
1263         g_return_if_fail (SECRET_IS_SERVICE (self));
1264         g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1265
1266         g_mutex_lock (&self->pv->mutex);
1267         session = self->pv->session;
1268         g_mutex_unlock (&self->pv->mutex);
1269
1270         if (session == NULL) {
1271                 _secret_session_open (self, cancellable, callback, user_data);
1272
1273         } else {
1274                 res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
1275                                                  secret_service_ensure_session);
1276                 g_simple_async_result_complete_in_idle (res);
1277                 g_object_unref (res);
1278         }
1279 }
1280
1281 /**
1282  * secret_service_ensure_session_finish:
1283  * @self: the secret service
1284  * @result: the asynchronous result passed to the callback
1285  * @error: location to place an error on failure
1286  *
1287  * Finish an asynchronous operation to ensure that the #SecretService proxy
1288  * has established a session with the Secret Service.
1289  *
1290  * Returns: whether a session is established or not
1291  */
1292 gboolean
1293 secret_service_ensure_session_finish (SecretService *self,
1294                                       GAsyncResult *result,
1295                                       GError **error)
1296 {
1297         g_return_val_if_fail (SECRET_IS_SERVICE (self), FALSE);
1298         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1299
1300         if (!g_simple_async_result_is_valid (result, G_OBJECT (self),
1301                                              secret_service_ensure_session)) {
1302                 if (!_secret_session_open_finish (result, error))
1303                         return FALSE;
1304         }
1305
1306         g_return_val_if_fail (self->pv->session != NULL, FALSE);
1307         return TRUE;
1308 }
1309
1310 /**
1311  * secret_service_ensure_session_sync:
1312  * @self: the secret service
1313  * @cancellable: optional cancellation object
1314  * @error: location to place an error on failure
1315  *
1316  * Ensure that the #SecretService proxy has established a session with the
1317  * Secret Service. This session is used to transfer secrets.
1318  *
1319  * It is not normally necessary to call this method, as the session is
1320  * established as necessary. You can also pass the %SECRET_SERVICE_OPEN_SESSION
1321  * to secret_service_get_sync() in order to ensure that a session has been
1322  * established by the time you get the #SecretService proxy.
1323  *
1324  * This method may block indefinitely and should not be used in user interface
1325  * threads.
1326  *
1327  * Returns: whether a session is established or not
1328  */
1329 gboolean
1330 secret_service_ensure_session_sync (SecretService *self,
1331                                     GCancellable *cancellable,
1332                                     GError **error)
1333 {
1334         SecretSync *sync;
1335         gboolean ret;
1336
1337         g_return_val_if_fail (SECRET_IS_SERVICE (self), FALSE);
1338         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
1339         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1340
1341         sync = _secret_sync_new ();
1342         g_main_context_push_thread_default (sync->context);
1343
1344         secret_service_ensure_session (self, cancellable,
1345                                        _secret_sync_on_result, sync);
1346
1347         g_main_loop_run (sync->loop);
1348
1349         ret = secret_service_ensure_session_finish (self, sync->result, error);
1350
1351         g_main_context_pop_thread_default (sync->context);
1352         _secret_sync_free (sync);
1353
1354         return ret;
1355 }
1356
1357 static SecretCollection *
1358 service_lookup_collection (SecretService *self,
1359                            const gchar *path)
1360 {
1361         SecretCollection *collection = NULL;
1362
1363         g_mutex_lock (&self->pv->mutex);
1364
1365         if (self->pv->collections) {
1366                 collection = g_hash_table_lookup (self->pv->collections, path);
1367                 if (collection != NULL)
1368                         g_object_ref (collection);
1369         }
1370
1371         g_mutex_unlock (&self->pv->mutex);
1372
1373         return collection;
1374 }
1375
1376 static void
1377 service_update_collections (SecretService *self,
1378                             GHashTable *collections)
1379 {
1380         GHashTable *previous;
1381
1382         g_hash_table_ref (collections);
1383
1384         g_mutex_lock (&self->pv->mutex);
1385
1386         previous = self->pv->collections;
1387         self->pv->collections = collections;
1388
1389         g_mutex_unlock (&self->pv->mutex);
1390
1391         if (previous != NULL)
1392                 g_hash_table_unref (previous);
1393
1394         g_object_notify (G_OBJECT (self), "collections");
1395 }
1396
1397 typedef struct {
1398         GCancellable *cancellable;
1399         GHashTable *collections;
1400         gint collections_loading;
1401 } EnsureClosure;
1402
1403 static GHashTable *
1404 collections_table_new (void)
1405 {
1406         return g_hash_table_new_full (g_str_hash, g_str_equal,
1407                                       g_free, g_object_unref);
1408 }
1409
1410 static void
1411 ensure_closure_free (gpointer data)
1412 {
1413         EnsureClosure *closure = data;
1414         g_clear_object (&closure->cancellable);
1415         g_hash_table_unref (closure->collections);
1416         g_slice_free (EnsureClosure, closure);
1417 }
1418
1419 static void
1420 on_ensure_collection (GObject *source,
1421                       GAsyncResult *result,
1422                       gpointer user_data)
1423 {
1424         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
1425         SecretService *self = SECRET_SERVICE (g_async_result_get_source_object (user_data));
1426         EnsureClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
1427         SecretCollection *collection;
1428         const gchar *path;
1429         GError *error = NULL;
1430
1431         closure->collections_loading--;
1432
1433         collection = secret_collection_new_for_dbus_path_finish (result, &error);
1434
1435         if (error != NULL)
1436                 g_simple_async_result_take_error (res, error);
1437
1438         if (collection != NULL) {
1439                 path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (collection));
1440                 g_hash_table_insert (closure->collections, g_strdup (path), collection);
1441         }
1442
1443         if (closure->collections_loading == 0) {
1444                 service_update_collections (self, closure->collections);
1445                 g_simple_async_result_complete (res);
1446         }
1447
1448         g_object_unref (self);
1449         g_object_unref (res);
1450 }
1451
1452 /**
1453  * secret_service_load_collections:
1454  * @self: the secret service
1455  * @cancellable: optional cancellation object
1456  * @callback: called when the operation completes
1457  * @user_data: data to be passed to the callback
1458  *
1459  * Ensure that the #SecretService proxy has loaded all the collections present
1460  * in the Secret Service. This affects the result of
1461  * secret_service_get_collections().
1462  *
1463  * You can also pass the %SECRET_SERVICE_LOAD_COLLECTIONS to
1464  * secret_service_get_sync() in order to ensure that the collections have been
1465  * loaded by the time you get the #SecretService proxy.
1466  *
1467  * This method will return immediately and complete asynchronously.
1468  */
1469 void
1470 secret_service_load_collections (SecretService *self,
1471                                  GCancellable *cancellable,
1472                                  GAsyncReadyCallback callback,
1473                                  gpointer user_data)
1474 {
1475         EnsureClosure *closure;
1476         SecretCollection *collection;
1477         GSimpleAsyncResult *res;
1478         const gchar *path;
1479         GVariant *paths;
1480         GVariantIter iter;
1481
1482         g_return_if_fail (SECRET_IS_SERVICE (self));
1483         g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1484
1485         paths = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Collections");
1486         g_return_if_fail (paths != NULL);
1487
1488         res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
1489                                          secret_service_load_collections);
1490         closure = g_slice_new0 (EnsureClosure);
1491         closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
1492         closure->collections = collections_table_new ();
1493         g_simple_async_result_set_op_res_gpointer (res, closure, ensure_closure_free);
1494
1495         g_variant_iter_init (&iter, paths);
1496         while (g_variant_iter_loop (&iter, "&o", &path)) {
1497                 collection = service_lookup_collection (self, path);
1498
1499                 /* No such collection yet create a new one */
1500                 if (collection == NULL) {
1501                         secret_collection_new_for_dbus_path (self, path, SECRET_COLLECTION_LOAD_ITEMS,
1502                                                              cancellable, on_ensure_collection, g_object_ref (res));
1503                         closure->collections_loading++;
1504                 } else {
1505                         g_hash_table_insert (closure->collections, g_strdup (path), collection);
1506                 }
1507         }
1508
1509         if (closure->collections_loading == 0) {
1510                 service_update_collections (self, closure->collections);
1511                 g_simple_async_result_complete_in_idle (res);
1512         }
1513
1514         g_variant_unref (paths);
1515         g_object_unref (res);
1516 }
1517
1518 /**
1519  * secret_service_load_collections_finish:
1520  * @self: the secret service
1521  * @result: the asynchronous result passed to the callback
1522  * @error: location to place an error on failure
1523  *
1524  * Complete an asynchronous operation to ensure that the #SecretService proxy
1525  * has loaded all the collections present in the Secret Service.
1526  *
1527  * Returns: whether the load was successful or not
1528  */
1529 gboolean
1530 secret_service_load_collections_finish (SecretService *self,
1531                                         GAsyncResult *result,
1532                                         GError **error)
1533 {
1534         g_return_val_if_fail (SECRET_IS_SERVICE (self), FALSE);
1535         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1536         g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
1537                               secret_service_load_collections), FALSE);
1538
1539         if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
1540                 return FALSE;
1541
1542         return TRUE;
1543 }
1544
1545 /**
1546  * secret_service_load_collections_sync:
1547  * @self: the secret service
1548  * @cancellable: optional cancellation object
1549  * @error: location to place an error on failure
1550  *
1551  * Ensure that the #SecretService proxy has loaded all the collections present
1552  * in the Secret Service. This affects the result of
1553  * secret_service_get_collections().
1554  *
1555  * You can also pass the %SECRET_SERVICE_LOAD_COLLECTIONS to
1556  * secret_service_get_sync() in order to ensure that the collections have been
1557  * loaded by the time you get the #SecretService proxy.
1558  *
1559  * This method may block indefinitely and should not be used in user interface
1560  * threads.
1561  *
1562  * Returns: whether the load was successful or not
1563  */
1564 gboolean
1565 secret_service_load_collections_sync (SecretService *self,
1566                                       GCancellable *cancellable,
1567                                       GError **error)
1568 {
1569         SecretCollection *collection;
1570         GHashTable *collections;
1571         GVariant *paths;
1572         GVariantIter iter;
1573         const gchar *path;
1574         gboolean ret = TRUE;
1575
1576         g_return_val_if_fail (SECRET_IS_SERVICE (self), FALSE);
1577         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
1578         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1579
1580         paths = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Collections");
1581         g_return_val_if_fail (paths != NULL, FALSE);
1582
1583         collections = collections_table_new ();
1584
1585         g_variant_iter_init (&iter, paths);
1586         while (g_variant_iter_next (&iter, "&o", &path)) {
1587                 collection = service_lookup_collection (self, path);
1588
1589                 /* No such collection yet create a new one */
1590                 if (collection == NULL) {
1591                         collection = secret_collection_new_for_dbus_path_sync (self, path,
1592                                                                                SECRET_COLLECTION_LOAD_ITEMS,
1593                                                                                cancellable, error);
1594                         if (collection == NULL) {
1595                                 ret = FALSE;
1596                                 break;
1597                         }
1598                 }
1599
1600                 g_hash_table_insert (collections, g_strdup (path), collection);
1601         }
1602
1603         if (ret)
1604                 service_update_collections (self, collections);
1605
1606         g_hash_table_unref (collections);
1607         g_variant_unref (paths);
1608         return ret;
1609 }
1610
1611 /**
1612  * secret_service_prompt_sync:
1613  * @self: the secret service
1614  * @prompt: the prompt
1615  * @cancellable: optional cancellation object
1616  * @return_type: the variant type of the prompt result
1617  * @error: location to place an error on failure
1618  *
1619  * Perform prompting for a #SecretPrompt.
1620  *
1621  * Runs a prompt and performs the prompting. Returns a variant result if the
1622  * prompt was completed and not dismissed. The type of result depends on the
1623  * action the prompt is completing, and is defined in the Secret Service DBus
1624  * API specification.
1625  *
1626  * This function is called by other parts of this library to handle prompts
1627  * for the various actions that can require prompting.
1628  *
1629  * Override the #SecretServiceClass <literal>prompt_sync</literal> virtual method
1630  * to change the behavior of the propmting. The default behavior is to simply
1631  * run secret_prompt_perform_sync() on the prompt.
1632  *
1633  * Returns: (transfer full): %NULL if the prompt was dismissed or an error occurred,
1634  *          a variant result if the prompt was successful
1635  */
1636 GVariant *
1637 secret_service_prompt_sync (SecretService *self,
1638                             SecretPrompt *prompt,
1639                             GCancellable *cancellable,
1640                             const GVariantType *return_type,
1641                             GError **error)
1642 {
1643         SecretServiceClass *klass;
1644
1645         g_return_val_if_fail (SECRET_IS_SERVICE (self), NULL);
1646         g_return_val_if_fail (SECRET_IS_PROMPT (prompt), NULL);
1647         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
1648         g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1649
1650         klass = SECRET_SERVICE_GET_CLASS (self);
1651         g_return_val_if_fail (klass->prompt_sync != NULL, NULL);
1652
1653         return (klass->prompt_sync) (self, prompt, cancellable, return_type, error);
1654 }
1655
1656 /**
1657  * secret_service_prompt:
1658  * @self: the secret service
1659  * @prompt: the prompt
1660  * @cancellable: optional cancellation object
1661  * @callback: called when the operation completes
1662  * @user_data: data to be passed to the callback
1663  *
1664  * Perform prompting for a #SecretPrompt.
1665  *
1666  * This function is called by other parts of this library to handle prompts
1667  * for the various actions that can require prompting.
1668  *
1669  * Override the #SecretServiceClass <literal>prompt_async</literal> virtual method
1670  * to change the behavior of the propmting. The default behavior is to simply
1671  * run secret_prompt_perform() on the prompt.
1672  */
1673 void
1674 secret_service_prompt (SecretService *self,
1675                        SecretPrompt *prompt,
1676                        GCancellable *cancellable,
1677                        GAsyncReadyCallback callback,
1678                        gpointer user_data)
1679 {
1680         SecretServiceClass *klass;
1681
1682         g_return_if_fail (SECRET_IS_SERVICE (self));
1683         g_return_if_fail (SECRET_IS_PROMPT (prompt));
1684         g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1685
1686         klass = SECRET_SERVICE_GET_CLASS (self);
1687         g_return_if_fail (klass->prompt_async != NULL);
1688
1689         (klass->prompt_async) (self, prompt, cancellable, callback, user_data);
1690 }
1691
1692 /**
1693  * secret_service_prompt_finish:
1694  * @self: the secret service
1695  * @result: the asynchronous result passed to the callback
1696  * @return_type: the variant type of the prompt result
1697  * @error: location to place an error on failure
1698  *
1699  * Complete asynchronous operation to perform prompting for a #SecretPrompt.
1700  *
1701  * Returns a variant result if the prompt was completed and not dismissed. The
1702  * type of result depends on the action the prompt is completing, and is defined
1703  * in the Secret Service DBus API specification.
1704  *
1705  * Returns: (transfer full): %NULL if the prompt was dismissed or an error occurred,
1706  *          a variant result if the prompt was successful
1707  */
1708 GVariant *
1709 secret_service_prompt_finish (SecretService *self,
1710                               GAsyncResult *result,
1711                               const GVariantType *return_type,
1712                               GError **error)
1713 {
1714         SecretServiceClass *klass;
1715
1716         g_return_val_if_fail (SECRET_IS_SERVICE (self), NULL);
1717         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
1718         g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1719
1720         klass = SECRET_SERVICE_GET_CLASS (self);
1721         g_return_val_if_fail (klass->prompt_finish != NULL, NULL);
1722
1723         return (klass->prompt_finish) (self, result, return_type, error);
1724 }