Bump version number
[platform/upstream/libsecret.git] / library / secret-methods.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-item.h"
21 #include "secret-paths.h"
22 #include "secret-private.h"
23 #include "secret-service.h"
24 #include "secret-types.h"
25 #include "secret-value.h"
26
27 /**
28  * SecretSearchFlags:
29  * @SECRET_SEARCH_NONE: no flags
30  * @SECRET_SEARCH_ALL: all the items matching the search will be returned, instead of just the first one
31  * @SECRET_SEARCH_UNLOCK: unlock locked items while searching
32  * @SECRET_SEARCH_LOAD_SECRETS: while searching load secrets for items that are not locked
33  *
34  * Various flags to be used with secret_service_search() and secret_service_search_sync().
35  */
36
37 typedef struct {
38         SecretService *service;
39         GCancellable *cancellable;
40         GHashTable *items;
41         gchar **unlocked;
42         gchar **locked;
43         guint loading;
44         SecretSearchFlags flags;
45         GVariant *attributes;
46 } SearchClosure;
47
48 static void
49 search_closure_free (gpointer data)
50 {
51         SearchClosure *closure = data;
52         g_object_unref (closure->service);
53         g_clear_object (&closure->cancellable);
54         g_hash_table_unref (closure->items);
55         g_variant_unref (closure->attributes);
56         g_strfreev (closure->unlocked);
57         g_strfreev (closure->locked);
58         g_slice_free (SearchClosure, closure);
59 }
60
61 static void
62 search_closure_take_item (SearchClosure *closure,
63                           SecretItem *item)
64 {
65         const gchar *path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (item));
66         g_hash_table_insert (closure->items, (gpointer)path, item);
67 }
68
69 static GList *
70 search_closure_build_items (SearchClosure *closure,
71                             gchar **paths)
72 {
73         GList *results = NULL;
74         SecretItem *item;
75         guint i;
76
77         for (i = 0; paths[i]; i++) {
78                 item = g_hash_table_lookup (closure->items, paths[i]);
79                 if (item != NULL)
80                         results = g_list_prepend (results, g_object_ref (item));
81         }
82
83         return g_list_reverse (results);
84 }
85
86 static void
87 on_search_secrets (GObject *source,
88                    GAsyncResult *result,
89                    gpointer user_data)
90 {
91         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
92
93         /* Note that we ignore any unlock failure */
94         secret_item_load_secrets_finish (result, NULL);
95
96         g_simple_async_result_complete (async);
97         g_object_unref (async);
98 }
99
100 static void
101 on_search_unlocked (GObject *source,
102                     GAsyncResult *result,
103                     gpointer user_data)
104 {
105         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
106         SearchClosure *search = g_simple_async_result_get_op_res_gpointer (async);
107         GList *items;
108
109         /* Note that we ignore any unlock failure */
110         secret_service_unlock_finish (search->service, result, NULL, NULL);
111
112         /* If loading secrets ... locked items automatically ignored */
113         if (search->flags & SECRET_SEARCH_LOAD_SECRETS) {
114                 items = g_hash_table_get_values (search->items);
115                 secret_item_load_secrets (items, search->cancellable,
116                                           on_search_secrets, g_object_ref (async));
117                 g_list_free (items);
118
119         /* No additional options, just complete */
120         } else {
121                 g_simple_async_result_complete (async);
122         }
123
124         g_object_unref (async);
125 }
126
127 static void
128 secret_search_unlock_load_or_complete (GSimpleAsyncResult *async,
129                                        SearchClosure *search)
130 {
131         GList *items;
132
133         /* If unlocking then unlock all the locked items */
134         if (search->flags & SECRET_SEARCH_UNLOCK) {
135                 items = search_closure_build_items (search, search->locked);
136                 secret_service_unlock (search->service, items, search->cancellable,
137                                        on_search_unlocked, g_object_ref (async));
138                 g_list_free_full (items, g_object_unref);
139
140         /* If loading secrets ... locked items automatically ignored */
141         } else if (search->flags & SECRET_SEARCH_LOAD_SECRETS) {
142                 items = g_hash_table_get_values (search->items);
143                 secret_item_load_secrets (items, search->cancellable,
144                                           on_search_secrets, g_object_ref (async));
145                 g_list_free (items);
146
147         /* No additional options, just complete */
148         } else {
149                 g_simple_async_result_complete (async);
150         }
151 }
152
153 static void
154 on_search_loaded (GObject *source,
155                   GAsyncResult *result,
156                   gpointer user_data)
157 {
158         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
159         SearchClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
160         GError *error = NULL;
161         SecretItem *item;
162
163         closure->loading--;
164
165         item = secret_item_new_for_dbus_path_finish (result, &error);
166         if (error != NULL)
167                 g_simple_async_result_take_error (res, error);
168
169         if (item != NULL)
170                 search_closure_take_item (closure, item);
171
172         /* We're done loading, lets go to the next step */
173         if (closure->loading == 0)
174                 secret_search_unlock_load_or_complete (res, closure);
175
176         g_object_unref (res);
177 }
178
179 static void
180 search_load_item_async (SecretService *self,
181                         GSimpleAsyncResult *res,
182                         SearchClosure *closure,
183                         const gchar *path)
184 {
185         SecretItem *item;
186
187         item = _secret_service_find_item_instance (self, path);
188         if (item == NULL) {
189                 secret_item_new_for_dbus_path (self, path, SECRET_ITEM_NONE, closure->cancellable,
190                                                on_search_loaded, g_object_ref (res));
191                 closure->loading++;
192         } else {
193                 search_closure_take_item (closure, item);
194         }
195 }
196
197 static void
198 on_search_paths (GObject *source,
199                  GAsyncResult *result,
200                  gpointer user_data)
201 {
202         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
203         SearchClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
204         SecretService *self = closure->service;
205         GError *error = NULL;
206         gint want = 1;
207         gint count;
208         gint i;
209
210         secret_service_search_for_dbus_paths_finish (self, result, &closure->unlocked,
211                                                      &closure->locked, &error);
212         if (error == NULL) {
213                 want = 1;
214                 if (closure->flags & SECRET_SEARCH_ALL)
215                         want = G_MAXINT;
216                 count = 0;
217
218                 for (i = 0; count < want && closure->unlocked[i] != NULL; i++, count++)
219                         search_load_item_async (self, res, closure, closure->unlocked[i]);
220                 for (i = 0; count < want && closure->locked[i] != NULL; i++, count++)
221                         search_load_item_async (self, res, closure, closure->locked[i]);
222
223                 /* No items loading, complete operation now */
224                 if (closure->loading == 0)
225                         secret_search_unlock_load_or_complete (res, closure);
226
227         } else {
228                 g_simple_async_result_take_error (res, error);
229                 g_simple_async_result_complete (res);
230         }
231
232         g_object_unref (res);
233 }
234
235 static void
236 on_search_service (GObject *source,
237                    GAsyncResult *result,
238                    gpointer user_data)
239 {
240         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
241         SearchClosure *search = g_simple_async_result_get_op_res_gpointer (async);
242         GError *error = NULL;
243
244         search->service = secret_service_get_finish (result, &error);
245         if (error == NULL) {
246                 _secret_service_search_for_paths_variant (search->service, search->attributes,
247                                                           search->cancellable, on_search_paths,
248                                                           g_object_ref (async));
249
250         } else {
251                 g_simple_async_result_take_error (async, error);
252                 g_simple_async_result_complete (async);
253         }
254
255         g_object_unref (async);
256 }
257
258 /**
259  * secret_service_search:
260  * @service: (allow-none): the secret service
261  * @schema: (allow-none): the schema for the attributes
262  * @attributes: (element-type utf8 utf8): search for items matching these attributes
263  * @flags: search option flags
264  * @cancellable: optional cancellation object
265  * @callback: called when the operation completes
266  * @user_data: data to pass to the callback
267  *
268  * Search for items matching the @attributes. All collections are searched.
269  * The @attributes should be a table of string keys and string values.
270  *
271  * If @service is NULL, then secret_service_get() will be called to get
272  * the default #SecretService proxy.
273  *
274  * If %SECRET_SEARCH_ALL is set in @flags, then all the items matching the
275  * search will be returned. Otherwise only the first item will be returned.
276  * This is almost always the unlocked item that was most recently stored.
277  *
278  * If %SECRET_SEARCH_UNLOCK is set in @flags, then items will be unlocked
279  * if necessary. In either case, locked and unlocked items will match the
280  * search and be returned. If the unlock fails, the search does not fail.
281  *
282  * If %SECRET_SEARCH_LOAD_SECRETS is set in @flags, then the items will have
283  * their secret values loaded and available via secret_item_get_secret().
284  *
285  * This function returns immediately and completes asynchronously.
286  */
287 void
288 secret_service_search (SecretService *service,
289                        const SecretSchema *schema,
290                        GHashTable *attributes,
291                        SecretSearchFlags flags,
292                        GCancellable *cancellable,
293                        GAsyncReadyCallback callback,
294                        gpointer user_data)
295 {
296         GSimpleAsyncResult *res;
297         SearchClosure *closure;
298         const gchar *schema_name = NULL;
299
300         g_return_if_fail (service == NULL || SECRET_IS_SERVICE (service));
301         g_return_if_fail (attributes != NULL);
302         g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
303
304         /* Warnings raised already */
305         if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, TRUE))
306                 return;
307
308         if (schema != NULL && !(schema->flags & SECRET_SCHEMA_DONT_MATCH_NAME))
309                 schema_name = schema->name;
310
311         res = g_simple_async_result_new (G_OBJECT (service), callback, user_data,
312                                          secret_service_search);
313         closure = g_slice_new0 (SearchClosure);
314         closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
315         closure->items = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
316         closure->flags = flags;
317         closure->attributes = _secret_attributes_to_variant (attributes, schema_name);
318         g_variant_ref_sink (closure->attributes);
319         g_simple_async_result_set_op_res_gpointer (res, closure, search_closure_free);
320
321         if (service) {
322                 closure->service = g_object_ref (service);
323                 _secret_service_search_for_paths_variant (closure->service, closure->attributes,
324                                                           closure->cancellable, on_search_paths,
325                                                           g_object_ref (res));
326
327         } else {
328                 secret_service_get (SECRET_SERVICE_NONE, cancellable,
329                                     on_search_service, g_object_ref (res));
330         }
331
332         g_object_unref (res);
333 }
334
335 /**
336  * secret_service_search_finish:
337  * @service: (allow-none): the secret service
338  * @result: asynchronous result passed to callback
339  * @error: location to place error on failure
340  *
341  * Complete asynchronous operation to search for items.
342  *
343  * Returns: (transfer full) (element-type Secret.Item):
344  *          a list of items that matched the search
345  */
346 GList *
347 secret_service_search_finish (SecretService *service,
348                               GAsyncResult *result,
349                               GError **error)
350 {
351         GSimpleAsyncResult *res;
352         SearchClosure *closure;
353         GList *items;
354
355         g_return_val_if_fail (service == NULL || SECRET_IS_SERVICE (service), FALSE);
356         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
357         g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (service),
358                               secret_service_search), FALSE);
359
360         res = G_SIMPLE_ASYNC_RESULT (result);
361
362         if (g_simple_async_result_propagate_error (res, error))
363                 return FALSE;
364
365         closure = g_simple_async_result_get_op_res_gpointer (res);
366         if (closure->unlocked)
367                 items = search_closure_build_items (closure, closure->unlocked);
368         if (closure->locked)
369                 items = g_list_concat (items, search_closure_build_items (closure, closure->locked));
370         return items;
371 }
372
373 static gboolean
374 service_load_items_sync (SecretService *service,
375                          GCancellable *cancellable,
376                          gchar **paths,
377                          GList **items,
378                          gint want,
379                          gint *have,
380                          GError **error)
381 {
382         SecretItem *item;
383         guint i;
384
385         for (i = 0; *have < want && paths[i] != NULL; i++) {
386                 item = _secret_service_find_item_instance (service, paths[i]);
387                 if (item == NULL)
388                         item = secret_item_new_for_dbus_path_sync (service, paths[i], SECRET_ITEM_NONE,
389                                                                    cancellable, error);
390                 if (item == NULL) {
391                         return FALSE;
392
393                 } else {
394                         *items = g_list_prepend (*items, item);
395                         (*have)++;
396                 }
397         }
398
399         return TRUE;
400 }
401
402 /**
403  * secret_service_search_sync:
404  * @service: (allow-none): the secret service
405  * @schema: (allow-none): the schema for the attributes
406  * @attributes: (element-type utf8 utf8): search for items matching these attributes
407  * @flags: search option flags
408  * @cancellable: optional cancellation object
409  * @error: location to place error on failure
410  *
411  * Search for items matching the @attributes. All collections are searched.
412  * The @attributes should be a table of string keys and string values.
413  *
414  * If @service is NULL, then secret_service_get_sync() will be called to get
415  * the default #SecretService proxy.
416  *
417  * If %SECRET_SEARCH_ALL is set in @flags, then all the items matching the
418  * search will be returned. Otherwise only the first item will be returned.
419  * This is almost always the unlocked item that was most recently stored.
420  *
421  * If %SECRET_SEARCH_UNLOCK is set in @flags, then items will be unlocked
422  * if necessary. In either case, locked and unlocked items will match the
423  * search and be returned. If the unlock fails, the search does not fail.
424  *
425  * If %SECRET_SEARCH_LOAD_SECRETS is set in @flags, then the items' secret
426  * values will be loaded for any unlocked items. Loaded item secret values
427  * are available via secret_item_get_secret(). If the load of a secret values
428  * fail, then the
429  *
430  * This function may block indefinetely. Use the asynchronous version
431  * in user interface threads.
432  *
433  * Returns: (transfer full) (element-type Secret.Item):
434  *          a list of items that matched the search
435  */
436 GList *
437 secret_service_search_sync (SecretService *service,
438                             const SecretSchema *schema,
439                             GHashTable *attributes,
440                             SecretSearchFlags flags,
441                             GCancellable *cancellable,
442                             GError **error)
443 {
444         gchar **unlocked_paths = NULL;
445         gchar **locked_paths = NULL;
446         GList *items = NULL;
447         GList *locked = NULL;
448         GList *unlocked = NULL;
449         gboolean ret;
450         gint want;
451         gint have;
452
453         g_return_val_if_fail (service == NULL || SECRET_IS_SERVICE (service), NULL);
454         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
455         g_return_val_if_fail (error == NULL || *error == NULL, NULL);
456
457         /* Warnings raised already */
458         if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, TRUE))
459                 return NULL;
460
461         if (service == NULL) {
462                 service = secret_service_get_sync (SECRET_SERVICE_NONE, cancellable, error);
463                 if (service == NULL)
464                         return NULL;
465         } else {
466                 g_object_ref (service);
467         }
468
469         if (!secret_service_search_for_dbus_paths_sync (service, schema, attributes, cancellable,
470                                                         &unlocked_paths, &locked_paths, error)) {
471                 g_object_unref (service);
472                 return NULL;
473         }
474
475         ret = TRUE;
476
477         want = 1;
478         if (flags & SECRET_SEARCH_ALL)
479                 want = G_MAXINT;
480         have = 0;
481
482         /* Remember, we're adding to the list backwards */
483
484         if (unlocked_paths) {
485                 ret = service_load_items_sync (service, cancellable, unlocked_paths,
486                                                &unlocked, want, &have, error);
487         }
488
489         if (ret && locked_paths) {
490                 ret = service_load_items_sync (service, cancellable, locked_paths,
491                                                &locked, want, &have, error);
492         }
493
494         g_strfreev (unlocked_paths);
495         g_strfreev (locked_paths);
496
497         if (!ret) {
498                 g_list_free_full (unlocked, g_object_unref);
499                 g_list_free_full (locked, g_object_unref);
500                 g_object_unref (service);
501                 return NULL;
502         }
503
504         /* The lists are backwards at this point ... */
505         items = g_list_concat (items, g_list_copy (locked));
506         items = g_list_concat (items, g_list_copy (unlocked));
507         items = g_list_reverse (items);
508
509         if (flags & SECRET_SEARCH_UNLOCK)
510                 secret_service_unlock_sync (service, locked, cancellable, NULL, NULL);
511
512         if (flags & SECRET_SEARCH_LOAD_SECRETS)
513                 secret_item_load_secrets_sync (items, NULL, NULL);
514
515         g_list_free (locked);
516         g_list_free (unlocked);
517         g_object_unref (service);
518         return items;
519 }
520
521 SecretValue *
522 _secret_service_decode_get_secrets_first (SecretService *self,
523                                           GVariant *out)
524 {
525         SecretSession *session;
526         SecretValue *value = NULL;
527         GVariantIter *iter;
528         GVariant *variant;
529         const gchar *path;
530
531         g_variant_get (out, "(a{o(oayays)})", &iter);
532         while (g_variant_iter_next (iter, "{&o@(oayays)}", &path, &variant)) {
533                 session = _secret_service_get_session (self);
534                 value = _secret_session_decode_secret (session, variant);
535                 g_variant_unref (variant);
536                 break;
537         }
538         g_variant_iter_free (iter);
539         return value;
540 }
541
542 GHashTable *
543 _secret_service_decode_get_secrets_all (SecretService *self,
544                                         GVariant *out)
545 {
546         SecretSession *session;
547         GVariantIter *iter;
548         GVariant *variant;
549         GHashTable *values;
550         SecretValue *value;
551         gchar *path;
552
553         session = _secret_service_get_session (self);
554         values = g_hash_table_new_full (g_str_hash, g_str_equal,
555                                         g_free, secret_value_unref);
556         g_variant_get (out, "(a{o(oayays)})", &iter);
557         while (g_variant_iter_loop (iter, "{o@(oayays)}", &path, &variant)) {
558                 value = _secret_session_decode_secret (session, variant);
559                 if (value && path)
560                         g_hash_table_insert (values, g_strdup (path), value);
561         }
562         g_variant_iter_free (iter);
563         return values;
564 }
565
566 typedef struct {
567         GCancellable *cancellable;
568         GPtrArray *paths;
569         GHashTable *objects;
570         gchar **xlocked;
571         guint count;
572         gboolean locking;
573 } XlockClosure;
574
575 static void
576 xlock_closure_free (gpointer data)
577 {
578         XlockClosure *closure = data;
579         if (closure->cancellable)
580                 g_object_unref (closure->cancellable);
581         g_ptr_array_free (closure->paths, TRUE);
582         g_strfreev (closure->xlocked);
583         g_hash_table_unref (closure->objects);
584         g_slice_free (XlockClosure, closure);
585 }
586
587 static void
588 on_xlock_paths (GObject *source,
589                 GAsyncResult *result,
590                 gpointer user_data)
591 {
592         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
593         XlockClosure *xlock = g_simple_async_result_get_op_res_gpointer (async);
594         GVariant *lockval;
595         GDBusProxy *object;
596         GError *error = NULL;
597         gint i;
598
599         xlock->count = _secret_service_xlock_paths_finish (SECRET_SERVICE (source), result,
600                                                            &xlock->xlocked, &error);
601
602         if (error == NULL) {
603                 /*
604                  * After a lock or unlock we want the Locked property to immediately
605                  * reflect the new state, and not have to wait for a PropertiesChanged
606                  * signal to be processed later.
607                  */
608
609                 lockval = g_variant_ref_sink (g_variant_new_boolean (xlock->locking));
610                 for (i = 0; xlock->xlocked[i] != NULL; i++) {
611                         object =  g_hash_table_lookup (xlock->objects, xlock->xlocked[i]);
612                         if (object != NULL)
613                                 g_dbus_proxy_set_cached_property (object, "Locked", lockval);
614                 }
615                 g_variant_unref (lockval);
616
617         } else {
618                 g_simple_async_result_take_error (async, error);
619         }
620
621         g_simple_async_result_complete (async);
622         g_object_unref (async);
623 }
624
625 static void
626 on_xlock_service (GObject *source,
627                   GAsyncResult *result,
628                   gpointer user_data)
629 {
630         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
631         XlockClosure *xlock = g_simple_async_result_get_op_res_gpointer (async);
632         GError *error = NULL;
633         SecretService *service;
634
635         service = secret_service_get_finish (result, &error);
636         if (error == NULL) {
637                 _secret_service_xlock_paths_async (service, xlock->locking ? "Lock" : "Unlock",
638                                                    (const gchar **)xlock->paths->pdata,
639                                                    xlock->cancellable, on_xlock_paths,
640                                                    g_object_ref (async));
641                 g_object_unref (service);
642
643         } else {
644                 g_simple_async_result_take_error (async, error);
645                 g_simple_async_result_complete (async);
646         }
647
648         g_object_unref (async);
649 }
650
651 static void
652 service_xlock_async (SecretService *service,
653                      gboolean locking,
654                      GList *objects,
655                      GCancellable *cancellable,
656                      GAsyncReadyCallback callback,
657                      gpointer user_data)
658 {
659         GSimpleAsyncResult *async;
660         XlockClosure *xlock;
661         const gchar *path;
662         GList *l;
663
664         async = g_simple_async_result_new (G_OBJECT (service), callback, user_data,
665                                            service_xlock_async);
666         xlock = g_slice_new0 (XlockClosure);
667         xlock->objects = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
668         xlock->locking = locking;
669         xlock->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
670         xlock->paths = g_ptr_array_new ();
671
672         for (l = objects; l != NULL; l = g_list_next (l)) {
673                 path = g_dbus_proxy_get_object_path (l->data);
674                 g_ptr_array_add (xlock->paths, (gpointer)path);
675                 g_hash_table_insert (xlock->objects, g_strdup (path), g_object_ref (l->data));
676         }
677         g_ptr_array_add (xlock->paths, NULL);
678
679         g_simple_async_result_set_op_res_gpointer (async, xlock, xlock_closure_free);
680
681         if (service == NULL) {
682                 secret_service_get (SECRET_SERVICE_NONE, cancellable,
683                                     on_xlock_service, g_object_ref (async));
684         } else {
685                 _secret_service_xlock_paths_async (service, xlock->locking ? "Lock" : "Unlock",
686                                                    (const gchar **)xlock->paths->pdata,
687                                                    xlock->cancellable, on_xlock_paths,
688                                                    g_object_ref (async));
689         }
690
691         g_object_unref (async);
692 }
693
694 static gint
695 service_xlock_finish (SecretService *service,
696                       GAsyncResult *result,
697                       GList **xlocked,
698                       GError **error)
699 {
700         GSimpleAsyncResult *async;
701         XlockClosure *xlock;
702         GDBusProxy *object;
703         gint i;
704
705         g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (service),
706                                                               service_xlock_async), -1);
707
708         async = G_SIMPLE_ASYNC_RESULT (result);
709         if (g_simple_async_result_propagate_error (async, error))
710                 return -1;
711
712         xlock = g_simple_async_result_get_op_res_gpointer (async);
713         if (xlocked) {
714                 *xlocked = NULL;
715                 for (i = 0; xlock->xlocked[i] != NULL; i++) {
716                         object = g_hash_table_lookup (xlock->objects, xlock->xlocked[i]);
717                         if (object != NULL)
718                                 *xlocked = g_list_prepend (*xlocked, g_object_ref (object));
719                 }
720         }
721
722         return xlock->count;
723 }
724
725 /**
726  * secret_service_lock:
727  * @service: (allow-none): the secret service
728  * @objects: (element-type GLib.DBusProxy): the items or collections to lock
729  * @cancellable: optional cancellation object
730  * @callback: called when the operation completes
731  * @user_data: data to pass to the callback
732  *
733  * Lock items or collections in the secret service.
734  *
735  * The secret service may not be able to lock items individually, and may
736  * lock an entire collection instead.
737  *
738  * If @service is NULL, then secret_service_get() will be called to get
739  * the default #SecretService proxy.
740  *
741  * This method returns immediately and completes asynchronously. The secret
742  * service may prompt the user. secret_service_prompt() will be used to handle
743  * any prompts that show up.
744  */
745 void
746 secret_service_lock (SecretService *service,
747                      GList *objects,
748                      GCancellable *cancellable,
749                      GAsyncReadyCallback callback,
750                      gpointer user_data)
751 {
752         g_return_if_fail (service == NULL || SECRET_IS_SERVICE (service));
753         g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
754
755         service_xlock_async (service, TRUE, objects, cancellable, callback, user_data);
756 }
757
758 /**
759  * secret_service_lock_finish:
760  * @service: (allow-none): the secret service
761  * @result: asynchronous result passed to the callback
762  * @locked: (out) (element-type GLib.DBusProxy) (transfer full) (allow-none):
763  *          location to place list of items or collections that were locked
764  * @error: location to place an error on failure
765  *
766  * Complete asynchronous operation to lock items or collections in the secret
767  * service.
768  *
769  * The secret service may not be able to lock items individually, and may
770  * lock an entire collection instead.
771  *
772  * Returns: the number of items or collections that were locked
773  */
774 gint
775 secret_service_lock_finish (SecretService *service,
776                             GAsyncResult *result,
777                             GList **locked,
778                             GError **error)
779 {
780         g_return_val_if_fail (service == NULL || SECRET_IS_SERVICE (service), -1);
781         g_return_val_if_fail (error == NULL || *error == NULL, -1);
782
783         return service_xlock_finish (service, result, locked, error);
784 }
785
786 /**
787  * secret_service_lock_sync:
788  * @service: (allow-none): the secret service
789  * @objects: (element-type GLib.DBusProxy): the items or collections to lock
790  * @cancellable: optional cancellation object
791  * @locked: (out) (element-type GLib.DBusProxy) (transfer full) (allow-none):
792  *          location to place list of items or collections that were locked
793  * @error: location to place an error on failure
794  *
795  * Lock items or collections in the secret service.
796  *
797  * The secret service may not be able to lock items individually, and may
798  * lock an entire collection instead.
799  *
800  * If @service is NULL, then secret_service_get_sync() will be called to get
801  * the default #SecretService proxy.
802  *
803  * This method may block indefinitely and should not be used in user
804  * interface threads. The secret service may prompt the user.
805  * secret_service_prompt() will be used to handle any prompts that show up.
806  *
807  * Returns: the number of items or collections that were locked
808  */
809 gint
810 secret_service_lock_sync (SecretService *service,
811                           GList *objects,
812                           GCancellable *cancellable,
813                           GList **locked,
814                           GError **error)
815 {
816         SecretSync *sync;
817         gint count;
818
819         g_return_val_if_fail (service == NULL || SECRET_IS_SERVICE (service), -1);
820         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), -1);
821         g_return_val_if_fail (error == NULL || *error == NULL, -1);
822
823         sync = _secret_sync_new ();
824         g_main_context_push_thread_default (sync->context);
825
826         secret_service_lock (service, objects, cancellable,
827                              _secret_sync_on_result, sync);
828
829         g_main_loop_run (sync->loop);
830
831         count = secret_service_lock_finish (service, sync->result, locked, error);
832
833         g_main_context_pop_thread_default (sync->context);
834         _secret_sync_free (sync);
835
836         return count;
837 }
838
839 /**
840  * secret_service_unlock:
841  * @service: (allow-none): the secret service
842  * @objects: (element-type GLib.DBusProxy): the items or collections to unlock
843  * @cancellable: optional cancellation object
844  * @callback: called when the operation completes
845  * @user_data: data to pass to the callback
846  *
847  * Unlock items or collections in the secret service.
848  *
849  * The secret service may not be able to unlock items individually, and may
850  * unlock an entire collection instead.
851  *
852  * If @service is NULL, then secret_service_get() will be called to get
853  * the default #SecretService proxy.
854  *
855  * This method may block indefinitely and should not be used in user
856  * interface threads. The secret service may prompt the user.
857  * secret_service_prompt() will be used to handle any prompts that show up.
858  */
859 void
860 secret_service_unlock (SecretService *service,
861                        GList *objects,
862                        GCancellable *cancellable,
863                        GAsyncReadyCallback callback,
864                        gpointer user_data)
865 {
866         g_return_if_fail (service == NULL || SECRET_IS_SERVICE (service));
867         g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
868
869         service_xlock_async (service, FALSE, objects, cancellable, callback, user_data);
870 }
871
872 /**
873  * secret_service_unlock_finish:
874  * @service: (allow-none): the secret service
875  * @result: asynchronous result passed to the callback
876  * @unlocked: (out) (element-type GLib.DBusProxy) (transfer full) (allow-none):
877  *            location to place list of items or collections that were unlocked
878  * @error: location to place an error on failure
879  *
880  * Complete asynchronous operation to unlock items or collections in the secret
881  * service.
882  *
883  * The secret service may not be able to unlock items individually, and may
884  * unlock an entire collection instead.
885  *
886  * Returns: the number of items or collections that were unlocked
887  */
888 gint
889 secret_service_unlock_finish (SecretService *service,
890                               GAsyncResult *result,
891                               GList **unlocked,
892                               GError **error)
893 {
894         g_return_val_if_fail (service == NULL || SECRET_IS_SERVICE (service), -1);
895         g_return_val_if_fail (error == NULL || *error == NULL, -1);
896
897         return service_xlock_finish (service, result, unlocked, error);
898 }
899
900 /**
901  * secret_service_unlock_sync:
902  * @service: (allow-none): the secret service
903  * @objects: (element-type GLib.DBusProxy): the items or collections to unlock
904  * @cancellable: optional cancellation object
905  * @unlocked: (out) (element-type GLib.DBusProxy) (transfer full) (allow-none):
906  *            location to place list of items or collections that were unlocked
907  * @error: location to place an error on failure
908  *
909  * Unlock items or collections in the secret service.
910  *
911  * The secret service may not be able to unlock items individually, and may
912  * unlock an entire collection instead.
913  *
914  * If @service is NULL, then secret_service_get_sync() will be called to get
915  * the default #SecretService proxy.
916  *
917  * This method may block indefinitely and should not be used in user
918  * interface threads. The secret service may prompt the user.
919  * secret_service_prompt() will be used to handle any prompts that show up.
920  *
921  * Returns: the number of items or collections that were unlocked
922  */
923 gint
924 secret_service_unlock_sync (SecretService *service,
925                             GList *objects,
926                             GCancellable *cancellable,
927                             GList **unlocked,
928                             GError **error)
929 {
930         SecretSync *sync;
931         gint count;
932
933         g_return_val_if_fail (service == NULL || SECRET_IS_SERVICE (service), -1);
934         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), -1);
935         g_return_val_if_fail (error == NULL || *error == NULL, -1);
936
937         sync = _secret_sync_new ();
938         g_main_context_push_thread_default (sync->context);
939
940         secret_service_unlock (service, objects, cancellable,
941                                _secret_sync_on_result, sync);
942
943         g_main_loop_run (sync->loop);
944
945         count = secret_service_unlock_finish (service, sync->result, unlocked, error);
946
947         g_main_context_pop_thread_default (sync->context);
948         _secret_sync_free (sync);
949
950         return count;
951 }
952
953 typedef struct {
954         GCancellable *cancellable;
955         gchar *collection_path;
956         SecretValue *value;
957         GHashTable *properties;
958 } StoreClosure;
959
960 static void
961 store_closure_free (gpointer data)
962 {
963         StoreClosure *store = data;
964         if (store->cancellable)
965                 g_object_unref (store->cancellable);
966         g_free (store->collection_path);
967         secret_value_unref (store->value);
968         g_hash_table_unref (store->properties);
969         g_slice_free (StoreClosure, store);
970 }
971
972 static void
973 on_store_create (GObject *source,
974                  GAsyncResult *result,
975                  gpointer user_data)
976 {
977         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
978         GError *error = NULL;
979         gchar *path;
980
981         path = secret_service_create_item_dbus_path_finish (SECRET_SERVICE (source), result, &error);
982         if (error != NULL)
983                 g_simple_async_result_take_error (async, error);
984         g_free (path);
985
986         g_simple_async_result_complete (async);
987         g_object_unref (async);
988 }
989
990 static void
991 on_store_service (GObject *source,
992                   GAsyncResult *result,
993                   gpointer user_data)
994 {
995         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
996         StoreClosure *store = g_simple_async_result_get_op_res_gpointer (async);
997         SecretService *service;
998         GError *error = NULL;
999
1000         service = secret_service_get_finish (result, &error);
1001         if (error == NULL) {
1002                 secret_service_create_item_dbus_path (service, store->collection_path,
1003                                                       store->properties, store->value,
1004                                                       SECRET_ITEM_CREATE_REPLACE, store->cancellable,
1005                                                       on_store_create, g_object_ref (async));
1006                 g_object_unref (service);
1007
1008         } else {
1009                 g_simple_async_result_take_error (async, error);
1010                 g_simple_async_result_complete (async);
1011         }
1012
1013         g_object_unref (async);
1014 }
1015
1016 /**
1017  * secret_service_store:
1018  * @service: (allow-none): the secret service
1019  * @schema: (allow-none): the schema to use to check attributes
1020  * @attributes: (element-type utf8 utf8): the attribute keys and values
1021  * @collection: (allow-none): a collection alias, or D-Bus object path of the collection where to store the secret
1022  * @label: label for the secret
1023  * @value: the secret value
1024  * @cancellable: optional cancellation object
1025  * @callback: called when the operation completes
1026  * @user_data: data to be passed to the callback
1027  *
1028  * Store a secret value in the secret service.
1029  *
1030  * The @attributes should be a set of key and value string pairs.
1031  *
1032  * If the attributes match a secret item already stored in the collection, then
1033  * the item will be updated with these new values.
1034  *
1035  * If @service is NULL, then secret_service_get() will be called to get
1036  * the default #SecretService proxy.
1037  *
1038  * If @collection is not specified, then the default collection will be
1039  * used. Use #SECRET_COLLECTION_SESSION to store the password in the session
1040  * collection, which doesn't get stored across login sessions.
1041  *
1042  * This method will return immediately and complete asynchronously.
1043  */
1044 void
1045 secret_service_store (SecretService *service,
1046                       const SecretSchema *schema,
1047                       GHashTable *attributes,
1048                       const gchar *collection,
1049                       const gchar *label,
1050                       SecretValue *value,
1051                       GCancellable *cancellable,
1052                       GAsyncReadyCallback callback,
1053                       gpointer user_data)
1054 {
1055         GSimpleAsyncResult *async;
1056         StoreClosure *store;
1057         const gchar *schema_name;
1058         GVariant *propval;
1059
1060         g_return_if_fail (service == NULL || SECRET_IS_SERVICE (service));
1061         g_return_if_fail (attributes != NULL);
1062         g_return_if_fail (label != NULL);
1063         g_return_if_fail (value != NULL);
1064         g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1065
1066         /* Warnings raised already */
1067         if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, FALSE))
1068                 return;
1069
1070         async = g_simple_async_result_new  (G_OBJECT (service), callback, user_data,
1071                                             secret_service_store);
1072         store = g_slice_new0 (StoreClosure);
1073         store->collection_path = _secret_util_collection_to_path (collection);
1074         store->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
1075         store->value = secret_value_ref (value);
1076         store->properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
1077                                                    (GDestroyNotify)g_variant_unref);
1078
1079         propval = g_variant_new_string (label);
1080         g_hash_table_insert (store->properties,
1081                              SECRET_ITEM_INTERFACE ".Label",
1082                              g_variant_ref_sink (propval));
1083
1084         /* Always store the schema name in the attributes */
1085         schema_name = (schema == NULL) ? NULL : schema->name;
1086         propval = _secret_attributes_to_variant (attributes, schema_name);
1087         g_hash_table_insert (store->properties,
1088                              SECRET_ITEM_INTERFACE ".Attributes",
1089                              g_variant_ref_sink (propval));
1090
1091         g_simple_async_result_set_op_res_gpointer (async, store, store_closure_free);
1092
1093         if (service == NULL) {
1094                 secret_service_get (SECRET_SERVICE_OPEN_SESSION, cancellable,
1095                                     on_store_service, g_object_ref (async));
1096
1097         } else {
1098                 secret_service_create_item_dbus_path (service, store->collection_path,
1099                                                       store->properties, store->value,
1100                                                       SECRET_ITEM_CREATE_REPLACE, store->cancellable,
1101                                                       on_store_create, g_object_ref (async));
1102         }
1103
1104         g_object_unref (async);
1105 }
1106
1107 /**
1108  * secret_service_store_finish:
1109  * @service: (allow-none): the secret service
1110  * @result: the asynchronous result passed to the callback
1111  * @error: location to place an error on failure
1112  *
1113  * Finish asynchronous operation to store a secret value in the secret service.
1114  *
1115  * Returns: whether the storage was successful or not
1116  */
1117 gboolean
1118 secret_service_store_finish (SecretService *service,
1119                              GAsyncResult *result,
1120                              GError **error)
1121 {
1122         g_return_val_if_fail (service == NULL || SECRET_IS_SERVICE (service), FALSE);
1123         g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (service),
1124                                                               secret_service_store), FALSE);
1125         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1126
1127         if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
1128                 return FALSE;
1129
1130         return TRUE;
1131 }
1132
1133 /**
1134  * secret_service_store_sync:
1135  * @service: (allow-none): the secret service
1136  * @schema: (allow-none): the schema for the attributes
1137  * @attributes: (element-type utf8 utf8): the attribute keys and values
1138  * @collection: (allow-none): a collection alias, or D-Bus object path of the collection where to store the secret
1139  * @label: label for the secret
1140  * @value: the secret value
1141  * @cancellable: optional cancellation object
1142  * @error: location to place an error on failure
1143  *
1144  * Store a secret value in the secret service.
1145  *
1146  * The @attributes should be a set of key and value string pairs.
1147  *
1148  * If the attributes match a secret item already stored in the collection, then
1149  * the item will be updated with these new values.
1150  *
1151  * If @collection is %NULL, then the default collection will be
1152  * used. Use #SECRET_COLLECTION_SESSION to store the password in the session
1153  * collection, which doesn't get stored across login sessions.
1154  *
1155  * If @service is NULL, then secret_service_get_sync() will be called to get
1156  * the default #SecretService proxy.
1157  *
1158  * This method may block indefinitely and should not be used in user interface
1159  * threads.
1160  *
1161  * Returns: whether the storage was successful or not
1162  */
1163 gboolean
1164 secret_service_store_sync (SecretService *service,
1165                            const SecretSchema *schema,
1166                            GHashTable *attributes,
1167                            const gchar *collection,
1168                            const gchar *label,
1169                            SecretValue *value,
1170                            GCancellable *cancellable,
1171                            GError **error)
1172 {
1173         SecretSync *sync;
1174         gboolean ret;
1175
1176         g_return_val_if_fail (service == NULL || SECRET_IS_SERVICE (service), FALSE);
1177         g_return_val_if_fail (attributes != NULL, FALSE);
1178         g_return_val_if_fail (label != NULL, FALSE);
1179         g_return_val_if_fail (value != NULL, FALSE);
1180         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
1181         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1182
1183         /* Warnings raised already */
1184         if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, FALSE))
1185                 return FALSE;
1186
1187         sync = _secret_sync_new ();
1188         g_main_context_push_thread_default (sync->context);
1189
1190         secret_service_store (service, schema, attributes, collection,
1191                               label, value, cancellable, _secret_sync_on_result, sync);
1192
1193         g_main_loop_run (sync->loop);
1194
1195         ret = secret_service_store_finish (service, sync->result, error);
1196
1197         g_main_context_pop_thread_default (sync->context);
1198         _secret_sync_free (sync);
1199
1200         return ret;
1201 }
1202
1203 typedef struct {
1204         GVariant *attributes;
1205         SecretValue *value;
1206         GCancellable *cancellable;
1207 } LookupClosure;
1208
1209 static void
1210 lookup_closure_free (gpointer data)
1211 {
1212         LookupClosure *closure = data;
1213         g_variant_unref (closure->attributes);
1214         if (closure->value)
1215                 secret_value_unref (closure->value);
1216         g_clear_object (&closure->cancellable);
1217         g_slice_free (LookupClosure, closure);
1218 }
1219
1220 static void
1221 on_lookup_get_secret (GObject *source,
1222                       GAsyncResult *result,
1223                       gpointer user_data)
1224 {
1225         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
1226         LookupClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
1227         SecretService *self = SECRET_SERVICE (source);
1228         GError *error = NULL;
1229
1230         closure->value = secret_service_get_secret_for_dbus_path_finish (self, result, &error);
1231         if (error != NULL)
1232                 g_simple_async_result_take_error (res, error);
1233
1234         g_simple_async_result_complete (res);
1235         g_object_unref (res);
1236 }
1237
1238 static void
1239 on_lookup_unlocked (GObject *source,
1240                     GAsyncResult *result,
1241                     gpointer user_data)
1242 {
1243         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
1244         LookupClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
1245         SecretService *self = SECRET_SERVICE (source);
1246         GError *error = NULL;
1247         gchar **unlocked = NULL;
1248
1249         secret_service_unlock_dbus_paths_finish (SECRET_SERVICE (source),
1250                                                  result, &unlocked, &error);
1251         if (error != NULL) {
1252                 g_simple_async_result_take_error (res, error);
1253                 g_simple_async_result_complete (res);
1254
1255         } else if (unlocked && unlocked[0]) {
1256                 secret_service_get_secret_for_dbus_path (self, unlocked[0],
1257                                                          closure->cancellable,
1258                                                          on_lookup_get_secret,
1259                                                          g_object_ref (res));
1260
1261         } else {
1262                 g_simple_async_result_complete (res);
1263         }
1264
1265         g_strfreev (unlocked);
1266         g_object_unref (res);
1267 }
1268
1269 static void
1270 on_lookup_searched (GObject *source,
1271                     GAsyncResult *result,
1272                     gpointer user_data)
1273 {
1274         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
1275         LookupClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
1276         SecretService *self = SECRET_SERVICE (source);
1277         GError *error = NULL;
1278         gchar **unlocked = NULL;
1279         gchar **locked = NULL;
1280
1281         secret_service_search_for_dbus_paths_finish (self, result, &unlocked, &locked, &error);
1282         if (error != NULL) {
1283                 g_simple_async_result_take_error (res, error);
1284                 g_simple_async_result_complete (res);
1285
1286         } else if (unlocked && unlocked[0]) {
1287                 secret_service_get_secret_for_dbus_path (self, unlocked[0],
1288                                                          closure->cancellable,
1289                                                          on_lookup_get_secret,
1290                                                          g_object_ref (res));
1291
1292         } else if (locked && locked[0]) {
1293                 const gchar *paths[] = { locked[0], NULL };
1294                 secret_service_unlock_dbus_paths (self, paths,
1295                                                   closure->cancellable,
1296                                                   on_lookup_unlocked,
1297                                                   g_object_ref (res));
1298
1299         } else {
1300                 g_simple_async_result_complete (res);
1301         }
1302
1303         g_strfreev (unlocked);
1304         g_strfreev (locked);
1305         g_object_unref (res);
1306 }
1307
1308 static void
1309 on_lookup_service (GObject *source,
1310                    GAsyncResult *result,
1311                    gpointer user_data)
1312 {
1313         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
1314         LookupClosure *lookup = g_simple_async_result_get_op_res_gpointer (async);
1315         SecretService *service;
1316         GError *error = NULL;
1317
1318         service = secret_service_get_finish (result, &error);
1319         if (error == NULL) {
1320                 _secret_service_search_for_paths_variant (service, lookup->attributes,
1321                                                           lookup->cancellable,
1322                                                           on_lookup_searched, g_object_ref (async));
1323                 g_object_unref (service);
1324
1325         } else {
1326                 g_simple_async_result_take_error (async, error);
1327                 g_simple_async_result_complete (async);
1328         }
1329
1330         g_object_unref (async);
1331 }
1332
1333 /**
1334  * secret_service_lookup:
1335  * @service: (allow-none): the secret service
1336  * @schema: (allow-none): the schema for the attributes
1337  * @attributes: (element-type utf8 utf8): the attribute keys and values
1338  * @cancellable: optional cancellation object
1339  * @callback: called when the operation completes
1340  * @user_data: data to be passed to the callback
1341  *
1342  * Lookup a secret value in the secret service.
1343  *
1344  * The @attributes should be a set of key and value string pairs.
1345  *
1346  * If @service is NULL, then secret_service_get() will be called to get
1347  * the default #SecretService proxy.
1348  *
1349  * This method will return immediately and complete asynchronously.
1350  */
1351 void
1352 secret_service_lookup (SecretService *service,
1353                        const SecretSchema *schema,
1354                        GHashTable *attributes,
1355                        GCancellable *cancellable,
1356                        GAsyncReadyCallback callback,
1357                        gpointer user_data)
1358 {
1359         const gchar *schema_name = NULL;
1360         GSimpleAsyncResult *res;
1361         LookupClosure *closure;
1362
1363         g_return_if_fail (service == NULL || SECRET_IS_SERVICE (service));
1364         g_return_if_fail (attributes != NULL);
1365         g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1366
1367         /* Warnings raised already */
1368         if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, TRUE))
1369                 return;
1370
1371         if (schema != NULL && !(schema->flags & SECRET_SCHEMA_DONT_MATCH_NAME))
1372                 schema_name = schema->name;
1373
1374         res = g_simple_async_result_new (G_OBJECT (service), callback, user_data,
1375                                          secret_service_lookup);
1376         closure = g_slice_new0 (LookupClosure);
1377         closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
1378         closure->attributes = _secret_attributes_to_variant (attributes, schema_name);
1379         g_variant_ref_sink (closure->attributes);
1380         g_simple_async_result_set_op_res_gpointer (res, closure, lookup_closure_free);
1381
1382         if (service == NULL) {
1383                 secret_service_get (SECRET_SERVICE_OPEN_SESSION, cancellable,
1384                                     on_lookup_service, g_object_ref (res));
1385         } else {
1386                 _secret_service_search_for_paths_variant (service, closure->attributes,
1387                                                           closure->cancellable,
1388                                                           on_lookup_searched, g_object_ref (res));
1389         }
1390
1391         g_object_unref (res);
1392 }
1393
1394 /**
1395  * secret_service_lookup_finish:
1396  * @service: (allow-none): the secret service
1397  * @result: the asynchronous result passed to the callback
1398  * @error: location to place an error on failure
1399  *
1400  * Finish asynchronous operation to lookup a secret value in the secret service.
1401  *
1402  * If no secret is found then %NULL is returned.
1403  *
1404  * Returns: (transfer full): a newly allocated #SecretValue, which should be
1405  *          released with secret_value_unref(), or %NULL if no secret found
1406  */
1407 SecretValue *
1408 secret_service_lookup_finish (SecretService *service,
1409                               GAsyncResult *result,
1410                               GError **error)
1411 {
1412         GSimpleAsyncResult *res;
1413         LookupClosure *closure;
1414         SecretValue *value;
1415
1416         g_return_val_if_fail (service == NULL || SECRET_IS_SERVICE (service), NULL);
1417         g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1418         g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (service),
1419                               secret_service_lookup), NULL);
1420
1421         res = G_SIMPLE_ASYNC_RESULT (result);
1422         if (g_simple_async_result_propagate_error (res, error))
1423                 return NULL;
1424
1425         closure = g_simple_async_result_get_op_res_gpointer (res);
1426         value = closure->value;
1427         closure->value = NULL;
1428         return value;
1429 }
1430
1431 /**
1432  * secret_service_lookup_sync:
1433  * @service: (allow-none): the secret service
1434  * @schema: (allow-none): the schema for the attributes
1435  * @attributes: (element-type utf8 utf8): the attribute keys and values
1436  * @cancellable: optional cancellation object
1437  * @error: location to place an error on failure
1438  *
1439  * Lookup a secret value in the secret service.
1440  *
1441  * The @attributes should be a set of key and value string pairs.
1442  *
1443  * If @service is NULL, then secret_service_get_sync() will be called to get
1444  * the default #SecretService proxy.
1445  *
1446  * This method may block indefinitely and should not be used in user interface
1447  * threads.
1448  *
1449  * Returns: (transfer full): a newly allocated #SecretValue, which should be
1450  *          released with secret_value_unref(), or %NULL if no secret found
1451  */
1452 SecretValue *
1453 secret_service_lookup_sync (SecretService *service,
1454                             const SecretSchema *schema,
1455                             GHashTable *attributes,
1456                             GCancellable *cancellable,
1457                             GError **error)
1458 {
1459         SecretSync *sync;
1460         SecretValue *value;
1461
1462         g_return_val_if_fail (service == NULL || SECRET_IS_SERVICE (service), NULL);
1463         g_return_val_if_fail (attributes != NULL, NULL);
1464         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
1465
1466         /* Warnings raised already */
1467         if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, TRUE))
1468                 return NULL;
1469
1470         sync = _secret_sync_new ();
1471         g_main_context_push_thread_default (sync->context);
1472
1473         secret_service_lookup (service, schema, attributes, cancellable,
1474                                _secret_sync_on_result, sync);
1475
1476         g_main_loop_run (sync->loop);
1477
1478         value = secret_service_lookup_finish (service, sync->result, error);
1479
1480         g_main_context_pop_thread_default (sync->context);
1481         _secret_sync_free (sync);
1482
1483         return value;
1484 }
1485
1486 typedef struct {
1487         GCancellable *cancellable;
1488         SecretService *service;
1489         GVariant *attributes;
1490         gint deleted;
1491         gint deleting;
1492 } DeleteClosure;
1493
1494 static void
1495 delete_closure_free (gpointer data)
1496 {
1497         DeleteClosure *closure = data;
1498         if (closure->service)
1499                 g_object_unref (closure->service);
1500         g_variant_unref (closure->attributes);
1501         g_clear_object (&closure->cancellable);
1502         g_slice_free (DeleteClosure, closure);
1503 }
1504
1505 static void
1506 on_delete_password_complete (GObject *source,
1507                              GAsyncResult *result,
1508                              gpointer user_data)
1509 {
1510         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
1511         DeleteClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
1512         GError *error = NULL;
1513         gboolean deleted;
1514
1515         closure->deleting--;
1516
1517         deleted = _secret_service_delete_path_finish (SECRET_SERVICE (source), result, &error);
1518         if (error != NULL)
1519                 g_simple_async_result_take_error (res, error);
1520         if (deleted)
1521                 closure->deleted++;
1522
1523         if (closure->deleting <= 0)
1524                 g_simple_async_result_complete (res);
1525
1526         g_object_unref (res);
1527 }
1528
1529 static void
1530 on_delete_searched (GObject *source,
1531                     GAsyncResult *result,
1532                     gpointer user_data)
1533 {
1534         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
1535         DeleteClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
1536         GError *error = NULL;
1537         gchar **unlocked = NULL;
1538         gint i;
1539
1540         secret_service_search_for_dbus_paths_finish (SECRET_SERVICE (source), result, &unlocked, NULL, &error);
1541         if (error == NULL) {
1542                 for (i = 0; unlocked[i] != NULL; i++) {
1543                         _secret_service_delete_path (closure->service, unlocked[i], TRUE,
1544                                                      closure->cancellable,
1545                                                      on_delete_password_complete,
1546                                                      g_object_ref (res));
1547                         closure->deleting++;
1548                 }
1549
1550                 if (closure->deleting == 0)
1551                         g_simple_async_result_complete (res);
1552         } else {
1553                 g_simple_async_result_take_error (res, error);
1554                 g_simple_async_result_complete (res);
1555         }
1556
1557         g_strfreev (unlocked);
1558         g_object_unref (res);
1559 }
1560
1561 static void
1562 on_delete_service (GObject *source,
1563                    GAsyncResult *result,
1564                    gpointer user_data)
1565 {
1566         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
1567         DeleteClosure *closure = g_simple_async_result_get_op_res_gpointer (async);
1568         GError *error = NULL;
1569
1570         closure->service = secret_service_get_finish (result, &error);
1571         if (error == NULL) {
1572                 _secret_service_search_for_paths_variant (closure->service, closure->attributes,
1573                                                           closure->cancellable,
1574                                                           on_delete_searched, g_object_ref (async));
1575
1576         } else {
1577                 g_simple_async_result_take_error (async, error);
1578                 g_simple_async_result_complete (async);
1579         }
1580
1581         g_object_unref (async);
1582 }
1583
1584 /**
1585  * secret_service_remove:
1586  * @service: (allow-none): the secret service
1587  * @schema: (allow-none): the schema for the attributes
1588  * @attributes: (element-type utf8 utf8): the attribute keys and values
1589  * @cancellable: optional cancellation object
1590  * @callback: called when the operation completes
1591  * @user_data: data to be passed to the callback
1592  *
1593  * Remove a secret value from the secret service.
1594  *
1595  * The @attributes should be a set of key and value string pairs.
1596  *
1597  * If multiple items match the attributes, then only one will be deleted.
1598  *
1599  * If @service is NULL, then secret_service_get() will be called to get
1600  * the default #SecretService proxy.
1601  *
1602  * This method will return immediately and complete asynchronously.
1603  */
1604 void
1605 secret_service_remove (SecretService *service,
1606                        const SecretSchema *schema,
1607                        GHashTable *attributes,
1608                        GCancellable *cancellable,
1609                        GAsyncReadyCallback callback,
1610                        gpointer user_data)
1611 {
1612         const gchar *schema_name = NULL;
1613         GSimpleAsyncResult *res;
1614         DeleteClosure *closure;
1615
1616         g_return_if_fail (service == NULL || SECRET_SERVICE (service));
1617         g_return_if_fail (attributes != NULL);
1618         g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1619
1620         /* Warnings raised already */
1621         if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, TRUE))
1622                 return;
1623
1624         if (schema != NULL && !(schema->flags & SECRET_SCHEMA_DONT_MATCH_NAME))
1625                 schema_name = schema->name;
1626
1627         res = g_simple_async_result_new (G_OBJECT (service), callback, user_data,
1628                                          secret_service_remove);
1629         closure = g_slice_new0 (DeleteClosure);
1630         closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
1631         closure->attributes = _secret_attributes_to_variant (attributes, schema_name);
1632         g_variant_ref_sink (closure->attributes);
1633         g_simple_async_result_set_op_res_gpointer (res, closure, delete_closure_free);
1634
1635         /* A double check to make sure we don't delete everything, should have been checked earlier */
1636         g_assert (g_variant_n_children (closure->attributes) > 0);
1637
1638         if (service == NULL) {
1639                 secret_service_get (SECRET_SERVICE_NONE, cancellable,
1640                                     on_delete_service, g_object_ref (res));
1641         } else {
1642                 closure->service = g_object_ref (service);
1643                 _secret_service_search_for_paths_variant (closure->service, closure->attributes,
1644                                                           closure->cancellable,
1645                                                           on_delete_searched, g_object_ref (res));
1646         }
1647
1648         g_object_unref (res);
1649 }
1650
1651 /**
1652  * secret_service_remove_finish:
1653  * @service: (allow-none): the secret service
1654  * @result: the asynchronous result passed to the callback
1655  * @error: location to place an error on failure
1656  *
1657  * Finish asynchronous operation to remove a secret value from the secret
1658  * service.
1659  *
1660  * Returns: whether the removal was successful or not
1661  */
1662 gboolean
1663 secret_service_remove_finish (SecretService *service,
1664                               GAsyncResult *result,
1665                               GError **error)
1666 {
1667         GSimpleAsyncResult *res;
1668         DeleteClosure *closure;
1669
1670         g_return_val_if_fail (service == NULL || SECRET_IS_SERVICE (service), FALSE);
1671         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1672         g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (service),
1673                               secret_service_remove), FALSE);
1674
1675         res = G_SIMPLE_ASYNC_RESULT (result);
1676         if (g_simple_async_result_propagate_error (res, error))
1677                 return FALSE;
1678
1679         closure = g_simple_async_result_get_op_res_gpointer (res);
1680         return closure->deleted > 0;
1681 }
1682
1683 /**
1684  * secret_service_remove_sync:
1685  * @service: (allow-none): the secret service
1686  * @schema: (allow-none): the schema for the attributes
1687  * @attributes: (element-type utf8 utf8): the attribute keys and values
1688  * @cancellable: optional cancellation object
1689  * @error: location to place an error on failure
1690  *
1691  * Remove a secret value from the secret service.
1692  *
1693  * The @attributes should be a set of key and value string pairs.
1694  *
1695  * If multiple items match the attributes, then only one will be deleted.
1696  *
1697  * If @service is NULL, then secret_service_get_sync() will be called to get
1698  * the default #SecretService proxy.
1699  *
1700  * This method may block indefinitely and should not be used in user interface
1701  * threads.
1702  *
1703  * Returns: whether the removal was successful or not
1704  */
1705 gboolean
1706 secret_service_remove_sync (SecretService *service,
1707                             const SecretSchema *schema,
1708                             GHashTable *attributes,
1709                             GCancellable *cancellable,
1710                             GError **error)
1711 {
1712         SecretSync *sync;
1713         gboolean result;
1714
1715         g_return_val_if_fail (service == NULL || SECRET_IS_SERVICE (service), FALSE);
1716         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
1717         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1718
1719         /* Warnings raised already */
1720         if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, TRUE))
1721                 return FALSE;
1722
1723         sync = _secret_sync_new ();
1724         g_main_context_push_thread_default (sync->context);
1725
1726         secret_service_remove (service, schema, attributes, cancellable,
1727                                _secret_sync_on_result, sync);
1728
1729         g_main_loop_run (sync->loop);
1730
1731         result = secret_service_remove_finish (service, sync->result, error);
1732
1733         g_main_context_pop_thread_default (sync->context);
1734         _secret_sync_free (sync);
1735
1736         return result;
1737 }
1738
1739 typedef struct {
1740         GCancellable *cancellable;
1741         gchar *alias;
1742         gchar *collection_path;
1743 } SetClosure;
1744
1745 static void
1746 set_closure_free (gpointer data)
1747 {
1748         SetClosure *set = data;
1749         if (set->cancellable)
1750                 g_object_unref (set->cancellable);
1751         g_free (set->alias);
1752         g_free (set->collection_path);
1753         g_slice_free (SetClosure, set);
1754 }
1755
1756 static void
1757 on_set_alias_done (GObject *source,
1758                    GAsyncResult *result,
1759                    gpointer user_data)
1760 {
1761         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
1762         GError *error = NULL;
1763
1764         secret_service_set_alias_to_dbus_path_finish (SECRET_SERVICE (source), result, &error);
1765         if (error != NULL)
1766                 g_simple_async_result_take_error (async, error);
1767
1768         g_simple_async_result_complete (async);
1769         g_object_unref (async);
1770 }
1771
1772 static void
1773 on_set_alias_service (GObject *source,
1774                       GAsyncResult *result,
1775                       gpointer user_data)
1776 {
1777         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
1778         SetClosure *set = g_simple_async_result_get_op_res_gpointer (async);
1779         SecretService *service;
1780         GError *error = NULL;
1781
1782         service = secret_service_get_finish (result, &error);
1783         if (error == NULL) {
1784                 secret_service_set_alias_to_dbus_path (service, set->alias,
1785                                                        set->collection_path,
1786                                                        set->cancellable,
1787                                                        on_set_alias_done,
1788                                                        g_object_ref (async));
1789                 g_object_unref (service);
1790
1791         } else {
1792                 g_simple_async_result_take_error (async, error);
1793                 g_simple_async_result_complete (async);
1794         }
1795
1796         g_object_unref (async);
1797 }
1798
1799 /**
1800  * secret_service_set_alias:
1801  * @service: (allow-none): a secret service object
1802  * @alias: the alias to assign the collection to
1803  * @collection: (allow-none): the collection to assign to the alias
1804  * @cancellable: (allow-none): optional cancellation object
1805  * @callback: called when the operation completes
1806  * @user_data: data to pass to the callback
1807  *
1808  * Assign a collection to this alias. Aliases help determine
1809  * well known collections, such as 'default'.
1810  *
1811  * If @service is NULL, then secret_service_get() will be called to get
1812  * the default #SecretService proxy.
1813  *
1814  * This method will return immediately and complete asynchronously.
1815  */
1816 void
1817 secret_service_set_alias (SecretService *service,
1818                           const gchar *alias,
1819                           SecretCollection *collection,
1820                           GCancellable *cancellable,
1821                           GAsyncReadyCallback callback,
1822                           gpointer user_data)
1823 {
1824         GSimpleAsyncResult *async;
1825         SetClosure *set;
1826         const gchar *path;
1827
1828         g_return_if_fail (service == NULL || SECRET_IS_SERVICE (service));
1829         g_return_if_fail (alias != NULL);
1830         g_return_if_fail (collection == NULL || SECRET_IS_COLLECTION (collection));
1831         g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1832
1833         async = g_simple_async_result_new (G_OBJECT (service), callback, user_data,
1834                                            secret_service_set_alias);
1835         set = g_slice_new0 (SetClosure);
1836         set->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
1837         set->alias = g_strdup (alias);
1838
1839         if (collection) {
1840                 path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (collection));
1841                 g_return_if_fail (path != NULL);
1842         } else {
1843                 path = NULL;
1844         }
1845
1846         set->collection_path = g_strdup (path);
1847         g_simple_async_result_set_op_res_gpointer (async, set, set_closure_free);
1848
1849         if (service == NULL) {
1850                 secret_service_get (SECRET_SERVICE_NONE, cancellable,
1851                                     on_set_alias_service, g_object_ref (async));
1852         } else {
1853                 secret_service_set_alias_to_dbus_path (service, set->alias,
1854                                                        set->collection_path,
1855                                                        set->cancellable,
1856                                                        on_set_alias_done,
1857                                                        g_object_ref (async));
1858         }
1859
1860         g_object_unref (async);
1861 }
1862
1863 /**
1864  * secret_service_set_alias_finish:
1865  * @service: (allow-none): a secret service object
1866  * @result: asynchronous result passed to callback
1867  * @error: location to place error on failure
1868  *
1869  * Finish an asynchronous operation to assign a collection to an alias.
1870  *
1871  * Returns: %TRUE if successful
1872  */
1873 gboolean
1874 secret_service_set_alias_finish (SecretService *service,
1875                                  GAsyncResult *result,
1876                                  GError **error)
1877 {
1878         g_return_val_if_fail (service == NULL || SECRET_IS_SERVICE (service), FALSE);
1879         g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (service),
1880                                                               secret_service_set_alias), FALSE);
1881         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1882
1883         if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
1884                 return FALSE;
1885
1886         return TRUE;
1887 }
1888
1889 /**
1890  * secret_service_set_alias_sync:
1891  * @service: (allow-none): a secret service object
1892  * @alias: the alias to assign the collection to
1893  * @collection: (allow-none): the collection to assign to the alias
1894  * @cancellable: (allow-none): optional cancellation object
1895  * @error: location to place error on failure
1896  *
1897  * Assign a collection to this alias. Aliases help determine
1898  * well known collections, such as 'default'.
1899  *
1900  * If @service is NULL, then secret_service_get_sync() will be called to get
1901  * the default #SecretService proxy.
1902  *
1903  * This method may block and should not be used in user interface threads.
1904  *
1905  * Returns: %TRUE if successful
1906  */
1907 gboolean
1908 secret_service_set_alias_sync (SecretService *service,
1909                                const gchar *alias,
1910                                SecretCollection *collection,
1911                                GCancellable *cancellable,
1912                                GError **error)
1913 {
1914         SecretSync *sync;
1915         gboolean ret;
1916
1917         g_return_val_if_fail (service == NULL || SECRET_IS_SERVICE (service), FALSE);
1918         g_return_val_if_fail (alias != NULL, FALSE);
1919         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
1920         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1921
1922         sync = _secret_sync_new ();
1923         g_main_context_push_thread_default (sync->context);
1924
1925         secret_service_set_alias (service, alias, collection, cancellable,
1926                                   _secret_sync_on_result, sync);
1927
1928         g_main_loop_run (sync->loop);
1929
1930         ret = secret_service_set_alias_finish (service, sync->result, error);
1931
1932         g_main_context_pop_thread_default (sync->context);
1933         _secret_sync_free (sync);
1934
1935         return ret;
1936 }