Use upstream tag
[platform/upstream/libsecret.git] / libsecret / secret-item.c
1 /* libsecret - GLib wrapper for Secret Service
2  *
3  * Copyright 2012 Red Hat Inc.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published
7  * by the Free Software Foundation; either version 2.1 of the licence or (at
8  * your option) any later version.
9  *
10  * See the included COPYING file for more information.
11  *
12  * Author: Stef Walter <stefw@gnome.org>
13  */
14
15 #include "config.h"
16
17 #include "secret-collection.h"
18 #include "secret-dbus-generated.h"
19 #include "secret-item.h"
20 #include "secret-paths.h"
21 #include "secret-private.h"
22 #include "secret-service.h"
23 #include "secret-types.h"
24 #include "secret-value.h"
25
26 #include "libsecret/secret-enum-types.h"
27
28 #include <glib/gi18n-lib.h>
29
30 /**
31  * SECTION:secret-item
32  * @title: SecretItem
33  * @short_description: A secret item
34  *
35  * #SecretItem represents a secret item stored in the Secret Service.
36  *
37  * Each item has a value, represented by a #SecretValue, which can be
38  * retrieved by secret_item_get_secret() or set by secret_item_set_secret().
39  * The item is only available when the item is not locked.
40  *
41  * Items can be locked or unlocked using the secret_service_lock() or
42  * secret_service_unlock() functions. The Secret Service may not be able to
43  * unlock individual items, and may unlock an entire collection when a single
44  * item is unlocked.
45  *
46  * Each item has a set of attributes, which are used to locate the item later.
47  * These are not stored or transferred in a secure manner. Each attribute has
48  * a string name and a string value. Use secret_service_search() to search for
49  * items based on their attributes, and secret_item_set_attributes() to change
50  * the attributes associated with an item.
51  *
52  * Items can be created with secret_item_create() or secret_service_store().
53  *
54  * Stability: Stable
55  */
56
57 /**
58  * SecretItem:
59  *
60  * A proxy object representing a secret item in the Secret Service.
61  */
62
63 /**
64  * SecretItemClass:
65  * @parent_class: the parent class
66  *
67  * The class for #SecretItem.
68  */
69
70 /**
71  * SecretItemFlags:
72  * @SECRET_ITEM_NONE: no flags
73  * @SECRET_ITEM_LOAD_SECRET: a secret has been (or should be) loaded for #SecretItem
74  *
75  * Flags which determine which parts of the #SecretItem proxy are initialized.
76  */
77
78 /**
79  * SecretItemCreateFlags:
80  * @SECRET_ITEM_CREATE_NONE: no flags
81  * @SECRET_ITEM_CREATE_REPLACE: replace an item with the same attributes.
82  *
83  * Flags for secret_item_create().
84  */
85
86 enum {
87         PROP_0,
88         PROP_SERVICE,
89         PROP_FLAGS,
90         PROP_ATTRIBUTES,
91         PROP_LABEL,
92         PROP_LOCKED,
93         PROP_CREATED,
94         PROP_MODIFIED
95 };
96
97 struct _SecretItemPrivate {
98         /* No changes between construct and finalize */
99         SecretService *service;
100         SecretItemFlags init_flags;
101
102         /* Locked by mutex */
103         GMutex mutex;
104         SecretValue *value;
105         gint disposed;
106 };
107
108 static GInitableIface *secret_item_initable_parent_iface = NULL;
109
110 static GAsyncInitableIface *secret_item_async_initable_parent_iface = NULL;
111
112 static void   secret_item_initable_iface         (GInitableIface *iface);
113
114 static void   secret_item_async_initable_iface   (GAsyncInitableIface *iface);
115
116 G_DEFINE_TYPE_WITH_CODE (SecretItem, secret_item, G_TYPE_DBUS_PROXY,
117                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, secret_item_initable_iface);
118                          G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, secret_item_async_initable_iface);
119 );
120
121 static void
122 secret_item_init (SecretItem *self)
123 {
124         self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, SECRET_TYPE_ITEM, SecretItemPrivate);
125         g_mutex_init (&self->pv->mutex);
126 }
127
128 static void
129 on_set_attributes (GObject *source,
130                    GAsyncResult *result,
131                    gpointer user_data)
132 {
133         SecretItem *self = SECRET_ITEM (user_data);
134         GError *error = NULL;
135
136         if (!g_atomic_int_get (&self->pv->disposed)) {
137                 secret_item_set_attributes_finish (self, result, &error);
138                 if (error != NULL) {
139                         g_warning ("couldn't set SecretItem Attributes: %s", error->message);
140                         g_error_free (error);
141                 }
142         }
143
144         g_object_unref (self);
145 }
146
147 static void
148 on_set_label (GObject *source,
149               GAsyncResult *result,
150               gpointer user_data)
151 {
152         SecretItem *self = SECRET_ITEM (user_data);
153         GError *error = NULL;
154
155         if (!g_atomic_int_get (&self->pv->disposed)) {
156                 secret_item_set_label_finish (self, result, &error);
157                 if (error != NULL) {
158                         g_warning ("couldn't set SecretItem Label: %s", error->message);
159                         g_error_free (error);
160                 }
161         }
162
163         g_object_unref (self);
164 }
165
166
167 static void
168 item_take_service (SecretItem *self,
169                    SecretService *service)
170 {
171         if (service == NULL)
172                 return;
173
174         g_return_if_fail (self->pv->service == NULL);
175
176         self->pv->service = service;
177         g_object_add_weak_pointer (G_OBJECT (self->pv->service),
178                                    (gpointer *)&self->pv->service);
179
180         /* Yes, we expect that the service will stay around */
181         g_object_unref (service);
182 }
183
184 static void
185 secret_item_set_property (GObject *obj,
186                           guint prop_id,
187                           const GValue *value,
188                           GParamSpec *pspec)
189 {
190         SecretItem *self = SECRET_ITEM (obj);
191
192         switch (prop_id) {
193         case PROP_SERVICE:
194                 item_take_service (self, g_value_dup_object (value));
195                 break;
196         case PROP_FLAGS:
197                 self->pv->init_flags = g_value_get_flags (value);
198                 break;
199         case PROP_ATTRIBUTES:
200                 secret_item_set_attributes (self, NULL, g_value_get_boxed (value),
201                                             NULL, on_set_attributes,
202                                             g_object_ref (self));
203                 break;
204         case PROP_LABEL:
205                 secret_item_set_label (self, g_value_get_string (value),
206                                        NULL, on_set_label,
207                                        g_object_ref (self));
208                 break;
209         default:
210                 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
211                 break;
212         }
213 }
214
215 static void
216 secret_item_get_property (GObject *obj,
217                           guint prop_id,
218                           GValue *value,
219                           GParamSpec *pspec)
220 {
221         SecretItem *self = SECRET_ITEM (obj);
222
223         switch (prop_id) {
224         case PROP_SERVICE:
225                 g_value_set_object (value, self->pv->service);
226                 break;
227         case PROP_FLAGS:
228                 g_value_set_flags (value, secret_item_get_flags (self));
229                 break;
230         case PROP_ATTRIBUTES:
231                 g_value_take_boxed (value, secret_item_get_attributes (self));
232                 break;
233         case PROP_LABEL:
234                 g_value_take_string (value, secret_item_get_label (self));
235                 break;
236         case PROP_LOCKED:
237                 g_value_set_boolean (value, secret_item_get_locked (self));
238                 break;
239         case PROP_CREATED:
240                 g_value_set_uint64 (value, secret_item_get_created (self));
241                 break;
242         case PROP_MODIFIED:
243                 g_value_set_uint64 (value, secret_item_get_modified (self));
244                 break;
245         default:
246                 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
247                 break;
248         }
249 }
250
251 static void
252 secret_item_dispose (GObject *obj)
253 {
254         SecretItem *self = SECRET_ITEM (obj);
255
256         g_atomic_int_inc (&self->pv->disposed);
257
258         G_OBJECT_CLASS (secret_item_parent_class)->dispose (obj);
259 }
260
261 static void
262 secret_item_finalize (GObject *obj)
263 {
264         SecretItem *self = SECRET_ITEM (obj);
265
266         if (self->pv->service)
267                 g_object_remove_weak_pointer (G_OBJECT (self->pv->service),
268                                               (gpointer *)&self->pv->service);
269
270         g_mutex_clear (&self->pv->mutex);
271
272         G_OBJECT_CLASS (secret_item_parent_class)->finalize (obj);
273 }
274
275 static void
276 handle_property_changed (GObject *object,
277                          const gchar *property_name)
278 {
279         if (g_str_equal (property_name, "Attributes"))
280                 g_object_notify (object, "attributes");
281
282         else if (g_str_equal (property_name, "Label"))
283                 g_object_notify (object, "label");
284
285         else if (g_str_equal (property_name, "Locked"))
286                 g_object_notify (object, "locked");
287
288         else if (g_str_equal (property_name, "Created"))
289                 g_object_notify (object, "created");
290
291         else if (g_str_equal (property_name, "Modified"))
292                 g_object_notify (object, "modified");
293 }
294
295 static void
296 secret_item_properties_changed (GDBusProxy *proxy,
297                                 GVariant *changed_properties,
298                                 const gchar* const *invalidated_properties)
299 {
300         GObject *obj = G_OBJECT (proxy);
301         gchar *property_name;
302         GVariantIter iter;
303         GVariant *value;
304
305         g_object_freeze_notify (obj);
306
307         g_variant_iter_init (&iter, changed_properties);
308         while (g_variant_iter_loop (&iter, "{sv}", &property_name, &value))
309                 handle_property_changed (obj, property_name);
310
311         g_object_thaw_notify (obj);
312 }
313
314 static void
315 secret_item_class_init (SecretItemClass *klass)
316 {
317         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
318         GDBusProxyClass *proxy_class = G_DBUS_PROXY_CLASS (klass);
319
320         gobject_class->get_property = secret_item_get_property;
321         gobject_class->set_property = secret_item_set_property;
322         gobject_class->dispose = secret_item_dispose;
323         gobject_class->finalize = secret_item_finalize;
324
325         proxy_class->g_properties_changed = secret_item_properties_changed;
326
327         /**
328          * SecretItem:service:
329          *
330          * The #SecretService object that this item is associated with and
331          * uses to interact with the actual D-Bus Secret Service.
332          */
333         g_object_class_install_property (gobject_class, PROP_SERVICE,
334                     g_param_spec_object ("service", "Service", "Secret Service",
335                                          SECRET_TYPE_SERVICE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
336
337         /**
338          * SecretItem:flags:
339          *
340          * A set of flags describing which parts of the secret item have
341          * been initialized.
342          */
343         g_object_class_install_property (gobject_class, PROP_FLAGS,
344                      g_param_spec_flags ("flags", "Flags", "Item flags",
345                                          secret_item_flags_get_type (), SECRET_ITEM_NONE,
346                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
347
348         /**
349          * SecretItem:attributes:
350          *
351          * The attributes set on this item. Attributes are used to locate an
352          * item. They are not guaranteed to be stored or transferred securely.
353          *
354          * Type: GLib.HashTable(utf8,utf8)
355          * Transfer: full
356          */
357         g_object_class_install_property (gobject_class, PROP_ATTRIBUTES,
358                      g_param_spec_boxed ("attributes", "Attributes", "Item attributes",
359                                          G_TYPE_HASH_TABLE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
360
361         /**
362          * SecretItem:label:
363          *
364          * The human readable label for the item.
365          *
366          * Setting this property will result in the label of the item being
367          * set asynchronously. To properly track the changing of the label use the
368          * secret_item_set_label() function.
369          */
370         g_object_class_install_property (gobject_class, PROP_LABEL,
371                     g_param_spec_string ("label", "Label", "Item label",
372                                          NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
373
374         /**
375          * SecretItem:locked:
376          *
377          * Whether the item is locked or not. An item may not be independently
378          * lockable separate from other items in its collection.
379          *
380          * To lock or unlock a item use the secret_service_lock() or
381          * secret_service_unlock() functions.
382          */
383         g_object_class_install_property (gobject_class, PROP_LOCKED,
384                    g_param_spec_boolean ("locked", "Locked", "Item locked",
385                                          TRUE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
386
387         /**
388          * SecretItem:created:
389          *
390          * The date and time (in seconds since the UNIX epoch) that this
391          * item was created.
392          */
393         g_object_class_install_property (gobject_class, PROP_CREATED,
394                     g_param_spec_uint64 ("created", "Created", "Item creation date",
395                                          0UL, G_MAXUINT64, 0UL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
396
397         /**
398          * SecretItem:modified:
399          *
400          * The date and time (in seconds since the UNIX epoch) that this
401          * item was last modified.
402          */
403         g_object_class_install_property (gobject_class, PROP_MODIFIED,
404                     g_param_spec_uint64 ("modified", "Modified", "Item modified date",
405                                          0UL, G_MAXUINT64, 0UL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
406
407         g_type_class_add_private (gobject_class, sizeof (SecretItemPrivate));
408 }
409
410 typedef struct {
411         GCancellable *cancellable;
412 } InitClosure;
413
414 static void
415 init_closure_free (gpointer data)
416 {
417         InitClosure *closure = data;
418         g_clear_object (&closure->cancellable);
419         g_slice_free (InitClosure, closure);
420 }
421
422 static gboolean
423 item_ensure_for_flags_sync (SecretItem *self,
424                             SecretItemFlags flags,
425                             GCancellable *cancellable,
426                             GError **error)
427 {
428         if (flags & SECRET_ITEM_LOAD_SECRET && !secret_item_get_locked (self)) {
429                 if (!secret_item_load_secret_sync (self, cancellable, error))
430                         return FALSE;
431         }
432
433         return TRUE;
434 }
435
436
437 static void
438 on_init_load_secret (GObject *source,
439                      GAsyncResult *result,
440                      gpointer user_data)
441 {
442         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
443         SecretItem *self = SECRET_ITEM (source);
444         GError *error = NULL;
445
446         if (!secret_item_load_secret_finish (self, result, &error))
447                 g_simple_async_result_take_error (async, error);
448
449         g_simple_async_result_complete (async);
450         g_object_unref (async);
451 }
452
453 static void
454 item_ensure_for_flags_async (SecretItem *self,
455                              SecretItemFlags flags,
456                              GSimpleAsyncResult *async)
457 {
458         InitClosure *init = g_simple_async_result_get_op_res_gpointer (async);
459
460         if (flags & SECRET_ITEM_LOAD_SECRET && !secret_item_get_locked (self))
461                 secret_item_load_secret (self, init->cancellable,
462                                          on_init_load_secret, g_object_ref (async));
463
464         else
465                 g_simple_async_result_complete (async);
466 }
467
468 static gboolean
469 secret_item_initable_init (GInitable *initable,
470                            GCancellable *cancellable,
471                            GError **error)
472 {
473         SecretItem *self;
474         SecretService *service;
475         GDBusProxy *proxy;
476
477         if (!secret_item_initable_parent_iface->init (initable, cancellable, error))
478                 return FALSE;
479
480         proxy = G_DBUS_PROXY (initable);
481
482         if (!_secret_util_have_cached_properties (proxy)) {
483                 g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD,
484                              "No such secret item at path: %s",
485                              g_dbus_proxy_get_object_path (proxy));
486                 return FALSE;
487         }
488
489         self = SECRET_ITEM (initable);
490         if (!self->pv->service) {
491                 service = secret_service_get_sync (SECRET_SERVICE_NONE, cancellable, error);
492                 if (service == NULL)
493                         return FALSE;
494                 else
495                         item_take_service (self, service);
496         }
497
498         return item_ensure_for_flags_sync (self, self->pv->init_flags, cancellable, error);
499 }
500
501 static void
502 secret_item_initable_iface (GInitableIface *iface)
503 {
504         secret_item_initable_parent_iface = g_type_interface_peek_parent (iface);
505
506         iface->init = secret_item_initable_init;
507 }
508
509 static void
510 on_init_service (GObject *source,
511                  GAsyncResult *result,
512                  gpointer user_data)
513 {
514         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
515         SecretItem *self = SECRET_ITEM (g_async_result_get_source_object (user_data));
516         SecretService *service;
517         GError *error = NULL;
518
519         service = secret_service_get_finish (result, &error);
520         if (error == NULL) {
521                 item_take_service (self, service);
522                 item_ensure_for_flags_async (self, self->pv->init_flags, async);
523
524         } else {
525                 g_simple_async_result_take_error (async, error);
526                 g_simple_async_result_complete (async);
527         }
528
529         g_object_unref (self);
530         g_object_unref (async);
531 }
532
533 static void
534 on_init_base (GObject *source,
535               GAsyncResult *result,
536               gpointer user_data)
537 {
538         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
539         InitClosure *init = g_simple_async_result_get_op_res_gpointer (res);
540         SecretItem *self = SECRET_ITEM (source);
541         GDBusProxy *proxy = G_DBUS_PROXY (self);
542         GError *error = NULL;
543
544         if (!secret_item_async_initable_parent_iface->init_finish (G_ASYNC_INITABLE (self),
545                                                                    result, &error)) {
546                 g_simple_async_result_take_error (res, error);
547                 g_simple_async_result_complete (res);
548
549         } else if (!_secret_util_have_cached_properties (proxy)) {
550                 g_simple_async_result_set_error (res, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD,
551                                                  "No such secret item at path: %s",
552                                                  g_dbus_proxy_get_object_path (proxy));
553                 g_simple_async_result_complete (res);
554
555         } else if (self->pv->service == NULL) {
556                 secret_service_get (SECRET_SERVICE_NONE, init->cancellable,
557                                     on_init_service, g_object_ref (res));
558
559         } else {
560                 item_ensure_for_flags_async (self, self->pv->init_flags, res);
561         }
562
563         g_object_unref (res);
564 }
565
566 static void
567 secret_item_async_initable_init_async (GAsyncInitable *initable,
568                                        int io_priority,
569                                        GCancellable *cancellable,
570                                        GAsyncReadyCallback callback,
571                                        gpointer user_data)
572 {
573         GSimpleAsyncResult *res;
574         InitClosure *init;
575
576         res = g_simple_async_result_new (G_OBJECT (initable), callback, user_data,
577                                          secret_item_async_initable_init_async);
578         init = g_slice_new0 (InitClosure);
579         init->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
580         g_simple_async_result_set_op_res_gpointer (res, init, init_closure_free);
581
582         secret_item_async_initable_parent_iface->init_async (initable, io_priority,
583                                                              cancellable,
584                                                              on_init_base,
585                                                              g_object_ref (res));
586
587         g_object_unref (res);
588 }
589
590 static gboolean
591 secret_item_async_initable_init_finish (GAsyncInitable *initable,
592                                         GAsyncResult *result,
593                                         GError **error)
594 {
595         g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (initable),
596                               secret_item_async_initable_init_async), FALSE);
597
598         if (_secret_util_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
599                 return FALSE;
600
601         return TRUE;
602 }
603
604 static void
605 secret_item_async_initable_iface (GAsyncInitableIface *iface)
606 {
607         secret_item_async_initable_parent_iface = g_type_interface_peek_parent (iface);
608
609         iface->init_async = secret_item_async_initable_init_async;
610         iface->init_finish = secret_item_async_initable_init_finish;
611 }
612
613 /**
614  * secret_item_refresh:
615  * @self: the collection
616  *
617  * Refresh the properties on this item. This fires off a request to
618  * refresh, and the properties will be updated later.
619  *
620  * Calling this method is not normally necessary, as the secret service
621  * will notify the client when properties change.
622  */
623 void
624 secret_item_refresh (SecretItem *self)
625 {
626         g_return_if_fail (SECRET_IS_ITEM (self));
627
628         _secret_util_get_properties (G_DBUS_PROXY (self),
629                                      secret_item_refresh,
630                                      NULL, NULL, NULL);
631 }
632
633 void
634 _secret_item_set_cached_secret (SecretItem *self,
635                                 SecretValue *value)
636 {
637         SecretValue *other = NULL;
638         gboolean updated = FALSE;
639
640         g_return_if_fail (SECRET_IS_ITEM (self));
641
642         if (value != NULL)
643                 secret_value_ref (value);
644
645         g_mutex_lock (&self->pv->mutex);
646
647         if (value != self->pv->value) {
648                 other = self->pv->value;
649                 self->pv->value = value;
650                 updated = TRUE;
651         } else {
652                 other = value;
653         }
654
655         g_mutex_unlock (&self->pv->mutex);
656
657         if (other != NULL)
658                 secret_value_unref (other);
659
660         if (updated)
661                 g_object_notify (G_OBJECT (self), "flags");
662 }
663
664 typedef struct {
665         GCancellable *cancellable;
666         SecretItem *item;
667         SecretValue *value;
668 } CreateClosure;
669
670 static void
671 create_closure_free (gpointer data)
672 {
673         CreateClosure *closure = data;
674         g_clear_object (&closure->cancellable);
675         g_clear_object (&closure->item);
676         secret_value_unref (closure->value);
677         g_slice_free (CreateClosure, closure);
678 }
679
680 static void
681 on_create_item (GObject *source,
682                 GAsyncResult *result,
683                 gpointer user_data)
684 {
685         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
686         CreateClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
687         GError *error = NULL;
688
689         closure->item = secret_item_new_for_dbus_path_finish (result, &error);
690         if (error != NULL)
691                 g_simple_async_result_take_error (res, error);
692
693         /* As a convenince mark down the SecretValue on the item */
694         _secret_item_set_cached_secret (closure->item, closure->value);
695
696         g_simple_async_result_complete (res);
697         g_object_unref (res);
698 }
699
700 static void
701 on_create_path (GObject *source,
702                 GAsyncResult *result,
703                 gpointer user_data)
704 {
705         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
706         CreateClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
707         SecretService *service = SECRET_SERVICE (source);
708         GError *error = NULL;
709         gchar *path;
710
711         path = secret_service_create_item_dbus_path_finish (service, result, &error);
712         if (error == NULL) {
713                 secret_item_new_for_dbus_path (service, path, SECRET_ITEM_NONE,
714                                                closure->cancellable, on_create_item,
715                                                g_object_ref (res));
716         } else {
717                 g_simple_async_result_take_error (res, error);
718                 g_simple_async_result_complete (res);
719         }
720
721         g_object_unref (res);
722 }
723
724 static GHashTable *
725 item_properties_new (const gchar *label,
726                      const SecretSchema *schema,
727                      GHashTable *attributes)
728 {
729         const gchar *schema_name = NULL;
730         GHashTable *properties;
731         GVariant *value;
732
733         if (schema != NULL)
734                 schema_name = schema->name;
735
736         properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
737                                             (GDestroyNotify)g_variant_unref);
738
739         value = g_variant_new_string (label);
740         g_hash_table_insert (properties,
741                              SECRET_ITEM_INTERFACE ".Label",
742                              g_variant_ref_sink (value));
743
744         value = _secret_attributes_to_variant (attributes, schema_name);
745         g_hash_table_insert (properties,
746                              SECRET_ITEM_INTERFACE ".Attributes",
747                              g_variant_ref_sink (value));
748
749         return properties;
750 }
751
752 /**
753  * secret_item_create:
754  * @collection: a secret collection to create this item in
755  * @schema: (allow-none): the schema for the attributes
756  * @attributes: (element-type utf8 utf8): attributes for the new item
757  * @label: label for the new item
758  * @value: secret value for the new item
759  * @flags: flags for the creation of the new item
760  * @cancellable: optional cancellation object
761  * @callback: called when the operation completes
762  * @user_data: data to pass to the callback
763  *
764  * Create a new item in the secret service.
765  *
766  * If the @flags contains %SECRET_ITEM_CREATE_REPLACE, then the secret
767  * service will search for an item matching the @attributes, and update that item
768  * instead of creating a new one.
769  *
770  * This method may block indefinitely and should not be used in user interface
771  * threads. The secret service may prompt the user. secret_service_prompt()
772  * will be used to handle any prompts that are required.
773  */
774 void
775 secret_item_create (SecretCollection *collection,
776                     const SecretSchema *schema,
777                     GHashTable *attributes,
778                     const gchar *label,
779                     SecretValue *value,
780                     SecretItemCreateFlags flags,
781                     GCancellable *cancellable,
782                     GAsyncReadyCallback callback,
783                     gpointer user_data)
784 {
785         SecretService *service = NULL;
786         const gchar *collection_path;
787         GSimpleAsyncResult *res;
788         CreateClosure *closure;
789         GHashTable *properties;
790
791         g_return_if_fail (SECRET_IS_COLLECTION (collection));
792         g_return_if_fail (label != NULL);
793         g_return_if_fail (attributes != NULL);
794         g_return_if_fail (value != NULL);
795         g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
796
797         /* Warnings raised already */
798         if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, FALSE))
799                 return;
800
801         res = g_simple_async_result_new (NULL, callback, user_data,
802                                          secret_item_create);
803         closure = g_slice_new0 (CreateClosure);
804         closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
805         closure->value = secret_value_ref (value);
806         g_simple_async_result_set_op_res_gpointer (res, closure, create_closure_free);
807
808         properties = item_properties_new (label, schema, attributes);
809         g_object_get (collection, "service", &service, NULL);
810
811         collection_path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (collection));
812
813         secret_service_create_item_dbus_path (service, collection_path, properties,
814                                               value, flags, cancellable,
815                                               on_create_path, g_object_ref (res));
816
817         g_hash_table_unref (properties);
818         g_object_unref (service);
819         g_object_unref (res);
820 }
821
822 /**
823  * secret_item_create_finish:
824  * @result: the asynchronous result passed to the callback
825  * @error: location to place an error on failure
826  *
827  * Finish operation to create a new item in the secret service.
828  *
829  * Returns: (transfer full): the new item, which should be unreferenced
830  *          with g_object_unref()
831  */
832 SecretItem *
833 secret_item_create_finish (GAsyncResult *result,
834                            GError **error)
835 {
836         GSimpleAsyncResult *res;
837         CreateClosure *closure;
838
839         g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL,
840                               secret_item_create), NULL);
841         g_return_val_if_fail (error == NULL || *error == NULL, NULL);
842
843         res = G_SIMPLE_ASYNC_RESULT (result);
844
845         if (_secret_util_propagate_error (res, error))
846                 return NULL;
847
848         closure = g_simple_async_result_get_op_res_gpointer (res);
849         if (closure->item == NULL)
850                 return NULL;
851
852         return g_object_ref (closure->item);
853 }
854
855 /**
856  * secret_item_create_sync:
857  * @collection: a secret collection to create this item in
858  * @schema: (allow-none): the schema for the attributes
859  * @attributes: (element-type utf8 utf8): attributes for the new item
860  * @label: label for the new item
861  * @value: secret value for the new item
862  * @flags: flags for the creation of the new item
863  * @cancellable: optional cancellation object
864  * @error: location to place an error on failure
865  *
866  * Create a new item in the secret service.
867  *
868  * If the @flags contains %SECRET_ITEM_CREATE_REPLACE, then the secret
869  * service will search for an item matching the @attributes, and update that item
870  * instead of creating a new one.
871  *
872  * This method may block indefinitely and should not be used in user interface
873  * threads. The secret service may prompt the user. secret_service_prompt()
874  * will be used to handle any prompts that are required.
875  *
876  * Returns: (transfer full): the new item, which should be unreferenced
877  *          with g_object_unref()
878  */
879 SecretItem *
880 secret_item_create_sync (SecretCollection *collection,
881                          const SecretSchema *schema,
882                          GHashTable *attributes,
883                          const gchar *label,
884                          SecretValue *value,
885                          SecretItemCreateFlags flags,
886                          GCancellable *cancellable,
887                          GError **error)
888 {
889         SecretService *service = NULL;
890         const gchar *collection_path;
891         SecretItem *item = NULL;
892         GHashTable *properties;
893         gchar *path;
894
895         g_return_val_if_fail (SECRET_IS_COLLECTION (collection), NULL);
896         g_return_val_if_fail (label != NULL, NULL);
897         g_return_val_if_fail (attributes != NULL, NULL);
898         g_return_val_if_fail (value != NULL, NULL);
899         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
900         g_return_val_if_fail (error == NULL || *error == NULL, NULL);
901
902         /* Warnings raised already */
903         if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, FALSE))
904                 return NULL;
905
906         properties = item_properties_new (label, schema, attributes);
907         g_object_get (collection, "service", &service, NULL);
908
909         collection_path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (collection));
910
911         path = secret_service_create_item_dbus_path_sync (service, collection_path, properties,
912                                                           value, flags, cancellable, error);
913
914         if (path != NULL) {
915                 item = secret_item_new_for_dbus_path_sync (service, path, SECRET_ITEM_NONE,
916                                                            cancellable, error);
917                 g_free (path);
918         }
919
920         g_hash_table_unref (properties);
921         g_object_unref (service);
922
923         return item;
924 }
925
926 static void
927 on_item_deleted (GObject *source,
928                  GAsyncResult *result,
929                  gpointer user_data)
930 {
931         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
932         SecretItem *self = SECRET_ITEM (g_async_result_get_source_object (user_data));
933         GError *error = NULL;
934
935         if (_secret_service_delete_path_finish (SECRET_SERVICE (source), result, &error))
936                 g_simple_async_result_set_op_res_gboolean (res, TRUE);
937
938         if (error != NULL)
939                 g_simple_async_result_take_error (res, error);
940
941         g_simple_async_result_complete (res);
942         g_object_unref (self);
943         g_object_unref (res);
944 }
945
946 /**
947  * secret_item_delete:
948  * @self: an item
949  * @cancellable: optional cancellation object
950  * @callback: called when the operation completes
951  * @user_data: data to pass to the callback
952  *
953  * Delete this item.
954  *
955  * This method returns immediately and completes asynchronously. The secret
956  * service may prompt the user. secret_service_prompt() will be used to handle
957  * any prompts that show up.
958  */
959 void
960 secret_item_delete (SecretItem *self,
961                     GCancellable *cancellable,
962                     GAsyncReadyCallback callback,
963                     gpointer user_data)
964 {
965         GSimpleAsyncResult *res;
966         const gchar *object_path;
967
968         g_return_if_fail (SECRET_IS_ITEM (self));
969         g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
970
971         object_path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (self));
972         res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
973                                          secret_item_delete);
974
975         _secret_service_delete_path (self->pv->service, object_path, TRUE,
976                                      cancellable, on_item_deleted, g_object_ref (res));
977
978         g_object_unref (res);
979 }
980
981 /**
982  * secret_item_delete_finish:
983  * @self: an item
984  * @result: asynchronous result passed to the callback
985  * @error: location to place an error on failure
986  *
987  * Complete asynchronous operation to delete the secret item.
988  *
989  * Returns: whether the item was successfully deleted or not
990  */
991 gboolean
992 secret_item_delete_finish (SecretItem *self,
993                            GAsyncResult *result,
994                            GError **error)
995 {
996         GSimpleAsyncResult *res;
997
998         g_return_val_if_fail (SECRET_IS_ITEM (self), FALSE);
999         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1000         g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
1001                               secret_item_delete), FALSE);
1002
1003         res = G_SIMPLE_ASYNC_RESULT (result);
1004
1005         if (_secret_util_propagate_error (res, error))
1006                 return FALSE;
1007
1008         return g_simple_async_result_get_op_res_gboolean (res);
1009 }
1010
1011 /**
1012  * secret_item_delete_sync:
1013  * @self: an item
1014  * @cancellable: optional cancellation object
1015  * @error: location to place an error on failure
1016  *
1017  * Delete this secret item.
1018  *
1019  * This method may block indefinitely and should not be used in user
1020  * interface threads. The secret service may prompt the user.
1021  * secret_service_prompt() will be used to handle any prompts that show up.
1022  *
1023  * Returns: whether the item was successfully deleted or not
1024  */
1025 gboolean
1026 secret_item_delete_sync (SecretItem *self,
1027                          GCancellable *cancellable,
1028                          GError **error)
1029 {
1030         SecretSync *sync;
1031         gboolean ret;
1032
1033         g_return_val_if_fail (SECRET_IS_ITEM (self), FALSE);
1034         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
1035         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1036
1037         sync = _secret_sync_new ();
1038         g_main_context_push_thread_default (sync->context);
1039
1040         secret_item_delete (self, cancellable, _secret_sync_on_result, sync);
1041
1042         g_main_loop_run (sync->loop);
1043
1044         ret = secret_item_delete_finish (self, sync->result, error);
1045
1046         g_main_context_pop_thread_default (sync->context);
1047         _secret_sync_free (sync);
1048
1049         return ret;
1050 }
1051
1052 /**
1053  * secret_item_get_flags:
1054  * @self: the secret item proxy
1055  *
1056  * Get the flags representing what features of the #SecretItem proxy
1057  * have been initialized.
1058  *
1059  * Use secret_item_load_secret() to initialize further features
1060  * and change the flags.
1061  *
1062  * Returns: the flags for features initialized
1063  */
1064 SecretItemFlags
1065 secret_item_get_flags (SecretItem *self)
1066 {
1067         SecretServiceFlags flags = 0;
1068
1069         g_return_val_if_fail (SECRET_IS_ITEM (self), SECRET_ITEM_NONE);
1070
1071         g_mutex_lock (&self->pv->mutex);
1072
1073         if (self->pv->value)
1074                 flags |= SECRET_ITEM_LOAD_SECRET;
1075
1076         g_mutex_unlock (&self->pv->mutex);
1077
1078         return flags;
1079
1080 }
1081
1082 /**
1083  * secret_item_get_service:
1084  * @self: an item
1085  *
1086  * Get the Secret Service object that this item was created with.
1087  *
1088  * Returns: (transfer none): the Secret Service object
1089  */
1090 SecretService *
1091 secret_item_get_service (SecretItem *self)
1092 {
1093         g_return_val_if_fail (SECRET_IS_ITEM (self), NULL);
1094         return self->pv->service;
1095 }
1096
1097
1098 /**
1099  * secret_item_get_secret:
1100  * @self: an item
1101  *
1102  * Get the secret value of this item. If this item is locked or the secret
1103  * has not yet been loaded then this will return %NULL.
1104  *
1105  * To load the secret call the secret_item_load_secret() method.
1106  *
1107  * Returns: (transfer full) (allow-none): the secret value which should be
1108  *          released with secret_value_unref(), or %NULL
1109  */
1110 SecretValue *
1111 secret_item_get_secret (SecretItem *self)
1112 {
1113         SecretValue *value = NULL;
1114
1115         g_return_val_if_fail (SECRET_IS_ITEM (self), NULL);
1116
1117         g_mutex_lock (&self->pv->mutex);
1118
1119         if (self->pv->value)
1120                 value = secret_value_ref (self->pv->value);
1121
1122         g_mutex_unlock (&self->pv->mutex);
1123
1124         return value;
1125 }
1126
1127
1128 typedef struct {
1129         GCancellable *cancellable;
1130 } LoadClosure;
1131
1132 static void
1133 load_closure_free (gpointer data)
1134 {
1135         LoadClosure *closure = data;
1136         g_clear_object (&closure->cancellable);
1137         g_slice_free (LoadClosure, closure);
1138 }
1139
1140 static void
1141 on_item_load_secret (GObject *source,
1142                      GAsyncResult *result,
1143                      gpointer user_data)
1144 {
1145         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
1146         SecretItem *self = SECRET_ITEM (g_async_result_get_source_object (user_data));
1147         SecretSession *session;
1148         GError *error = NULL;
1149         SecretValue *value;
1150         GVariant *retval;
1151         GVariant *child;
1152
1153         retval = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), result, &error);
1154         if (error == NULL) {
1155                 child = g_variant_get_child_value (retval, 0);
1156                 g_variant_unref (retval);
1157
1158                 session = _secret_service_get_session (self->pv->service);
1159                 value = _secret_session_decode_secret (session, child);
1160                 g_variant_unref (child);
1161
1162                 if (value == NULL) {
1163                         g_set_error (&error, SECRET_ERROR, SECRET_ERROR_PROTOCOL,
1164                                      _("Received invalid secret from the secret storage"));
1165                 } else {
1166                         _secret_item_set_cached_secret (self, value);
1167                         secret_value_unref (value);
1168                 }
1169         }
1170
1171         if (error != NULL) {
1172                 g_simple_async_result_take_error (res, error);
1173         }
1174
1175         g_simple_async_result_complete (res);
1176         g_object_unref (self);
1177         g_object_unref (res);
1178 }
1179
1180 static void
1181 on_load_ensure_session (GObject *source,
1182                         GAsyncResult *result,
1183                         gpointer user_data)
1184 {
1185         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
1186         SecretItem *self = SECRET_ITEM (g_async_result_get_source_object (user_data));
1187         LoadClosure *load = g_simple_async_result_get_op_res_gpointer (res);
1188         const gchar *session_path;
1189         GError *error = NULL;
1190
1191         secret_service_ensure_session_finish (self->pv->service, result, &error);
1192         if (error != NULL) {
1193                 g_simple_async_result_take_error (res, error);
1194                 g_simple_async_result_complete (res);
1195
1196         } else {
1197                 session_path = secret_service_get_session_dbus_path (self->pv->service);
1198                 g_assert (session_path != NULL && session_path[0] != '\0');
1199                 g_dbus_proxy_call (G_DBUS_PROXY (self), "GetSecret",
1200                                    g_variant_new ("(o)", session_path),
1201                                    G_DBUS_CALL_FLAGS_NONE, -1, load->cancellable,
1202                                    on_item_load_secret, g_object_ref (res));
1203         }
1204
1205         g_object_unref (self);
1206         g_object_unref (res);
1207 }
1208
1209 /**
1210  * secret_item_load_secret:
1211  * @self: an item proxy
1212  * @cancellable: optional cancellation object
1213  * @callback: called when the operation completes
1214  * @user_data: data to pass to the callback
1215  *
1216  * Load the secret value of this item.
1217  *
1218  * Each item has a single secret which might be a password or some
1219  * other secret binary value.
1220  *
1221  * This function will fail if the secret item is locked.
1222  *
1223  * This function returns immediately and completes asynchronously.
1224  */
1225 void
1226 secret_item_load_secret (SecretItem *self,
1227                          GCancellable *cancellable,
1228                          GAsyncReadyCallback callback,
1229                          gpointer user_data)
1230 {
1231         GSimpleAsyncResult *res;
1232         LoadClosure *closure;
1233
1234         g_return_if_fail (SECRET_IS_ITEM (self));
1235         g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1236
1237         res = g_simple_async_result_new (G_OBJECT (self), callback,
1238                                          user_data, secret_item_load_secret);
1239         closure = g_slice_new0 (LoadClosure);
1240         closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
1241         g_simple_async_result_set_op_res_gpointer (res, closure, load_closure_free);
1242
1243         secret_service_ensure_session (self->pv->service, cancellable,
1244                                        on_load_ensure_session,
1245                                        g_object_ref (res));
1246
1247         g_object_unref (res);
1248 }
1249
1250 /**
1251  * secret_item_load_secret_finish:
1252  * @self: an item proxy
1253  * @result: asynchronous result passed to callback
1254  * @error: location to place error on failure
1255  *
1256  * Complete asynchronous operation to load the secret value of this item.
1257  *
1258  * The newly loaded secret value can be accessed by calling
1259  * secret_item_get_secret().
1260  *
1261  * Returns: whether the secret item succesfully loaded or not
1262  */
1263 gboolean
1264 secret_item_load_secret_finish (SecretItem *self,
1265                                 GAsyncResult *result,
1266                                 GError **error)
1267 {
1268         GSimpleAsyncResult *res;
1269
1270         g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
1271                               secret_item_load_secret), FALSE);
1272
1273         res = G_SIMPLE_ASYNC_RESULT (result);
1274         if (_secret_util_propagate_error (res, error))
1275                 return FALSE;
1276
1277         return TRUE;
1278 }
1279
1280 /**
1281  * secret_item_load_secret_sync:
1282  * @self: an item
1283  * @cancellable: optional cancellation object
1284  * @error: location to place error on failure
1285  *
1286  * Load the secret value of this item.
1287  *
1288  * Each item has a single secret which might be a password or some
1289  * other secret binary value.
1290  *
1291  * This function may block indefinetely. Use the asynchronous version
1292  * in user interface threads.
1293  *
1294  * Returns: whether the secret item succesfully loaded or not
1295  */
1296 gboolean
1297 secret_item_load_secret_sync (SecretItem *self,
1298                               GCancellable *cancellable,
1299                               GError **error)
1300 {
1301         SecretSync *sync;
1302         gboolean result;
1303
1304         g_return_val_if_fail (SECRET_IS_ITEM (self), FALSE);
1305         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
1306         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1307
1308         sync = _secret_sync_new ();
1309         g_main_context_push_thread_default (sync->context);
1310
1311         secret_item_load_secret (self, cancellable, _secret_sync_on_result, sync);
1312
1313         g_main_loop_run (sync->loop);
1314
1315         result = secret_item_load_secret_finish (self, sync->result, error);
1316
1317         g_main_context_pop_thread_default (sync->context);
1318         _secret_sync_free (sync);
1319
1320         return result;
1321 }
1322
1323 typedef struct {
1324         SecretService *service;
1325         GCancellable *cancellable;
1326         GVariant *in;
1327         GHashTable *items;
1328 } LoadsClosure;
1329
1330 static void
1331 loads_closure_free (gpointer data)
1332 {
1333         LoadsClosure *loads = data;
1334         if (loads->in)
1335                 g_variant_unref (loads->in);
1336         if (loads->service)
1337                 g_object_unref (loads->service);
1338         g_clear_object (&loads->cancellable);
1339         g_hash_table_destroy (loads->items);
1340         g_slice_free (LoadsClosure, loads);
1341 }
1342
1343 static void
1344 on_get_secrets_complete (GObject *source,
1345                          GAsyncResult *result,
1346                          gpointer user_data)
1347 {
1348         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
1349         LoadsClosure *loads = g_simple_async_result_get_op_res_gpointer (async);
1350         GHashTable *with_paths;
1351         GError *error = NULL;
1352         GHashTableIter iter;
1353         const gchar *path;
1354         SecretValue *value;
1355         SecretItem *item;
1356         GVariant *retval;
1357
1358         retval = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), result, &error);
1359         if (retval != NULL) {
1360                 with_paths = _secret_service_decode_get_secrets_all (loads->service, retval);
1361                 g_return_if_fail (with_paths != NULL);
1362
1363                 g_hash_table_iter_init (&iter, with_paths);
1364                 while (g_hash_table_iter_next (&iter, (gpointer *)&path, (gpointer *)&value)) {
1365                         item = g_hash_table_lookup (loads->items, path);
1366                         if (item != NULL)
1367                                 _secret_item_set_cached_secret (item, value);
1368                 }
1369
1370                 g_hash_table_unref (with_paths);
1371                 g_variant_unref (retval);
1372         }
1373
1374         if (error != NULL)
1375                 g_simple_async_result_take_error (async, error);
1376
1377         g_simple_async_result_complete (async);
1378         g_object_unref (async);
1379 }
1380
1381 static void
1382 on_loads_secrets_session (GObject *source,
1383                           GAsyncResult *result,
1384                           gpointer user_data)
1385 {
1386         GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
1387         LoadsClosure *loads = g_simple_async_result_get_op_res_gpointer (async);
1388         GError *error = NULL;
1389         const gchar *session;
1390
1391         secret_service_ensure_session_finish (SECRET_SERVICE (source), result, &error);
1392         if (error != NULL) {
1393                 g_simple_async_result_take_error (async, error);
1394                 g_simple_async_result_complete (async);
1395
1396         } else {
1397                 session = secret_service_get_session_dbus_path (SECRET_SERVICE (source));
1398                 g_dbus_proxy_call (G_DBUS_PROXY (source), "GetSecrets",
1399                                    g_variant_new ("(@aoo)", loads->in, session),
1400                                    G_DBUS_CALL_FLAGS_NO_AUTO_START, -1,
1401                                    loads->cancellable, on_get_secrets_complete,
1402                                    g_object_ref (async));
1403         }
1404
1405         g_object_unref (async);
1406 }
1407
1408 /**
1409  * secret_item_load_secrets:
1410  * @items: (element-type Secret.Item): the items to retrieve secrets for
1411  * @cancellable: optional cancellation object
1412  * @callback: called when the operation completes
1413  * @user_data: data to pass to the callback
1414  *
1415  * Load the secret values for an secret items stored in the service.
1416  *
1417  * The @items must all have the same SecretItem::service property.
1418  *
1419  * This function returns immediately and completes asynchronously.
1420  */
1421 void
1422 secret_item_load_secrets (GList *items,
1423                           GCancellable *cancellable,
1424                           GAsyncReadyCallback callback,
1425                           gpointer user_data)
1426 {
1427         GSimpleAsyncResult *async;
1428         LoadsClosure *loads;
1429         GPtrArray *paths;
1430         const gchar *path;
1431         GList *l;
1432
1433         g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1434
1435         for (l = items; l != NULL; l = g_list_next (l))
1436                 g_return_if_fail (SECRET_IS_ITEM (l->data));
1437
1438         async = g_simple_async_result_new (NULL, callback, user_data,
1439                                            secret_item_load_secrets);
1440         loads = g_slice_new0 (LoadsClosure);
1441         loads->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
1442         loads->items = g_hash_table_new_full (g_str_hash, g_str_equal,
1443                                               g_free, g_object_unref);
1444
1445         paths = g_ptr_array_new ();
1446         for (l = items; l != NULL; l = g_list_next (l)) {
1447                 if (secret_item_get_locked (l->data))
1448                         continue;
1449
1450                 if (loads->service == NULL) {
1451                         loads->service = secret_item_get_service (l->data);
1452                         if (loads->service)
1453                                 g_object_ref (loads->service);
1454                 }
1455
1456                 path = g_dbus_proxy_get_object_path (l->data);
1457                 g_hash_table_insert (loads->items, g_strdup (path), g_object_ref (l->data));
1458                 g_ptr_array_add (paths, (gpointer)path);
1459         }
1460
1461         loads->in = g_variant_new_objv ((const gchar * const *)paths->pdata, paths->len);
1462         g_variant_ref_sink (loads->in);
1463
1464         g_ptr_array_free (paths, TRUE);
1465         g_simple_async_result_set_op_res_gpointer (async, loads, loads_closure_free);
1466
1467         if (loads->service) {
1468                 secret_service_ensure_session (loads->service, cancellable,
1469                                                on_loads_secrets_session,
1470                                                g_object_ref (async));
1471         } else {
1472                 g_simple_async_result_complete_in_idle (async);
1473         }
1474
1475         g_object_unref (async);
1476 }
1477
1478 /**
1479  * secret_item_load_secrets_finish:
1480  * @result: asynchronous result passed to callback
1481  * @error: location to place an error on failure
1482  *
1483  * Complete asynchronous operation to load the secret values for
1484  * secret items stored in the service.
1485  *
1486  * Items that are locked will not have their secrets loaded.
1487  *
1488  * Returns: whether the operation succeded or not
1489  */
1490 gboolean
1491 secret_item_load_secrets_finish (GAsyncResult *result,
1492                                  GError **error)
1493 {
1494         GSimpleAsyncResult *async;
1495
1496         g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL,
1497                               secret_item_load_secrets), FALSE);
1498         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1499
1500         async = G_SIMPLE_ASYNC_RESULT (result);
1501         if (_secret_util_propagate_error (async, error))
1502                 return FALSE;
1503
1504         return TRUE;
1505 }
1506
1507 /**
1508  * secret_item_load_secrets_sync:
1509  * @items: (element-type Secret.Item): the items to retrieve secrets for
1510  * @cancellable: optional cancellation object
1511  * @error: location to place an error on failure
1512  *
1513  * Load the secret values for an secret items stored in the service.
1514  *
1515  * The @items must all have the same SecretItem::service property.
1516  *
1517  * This method may block indefinitely and should not be used in user interface
1518  * threads.
1519  *
1520  * Items that are locked will not have their secrets loaded.
1521  *
1522  * Returns: whether the operation succeded or not
1523  */
1524 gboolean
1525 secret_item_load_secrets_sync (GList *items,
1526                                GCancellable *cancellable,
1527                                GError **error)
1528 {
1529         SecretSync *sync;
1530         gboolean ret;
1531         GList *l;
1532
1533         for (l = items; l != NULL; l = g_list_next (l))
1534                 g_return_val_if_fail (SECRET_IS_ITEM (l->data), FALSE);
1535
1536         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
1537         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1538
1539         sync = _secret_sync_new ();
1540         g_main_context_push_thread_default (sync->context);
1541
1542         secret_item_load_secrets (items, cancellable,
1543                                   _secret_sync_on_result, sync);
1544
1545         g_main_loop_run (sync->loop);
1546
1547         ret = secret_item_load_secrets_finish (sync->result, error);
1548
1549         g_main_context_pop_thread_default (sync->context);
1550         _secret_sync_free (sync);
1551
1552         return ret;
1553 }
1554
1555 typedef struct {
1556         GCancellable *cancellable;
1557         SecretValue *value;
1558 } SetClosure;
1559
1560 static void
1561 set_closure_free (gpointer data)
1562 {
1563         SetClosure *set = data;
1564         g_clear_object (&set->cancellable);
1565         secret_value_unref (set->value);
1566         g_slice_free (SetClosure, set);
1567 }
1568
1569 static void
1570 on_item_set_secret (GObject *source,
1571                     GAsyncResult *result,
1572                     gpointer user_data)
1573 {
1574         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
1575         SecretItem *self = SECRET_ITEM (g_async_result_get_source_object (user_data));
1576         SetClosure *set = g_simple_async_result_get_op_res_gpointer (res);
1577         GError *error = NULL;
1578         GVariant *retval;
1579
1580         retval = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), result, &error);
1581
1582         if (error == NULL) {
1583                 _secret_item_set_cached_secret (self, set->value);
1584         } else {
1585                 g_simple_async_result_take_error (res, error);
1586         }
1587         if (retval != NULL)
1588                 g_variant_unref (retval);
1589
1590         g_simple_async_result_complete (res);
1591         g_object_unref (self);
1592         g_object_unref (res);
1593 }
1594
1595 static void
1596 on_set_ensure_session (GObject *source,
1597                        GAsyncResult *result,
1598                        gpointer user_data)
1599 {
1600         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
1601         SecretItem *self = SECRET_ITEM (g_async_result_get_source_object (user_data));
1602         SetClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
1603         SecretSession *session;
1604         GVariant *encoded;
1605         GError *error = NULL;
1606
1607         secret_service_ensure_session_finish (self->pv->service, result, &error);
1608         if (error != NULL) {
1609                 g_simple_async_result_take_error (res, error);
1610                 g_simple_async_result_complete (res);
1611
1612         } else {
1613                 session = _secret_service_get_session (self->pv->service);
1614                 encoded = _secret_session_encode_secret (session, closure->value);
1615                 g_dbus_proxy_call (G_DBUS_PROXY (self), "SetSecret",
1616                                    g_variant_new ("(@(oayays))", encoded),
1617                                    G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, closure->cancellable,
1618                                    on_item_set_secret, g_object_ref (res));
1619         }
1620
1621         g_object_unref (self);
1622         g_object_unref (res);
1623 }
1624
1625 /**
1626  * secret_item_set_secret:
1627  * @self: an item
1628  * @value: a new secret value
1629  * @cancellable: optional cancellation object
1630  * @callback: called when the operation completes
1631  * @user_data: data to pass to the callback
1632  *
1633  * Set the secret value of this item.
1634  *
1635  * Each item has a single secret which might be a password or some
1636  * other secret binary value.
1637  *
1638  * This function returns immediately and completes asynchronously.
1639  */
1640 void
1641 secret_item_set_secret (SecretItem *self,
1642                         SecretValue *value,
1643                         GCancellable *cancellable,
1644                         GAsyncReadyCallback callback,
1645                         gpointer user_data)
1646 {
1647         GSimpleAsyncResult *res;
1648         SetClosure *closure;
1649
1650         g_return_if_fail (SECRET_IS_ITEM (self));
1651         g_return_if_fail (value != NULL);
1652         g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1653
1654         res = g_simple_async_result_new (G_OBJECT (self), callback,
1655                                          user_data, secret_item_set_secret);
1656         closure = g_slice_new0 (SetClosure);
1657         closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
1658         closure->value = secret_value_ref (value);
1659         g_simple_async_result_set_op_res_gpointer (res, closure, set_closure_free);
1660
1661         secret_service_ensure_session (self->pv->service, cancellable,
1662                                        on_set_ensure_session,
1663                                        g_object_ref (res));
1664
1665         g_object_unref (res);
1666 }
1667
1668 /**
1669  * secret_item_set_secret_finish:
1670  * @self: an item
1671  * @result: asynchronous result passed to callback
1672  * @error: location to place error on failure
1673  *
1674  * Complete asynchronous operation to set the secret value of this item.
1675  *
1676  * Returns: whether the change was successful or not
1677  */
1678 gboolean
1679 secret_item_set_secret_finish (SecretItem *self,
1680                                GAsyncResult *result,
1681                                GError **error)
1682 {
1683         GSimpleAsyncResult *res;
1684
1685         g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
1686                               secret_item_set_secret), FALSE);
1687
1688         res = G_SIMPLE_ASYNC_RESULT (result);
1689         if (_secret_util_propagate_error (res, error))
1690                 return FALSE;
1691
1692         return TRUE;
1693 }
1694
1695 /**
1696  * secret_item_set_secret_sync:
1697  * @self: an item
1698  * @value: a new secret value
1699  * @cancellable: optional cancellation object
1700  * @error: location to place error on failure
1701  *
1702  * Set the secret value of this item.
1703  *
1704  * Each item has a single secret which might be a password or some
1705  * other secret binary value.
1706  *
1707  * This function may block indefinetely. Use the asynchronous version
1708  * in user interface threads.
1709  *
1710  * Returns: whether the change was successful or not
1711  */
1712 gboolean
1713 secret_item_set_secret_sync (SecretItem *self,
1714                              SecretValue *value,
1715                              GCancellable *cancellable,
1716                              GError **error)
1717 {
1718         SecretSync *sync;
1719         gboolean ret;
1720
1721         g_return_val_if_fail (SECRET_IS_ITEM (self), FALSE);
1722         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
1723         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1724
1725         sync = _secret_sync_new ();
1726         g_main_context_push_thread_default (sync->context);
1727
1728         secret_item_set_secret (self, value, cancellable, _secret_sync_on_result, sync);
1729
1730         g_main_loop_run (sync->loop);
1731
1732         ret = secret_item_set_secret_finish (self, sync->result, error);
1733
1734         g_main_context_pop_thread_default (sync->context);
1735         _secret_sync_free (sync);
1736
1737         return ret;
1738 }
1739
1740 /**
1741  * secret_item_get_schema_name:
1742  * @self: an item
1743  *
1744  * Gets the name of the schema that this item was stored with. This is also
1745  * available at the <literal>xdg:schema</literal> attribute.
1746  *
1747  * Returns: (transfer full): the schema name
1748  */
1749 gchar *
1750 secret_item_get_schema_name (SecretItem *self)
1751 {
1752         gchar *schema_name;
1753         GVariant *variant;
1754
1755         g_return_val_if_fail (SECRET_IS_ITEM (self), NULL);
1756
1757         variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Attributes");
1758         g_return_val_if_fail (variant != NULL, NULL);
1759
1760         g_variant_lookup (variant, "xdg:schema", "s", &schema_name);
1761         g_variant_unref (variant);
1762
1763         return schema_name;
1764 }
1765
1766 /**
1767  * secret_item_get_attributes:
1768  * @self: an item
1769  *
1770  * Set the attributes of this item.
1771  *
1772  * The @attributes are a mapping of string keys to string values.
1773  * Attributes are used to search for items. Attributes are not stored
1774  * or transferred securely by the secret service.
1775  *
1776  * Do not modify the attributes returned by this method. Use
1777  * secret_item_set_attributes() instead.
1778  *
1779  * Returns: (transfer full) (element-type utf8 utf8): a new reference
1780  *          to the attributes, which should not be modified, and
1781  *          released with g_hash_table_unref()
1782  */
1783 GHashTable *
1784 secret_item_get_attributes (SecretItem *self)
1785 {
1786         GHashTable *attributes;
1787         GVariant *variant;
1788
1789         g_return_val_if_fail (SECRET_IS_ITEM (self), NULL);
1790
1791         variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Attributes");
1792         g_return_val_if_fail (variant != NULL, NULL);
1793
1794         attributes = _secret_attributes_for_variant (variant);
1795         g_variant_unref (variant);
1796
1797         return attributes;
1798 }
1799
1800 /**
1801  * secret_item_set_attributes:
1802  * @self: an item
1803  * @schema: (allow-none): the schema for the attributes
1804  * @attributes: (element-type utf8 utf8): a new set of attributes
1805  * @cancellable: optional cancellation object
1806  * @callback: called when the asynchronous operation completes
1807  * @user_data: data to pass to the callback
1808  *
1809  * Set the attributes of this item.
1810  *
1811  * The @attributes are a mapping of string keys to string values.
1812  * Attributes are used to search for items. Attributes are not stored
1813  * or transferred securely by the secret service.
1814  *
1815  * This function returns immediately and completes asynchronously.
1816  */
1817 void
1818 secret_item_set_attributes (SecretItem *self,
1819                             const SecretSchema *schema,
1820                             GHashTable *attributes,
1821                             GCancellable *cancellable,
1822                             GAsyncReadyCallback callback,
1823                             gpointer user_data)
1824 {
1825         const gchar *schema_name = NULL;
1826
1827         g_return_if_fail (SECRET_IS_ITEM (self));
1828         g_return_if_fail (attributes != NULL);
1829
1830         if (schema != NULL) {
1831                 if (!_secret_attributes_validate (schema, attributes, G_STRFUNC, FALSE))
1832                         return; /* Warnings raised already */
1833                 schema_name = schema->name;
1834         }
1835
1836         _secret_util_set_property (G_DBUS_PROXY (self), "Attributes",
1837                                    _secret_attributes_to_variant (attributes, schema_name),
1838                                    secret_item_set_attributes, cancellable,
1839                                    callback, user_data);
1840 }
1841
1842 /**
1843  * secret_item_set_attributes_finish:
1844  * @self: an item
1845  * @result: asynchronous result passed to the callback
1846  * @error: location to place error on failure
1847  *
1848  * Complete operation to set the attributes of this item.
1849  *
1850  * Returns: whether the change was successful or not
1851  */
1852 gboolean
1853 secret_item_set_attributes_finish (SecretItem *self,
1854                                    GAsyncResult *result,
1855                                    GError **error)
1856 {
1857         g_return_val_if_fail (SECRET_IS_ITEM (self), FALSE);
1858
1859         return _secret_util_set_property_finish (G_DBUS_PROXY (self),
1860                                                  secret_item_set_attributes,
1861                                                  result, error);
1862 }
1863
1864 /**
1865  * secret_item_set_attributes_sync:
1866  * @self: an item
1867  * @schema: (allow-none): the schema for the attributes
1868  * @attributes: (element-type utf8 utf8): a new set of attributes
1869  * @cancellable: optional cancellation object
1870  * @error: location to place error on failure
1871  *
1872  * Set the attributes of this item.
1873  *
1874  * The @attributes are a mapping of string keys to string values.
1875  * Attributes are used to search for items. Attributes are not stored
1876  * or transferred securely by the secret service.
1877  *
1878  * This function may block indefinetely. Use the asynchronous version
1879  * in user interface threads.
1880  *
1881  * Returns: whether the change was successful or not
1882  */
1883 gboolean
1884 secret_item_set_attributes_sync (SecretItem *self,
1885                                  const SecretSchema *schema,
1886                                  GHashTable *attributes,
1887                                  GCancellable *cancellable,
1888                                  GError **error)
1889 {
1890         const gchar *schema_name = NULL;
1891
1892         g_return_val_if_fail (SECRET_IS_ITEM (self), FALSE);
1893         g_return_val_if_fail (attributes != NULL, FALSE);
1894
1895         if (schema != NULL) {
1896                 if (!_secret_attributes_validate (schema, attributes, G_STRFUNC, FALSE))
1897                         return FALSE; /* Warnings raised already */
1898                 schema_name = schema->name;
1899         }
1900
1901         return _secret_util_set_property_sync (G_DBUS_PROXY (self), "Attributes",
1902                                                _secret_attributes_to_variant (attributes, schema_name),
1903                                                cancellable, error);
1904 }
1905
1906 /**
1907  * secret_item_get_label:
1908  * @self: an item
1909  *
1910  * Get the label of this item.
1911  *
1912  * Returns: (transfer full): the label, which should be freed with g_free()
1913  */
1914 gchar *
1915 secret_item_get_label (SecretItem *self)
1916 {
1917         GVariant *variant;
1918         gchar *label;
1919
1920         g_return_val_if_fail (SECRET_IS_ITEM (self), NULL);
1921
1922         variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Label");
1923         g_return_val_if_fail (variant != NULL, NULL);
1924
1925         label = g_variant_dup_string (variant, NULL);
1926         g_variant_unref (variant);
1927
1928         return label;
1929 }
1930
1931 /**
1932  * secret_item_set_label:
1933  * @self: an item
1934  * @label: a new label
1935  * @cancellable: optional cancellation object
1936  * @callback: called when the operation completes
1937  * @user_data: data to pass to the callback
1938  *
1939  * Set the label of this item.
1940  *
1941  * This function returns immediately and completes asynchronously.
1942  */
1943 void
1944 secret_item_set_label (SecretItem *self,
1945                        const gchar *label,
1946                        GCancellable *cancellable,
1947                        GAsyncReadyCallback callback,
1948                        gpointer user_data)
1949 {
1950         g_return_if_fail (SECRET_IS_ITEM (self));
1951         g_return_if_fail (label != NULL);
1952
1953         _secret_util_set_property (G_DBUS_PROXY (self), "Label",
1954                                    g_variant_new_string (label),
1955                                    secret_item_set_label,
1956                                    cancellable, callback, user_data);
1957 }
1958
1959 /**
1960  * secret_item_set_label_finish:
1961  * @self: an item
1962  * @result: asynchronous result passed to callback
1963  * @error: location to place error on failure
1964  *
1965  * Complete asynchronous operation to set the label of this collection.
1966  *
1967  * Returns: whether the change was successful or not
1968  */
1969 gboolean
1970 secret_item_set_label_finish (SecretItem *self,
1971                               GAsyncResult *result,
1972                               GError **error)
1973 {
1974         g_return_val_if_fail (SECRET_IS_ITEM (self), FALSE);
1975
1976         return _secret_util_set_property_finish (G_DBUS_PROXY (self),
1977                                                  secret_item_set_label,
1978                                                  result, error);
1979 }
1980
1981 /**
1982  * secret_item_set_label_sync:
1983  * @self: an item
1984  * @label: a new label
1985  * @cancellable: optional cancellation object
1986  * @error: location to place error on failure
1987  *
1988  * Set the label of this item.
1989  *
1990  * This function may block indefinetely. Use the asynchronous version
1991  * in user interface threads.
1992  *
1993  * Returns: whether the change was successful or not
1994  */
1995 gboolean
1996 secret_item_set_label_sync (SecretItem *self,
1997                             const gchar *label,
1998                             GCancellable *cancellable,
1999                             GError **error)
2000 {
2001         g_return_val_if_fail (SECRET_IS_ITEM (self), FALSE);
2002         g_return_val_if_fail (label != NULL, FALSE);
2003
2004         return _secret_util_set_property_sync (G_DBUS_PROXY (self), "Label",
2005                                                g_variant_new_string (label),
2006                                                cancellable, error);
2007 }
2008
2009 /**
2010  * secret_item_get_locked:
2011  * @self: an item
2012  *
2013  * Get whether the item is locked or not.
2014  *
2015  * Depending on the secret service an item may not be able to be locked
2016  * independently from the collection that it is in.
2017  *
2018  * Returns: whether the item is locked or not
2019  */
2020 gboolean
2021 secret_item_get_locked (SecretItem *self)
2022 {
2023         GVariant *variant;
2024         gboolean locked;
2025
2026         g_return_val_if_fail (SECRET_IS_ITEM (self), TRUE);
2027
2028         variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Locked");
2029         g_return_val_if_fail (variant != NULL, TRUE);
2030
2031         locked = g_variant_get_boolean (variant);
2032         g_variant_unref (variant);
2033
2034         return locked;
2035 }
2036
2037 /**
2038  * secret_item_get_created:
2039  * @self: an item
2040  *
2041  * Get the created date and time of the item. The return value is
2042  * the number of seconds since the unix epoch, January 1st 1970.
2043  *
2044  * Returns: the created date and time
2045  */
2046 guint64
2047 secret_item_get_created (SecretItem *self)
2048 {
2049         GVariant *variant;
2050         guint64 created;
2051
2052         g_return_val_if_fail (SECRET_IS_ITEM (self), TRUE);
2053
2054         variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Created");
2055         g_return_val_if_fail (variant != NULL, 0);
2056
2057         created = g_variant_get_uint64 (variant);
2058         g_variant_unref (variant);
2059
2060         return created;
2061 }
2062
2063 /**
2064  * secret_item_get_modified:
2065  * @self: an item
2066  *
2067  * Get the modified date and time of the item. The return value is
2068  * the number of seconds since the unix epoch, January 1st 1970.
2069  *
2070  * Returns: the modified date and time
2071  */
2072 guint64
2073 secret_item_get_modified (SecretItem *self)
2074 {
2075         GVariant *variant;
2076         guint64 modified;
2077
2078         g_return_val_if_fail (SECRET_IS_ITEM (self), TRUE);
2079
2080         variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Modified");
2081         g_return_val_if_fail (variant != NULL, 0);
2082
2083         modified = g_variant_get_uint64 (variant);
2084         g_variant_unref (variant);
2085
2086         return modified;
2087 }