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