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