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