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 
25 #include "libsecret/secret-enum-types.h"
26 
27 #include <glib/gi18n-lib.h>
28 
29 /**
30  * SECTION:secret-collection
31  * @title: SecretCollection
32  * @short_description: A collection of secret items
33  *
34  * #SecretCollection represents a collection of secret items stored in the
35  * Secret Service.
36  *
37  * A collection can be in a locked or unlocked state. Use secret_service_lock()
38  * or secret_service_unlock() to lock or unlock the collection.
39  *
40  * Use the SecretCollection::items property or secret_collection_get_items() to
41  * lookup the items in the collection. There may not be any items exposed when
42  * the collection is locked.
43  *
44  * Stability: Stable
45  */
46 
47 /**
48  * SecretCollection:
49  *
50  * A proxy object representing a collection of secrets in the Secret Service.
51  */
52 
53 /**
54  * SecretCollectionClass:
55  * @parent_class: the parent class
56  *
57  * The class for #SecretCollection.
58  */
59 
60 /**
61  * SecretCollectionFlags:
62  * @SECRET_COLLECTION_NONE: no flags
63  * @SECRET_COLLECTION_LOAD_ITEMS: items have or should be loaded
64  *
65  * Flags which determine which parts of the #SecretCollection proxy are initialized.
66  */
67 
68 /**
69  * SecretCollectionCreateFlags:
70  * @SECRET_COLLECTION_CREATE_NONE: no flags
71  *
72  * Flags for secret_collection_create().
73  */
74 
75 /**
76  * SECRET_COLLECTION_DEFAULT:
77  *
78  * An alias to the default collection. This can be passed to secret_password_store()
79  * secret_collection_for_alias().
80  */
81 
82 /**
83  * SECRET_COLLECTION_SESSION:
84  *
85  * An alias to the session collection, which will be cleared when the user ends
86  * the session. This can be passed to secret_password_store(),
87  * secret_collection_for_alias() or similar functions.
88  */
89 
90 enum {
91 	PROP_0,
92 	PROP_SERVICE,
93 	PROP_FLAGS,
94 	PROP_ITEMS,
95 	PROP_LABEL,
96 	PROP_LOCKED,
97 	PROP_CREATED,
98 	PROP_MODIFIED
99 };
100 
101 struct _SecretCollectionPrivate {
102 	/* Doesn't change between construct and finalize */
103 	SecretService *service;
104 	GCancellable *cancellable;
105 	gboolean constructing;
106 	SecretCollectionFlags init_flags;
107 
108 	/* Protected by mutex */
109 	GMutex mutex;
110 	GHashTable *items;
111 };
112 
113 static GInitableIface *secret_collection_initable_parent_iface = NULL;
114 
115 static GAsyncInitableIface *secret_collection_async_initable_parent_iface = NULL;
116 
117 static void   secret_collection_initable_iface         (GInitableIface *iface);
118 
119 static void   secret_collection_async_initable_iface   (GAsyncInitableIface *iface);
120 
121 G_DEFINE_TYPE_WITH_CODE (SecretCollection, secret_collection, G_TYPE_DBUS_PROXY,
122                          G_ADD_PRIVATE (SecretCollection)
123                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, secret_collection_initable_iface);
124                          G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, secret_collection_async_initable_iface);
125 );
126 
127 static GHashTable *
items_table_new(void)128 items_table_new (void)
129 {
130 	return g_hash_table_new_full (g_str_hash, g_str_equal,
131 	                              g_free, g_object_unref);
132 }
133 
134 static void
secret_collection_init(SecretCollection * self)135 secret_collection_init (SecretCollection *self)
136 {
137 	self->pv = secret_collection_get_instance_private (self);
138 
139 	g_mutex_init (&self->pv->mutex);
140 	self->pv->cancellable = g_cancellable_new ();
141 	self->pv->constructing = TRUE;
142 }
143 
144 static void
on_set_label(GObject * source,GAsyncResult * result,gpointer user_data)145 on_set_label (GObject *source,
146               GAsyncResult *result,
147               gpointer user_data)
148 {
149 	SecretCollection *self = SECRET_COLLECTION (user_data);
150 	GError *error = NULL;
151 
152 	secret_collection_set_label_finish (self, result, &error);
153 	if (error != NULL) {
154 		g_warning ("couldn't set SecretCollection Label: %s", error->message);
155 		g_error_free (error);
156 	}
157 
158 	g_object_unref (self);
159 }
160 
161 static void
collection_take_service(SecretCollection * self,SecretService * service)162 collection_take_service (SecretCollection *self,
163                          SecretService *service)
164 {
165 	if (service == NULL)
166 		return;
167 
168 	g_return_if_fail (self->pv->service == NULL);
169 
170 	self->pv->service = service;
171 	g_object_add_weak_pointer (G_OBJECT (self->pv->service),
172 	                           (gpointer *)&self->pv->service);
173 
174 	/* Yes, we expect that the service will stay around */
175 	g_object_unref (service);
176 }
177 
178 static void
secret_collection_set_property(GObject * obj,guint prop_id,const GValue * value,GParamSpec * pspec)179 secret_collection_set_property (GObject *obj,
180                                 guint prop_id,
181                                 const GValue *value,
182                                 GParamSpec *pspec)
183 {
184 	SecretCollection *self = SECRET_COLLECTION (obj);
185 
186 	switch (prop_id) {
187 	case PROP_SERVICE:
188 		collection_take_service (self, g_value_dup_object (value));
189 		break;
190 	case PROP_FLAGS:
191 		self->pv->init_flags = g_value_get_flags (value);
192 		break;
193 	case PROP_LABEL:
194 		secret_collection_set_label (self, g_value_get_string (value),
195 		                             self->pv->cancellable, on_set_label,
196 		                             g_object_ref (self));
197 		break;
198 	default:
199 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
200 		break;
201 	}
202 }
203 
204 static void
secret_collection_get_property(GObject * obj,guint prop_id,GValue * value,GParamSpec * pspec)205 secret_collection_get_property (GObject *obj,
206                                 guint prop_id,
207                                 GValue *value,
208                                 GParamSpec *pspec)
209 {
210 	SecretCollection *self = SECRET_COLLECTION (obj);
211 
212 	switch (prop_id) {
213 	case PROP_SERVICE:
214 		g_value_set_object (value, self->pv->service);
215 		break;
216 	case PROP_FLAGS:
217 		g_value_set_flags (value, secret_collection_get_flags (self));
218 		break;
219 	case PROP_ITEMS:
220 		g_value_take_boxed (value, secret_collection_get_items (self));
221 		break;
222 	case PROP_LABEL:
223 		g_value_take_string (value, secret_collection_get_label (self));
224 		break;
225 	case PROP_LOCKED:
226 		g_value_set_boolean (value, secret_collection_get_locked (self));
227 		break;
228 	case PROP_CREATED:
229 		g_value_set_uint64 (value, secret_collection_get_created (self));
230 		break;
231 	case PROP_MODIFIED:
232 		g_value_set_uint64 (value, secret_collection_get_modified (self));
233 		break;
234 	default:
235 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
236 		break;
237 	}
238 }
239 
240 static void
secret_collection_dispose(GObject * obj)241 secret_collection_dispose (GObject *obj)
242 {
243 	SecretCollection *self = SECRET_COLLECTION (obj);
244 
245 	g_cancellable_cancel (self->pv->cancellable);
246 
247 	G_OBJECT_CLASS (secret_collection_parent_class)->dispose (obj);
248 }
249 
250 static void
secret_collection_finalize(GObject * obj)251 secret_collection_finalize (GObject *obj)
252 {
253 	SecretCollection *self = SECRET_COLLECTION (obj);
254 
255 	if (self->pv->service)
256 		g_object_remove_weak_pointer (G_OBJECT (self->pv->service),
257 		                              (gpointer *)&self->pv->service);
258 
259 	g_mutex_clear (&self->pv->mutex);
260 	if (self->pv->items)
261 		g_hash_table_destroy (self->pv->items);
262 	g_object_unref (self->pv->cancellable);
263 
264 	G_OBJECT_CLASS (secret_collection_parent_class)->finalize (obj);
265 }
266 
267 static void
collection_update_items(SecretCollection * self,GHashTable * items)268 collection_update_items (SecretCollection *self,
269                          GHashTable *items)
270 {
271 	GHashTable *previous;
272 
273 	g_hash_table_ref (items);
274 
275 	g_mutex_lock (&self->pv->mutex);
276 	previous = self->pv->items;
277 	self->pv->items = items;
278 	g_mutex_unlock (&self->pv->mutex);
279 
280 	if (previous != NULL)
281 		g_hash_table_unref (previous);
282 
283 	g_object_notify (G_OBJECT (self), "items");
284 }
285 
286 static void
handle_property_changed(SecretCollection * self,const gchar * property_name,GVariant * value)287 handle_property_changed (SecretCollection *self,
288                          const gchar *property_name,
289                          GVariant *value)
290 {
291 	gboolean perform;
292 
293 	if (g_str_equal (property_name, "Label")) {
294 		g_object_notify (G_OBJECT (self), "label");
295 
296 	} else if (g_str_equal (property_name, "Locked")) {
297 		g_object_notify (G_OBJECT (self), "locked");
298 
299 	} else if (g_str_equal (property_name, "Created")) {
300 		g_object_notify (G_OBJECT (self), "created");
301 
302 	} else if (g_str_equal (property_name, "Modified")) {
303 		g_object_notify (G_OBJECT (self), "modified");
304 
305 	} else if (g_str_equal (property_name, "Items") && !self->pv->constructing) {
306 		g_mutex_lock (&self->pv->mutex);
307 		perform = self->pv->items != NULL;
308 		g_mutex_unlock (&self->pv->mutex);
309 
310 		if (perform)
311 			secret_collection_load_items (self, self->pv->cancellable, NULL, NULL);
312 	}
313 }
314 
315 static void
secret_collection_properties_changed(GDBusProxy * proxy,GVariant * changed_properties,const gchar * const * invalidated_properties)316 secret_collection_properties_changed (GDBusProxy *proxy,
317                                       GVariant *changed_properties,
318                                       const gchar* const *invalidated_properties)
319 {
320 	SecretCollection *self = SECRET_COLLECTION (proxy);
321 	gchar *property_name;
322 	GVariantIter iter;
323 	GVariant *value;
324 
325 	g_object_freeze_notify (G_OBJECT (self));
326 
327 	g_variant_iter_init (&iter, changed_properties);
328 	while (g_variant_iter_loop (&iter, "{sv}", &property_name, &value))
329 		handle_property_changed (self, property_name, value);
330 
331 	g_object_thaw_notify (G_OBJECT (self));
332 }
333 
334 static void
secret_collection_signal(GDBusProxy * proxy,const gchar * sender_name,const gchar * signal_name,GVariant * parameters)335 secret_collection_signal (GDBusProxy *proxy,
336                           const gchar *sender_name,
337                           const gchar *signal_name,
338                           GVariant *parameters)
339 {
340 	SecretCollection *self = SECRET_COLLECTION (proxy);
341 	SecretItem *item;
342 	const gchar *item_path;
343 	GVariantBuilder builder;
344 	gboolean found = FALSE;
345 	GVariantIter iter;
346 	GVariant *value;
347 	GVariant *paths;
348 	GVariant *path;
349 
350 	/*
351 	 * Remember that these signals come from a time before PropertiesChanged.
352 	 * We support them because they're in the spec, and ksecretservice uses them.
353 	 */
354 
355 	paths = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Items");
356 
357 	/* A new collection was added, add it to the Collections property */
358 	if (g_str_equal (signal_name, SECRET_SIGNAL_ITEM_CREATED)) {
359 		g_variant_get (parameters, "(@o)", &value);
360 		g_variant_builder_init (&builder, G_VARIANT_TYPE ("ao"));
361 		g_variant_iter_init (&iter, paths);
362 		while ((path = g_variant_iter_next_value (&iter)) != NULL) {
363 			if (g_variant_equal (path, value)) {
364 				found = TRUE;
365 				break;
366 			}
367 			g_variant_builder_add_value (&builder, path);
368 			g_variant_unref (path);
369 		}
370 		if (!found) {
371 			g_variant_builder_add_value (&builder, value);
372 			handle_property_changed (self, "Items", g_variant_builder_end (&builder));
373 		}
374 		g_variant_builder_clear (&builder);
375 		g_variant_unref (value);
376 
377 	/* A collection was deleted, remove it from the Collections property */
378 	} else if (g_str_equal (signal_name, SECRET_SIGNAL_ITEM_DELETED)) {
379 		g_variant_get (parameters, "(@o)", &value);
380 		g_variant_builder_init (&builder, G_VARIANT_TYPE ("ao"));
381 		g_variant_iter_init (&iter, paths);
382 		while ((path = g_variant_iter_next_value (&iter)) != NULL) {
383 			if (g_variant_equal (path, value))
384 				found = TRUE;
385 			else
386 				g_variant_builder_add_value (&builder, path);
387 			g_variant_unref (path);
388 		}
389 		if (found)
390 			handle_property_changed (self, "Items", g_variant_builder_end (&builder));
391 		g_variant_unref (value);
392 
393 	/* The collection changed, update it */
394 	} else if (g_str_equal (signal_name, SECRET_SIGNAL_ITEM_CHANGED)) {
395 		g_variant_get (parameters, "(&o)", &item_path);
396 
397 		g_mutex_lock (&self->pv->mutex);
398 
399 		if (self->pv->items)
400 			item = g_hash_table_lookup (self->pv->items, item_path);
401 		else
402 			item = NULL;
403 		if (item)
404 			g_object_ref (item);
405 
406 		g_mutex_unlock (&self->pv->mutex);
407 
408 		if (item) {
409 			secret_item_refresh (item);
410 			g_object_unref (item);
411 		}
412 	}
413 
414 	g_variant_unref (paths);
415 }
416 
417 static void
secret_collection_class_init(SecretCollectionClass * klass)418 secret_collection_class_init (SecretCollectionClass *klass)
419 {
420 	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
421 	GDBusProxyClass *proxy_class = G_DBUS_PROXY_CLASS (klass);
422 
423 	gobject_class->get_property = secret_collection_get_property;
424 	gobject_class->set_property = secret_collection_set_property;
425 	gobject_class->dispose = secret_collection_dispose;
426 	gobject_class->finalize = secret_collection_finalize;
427 
428 	proxy_class->g_properties_changed = secret_collection_properties_changed;
429 	proxy_class->g_signal = secret_collection_signal;
430 
431 	/**
432 	 * SecretCollection:service:
433 	 *
434 	 * The #SecretService object that this collection is associated with and
435 	 * uses to interact with the actual D-Bus Secret Service.
436 	 */
437 	g_object_class_install_property (gobject_class, PROP_SERVICE,
438 	            g_param_spec_object ("service", "Service", "Secret Service",
439 	                                 SECRET_TYPE_SERVICE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
440 
441 	/**
442 	 * SecretCollection:flags:
443 	 *
444 	 * A set of flags describing which parts of the secret collection have
445 	 * been initialized.
446 	 */
447 	g_object_class_install_property (gobject_class, PROP_FLAGS,
448 	             g_param_spec_flags ("flags", "Flags", "Collection flags",
449 	                                 secret_collection_flags_get_type (), SECRET_COLLECTION_NONE,
450 	                                 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
451 
452 	/**
453 	 * SecretCollection:items:
454 	 *
455 	 * A list of #SecretItem objects representing the items that are in
456 	 * this collection. This list will be empty if the collection is locked.
457 	 */
458 	g_object_class_install_property (gobject_class, PROP_ITEMS,
459 	             g_param_spec_boxed ("items", "Items", "Items in collection",
460 	                                 _secret_list_get_type (), G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
461 
462 	/**
463 	 * SecretCollection:label:
464 	 *
465 	 * The human readable label for the collection.
466 	 *
467 	 * Setting this property will result in the label of the collection being
468 	 * set asynchronously. To properly track the changing of the label use the
469 	 * secret_collection_set_label() function.
470 	 */
471 	g_object_class_install_property (gobject_class, PROP_LABEL,
472 	            g_param_spec_string ("label", "Label", "Item label",
473 	                                 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
474 
475 	/**
476 	 * SecretCollection:locked:
477 	 *
478 	 * Whether the collection is locked or not.
479 	 *
480 	 * To lock or unlock a collection use the secret_service_lock() or
481 	 * secret_service_unlock() functions.
482 	 */
483 	g_object_class_install_property (gobject_class, PROP_LOCKED,
484 	           g_param_spec_boolean ("locked", "Locked", "Item locked",
485 	                                 TRUE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
486 
487 	/**
488 	 * SecretCollection:created:
489 	 *
490 	 * The date and time (in seconds since the UNIX epoch) that this
491 	 * collection was created.
492 	 */
493 	g_object_class_install_property (gobject_class, PROP_CREATED,
494 	            g_param_spec_uint64 ("created", "Created", "Item creation date",
495 	                                 0UL, G_MAXUINT64, 0UL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
496 
497 	/**
498 	 * SecretCollection:modified:
499 	 *
500 	 * The date and time (in seconds since the UNIX epoch) that this
501 	 * collection was last modified.
502 	 */
503 	g_object_class_install_property (gobject_class, PROP_MODIFIED,
504 	            g_param_spec_uint64 ("modified", "Modified", "Item modified date",
505 	                                 0UL, G_MAXUINT64, 0UL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
506 }
507 
508 static gboolean
collection_ensure_for_flags_sync(SecretCollection * self,SecretCollectionFlags flags,GCancellable * cancellable,GError ** error)509 collection_ensure_for_flags_sync (SecretCollection *self,
510                                   SecretCollectionFlags flags,
511                                   GCancellable *cancellable,
512                                   GError **error)
513 {
514 	SecretCollectionFlags want_flags;
515 
516 	want_flags = flags & ~secret_collection_get_flags (self);
517 
518 	if (want_flags & SECRET_COLLECTION_LOAD_ITEMS) {
519 		if (!secret_collection_load_items_sync (self, cancellable, error))
520 			return FALSE;
521 	}
522 
523 	return TRUE;
524 }
525 
526 static gboolean
secret_collection_initable_init(GInitable * initable,GCancellable * cancellable,GError ** error)527 secret_collection_initable_init (GInitable *initable,
528                                  GCancellable *cancellable,
529                                  GError **error)
530 {
531 	SecretCollection *self;
532 	SecretService *service;
533 	GDBusProxy *proxy;
534 
535 	if (!secret_collection_initable_parent_iface->init (initable, cancellable, error))
536 		return FALSE;
537 
538 	proxy = G_DBUS_PROXY (initable);
539 
540 	if (!_secret_util_have_cached_properties (proxy)) {
541 		g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD,
542 		             "No such secret collection at path: %s",
543 		             g_dbus_proxy_get_object_path (proxy));
544 		return FALSE;
545 	}
546 
547 	self = SECRET_COLLECTION (initable);
548 
549 	if (self->pv->service == NULL) {
550 		service = secret_service_get_sync (SECRET_SERVICE_NONE, cancellable, error);
551 		if (service == NULL)
552 			return FALSE;
553 		else
554 			collection_take_service (self, service);
555 	}
556 
557 	if (!collection_ensure_for_flags_sync (self, self->pv->init_flags, cancellable, error))
558 		return FALSE;
559 
560 	self->pv->constructing = FALSE;
561 	return TRUE;
562 }
563 
564 static void
secret_collection_initable_iface(GInitableIface * iface)565 secret_collection_initable_iface (GInitableIface *iface)
566 {
567 	secret_collection_initable_parent_iface = g_type_interface_peek_parent (iface);
568 
569 	iface->init = secret_collection_initable_init;
570 }
571 
572 typedef struct {
573 	GCancellable *cancellable;
574 } InitClosure;
575 
576 static void
init_closure_free(gpointer data)577 init_closure_free (gpointer data)
578 {
579 	InitClosure *closure = data;
580 	g_clear_object (&closure->cancellable);
581 	g_slice_free (InitClosure, closure);
582 }
583 
584 static void
on_ensure_items(GObject * source,GAsyncResult * result,gpointer user_data)585 on_ensure_items (GObject *source,
586                  GAsyncResult *result,
587                  gpointer user_data)
588 {
589 	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
590 	SecretCollection *self = SECRET_COLLECTION (source);
591 	GError *error = NULL;
592 
593 	if (!secret_collection_load_items_finish (self, result, &error))
594 		g_simple_async_result_take_error (res, error);
595 
596 	g_simple_async_result_complete (res);
597 	g_object_unref (res);
598 }
599 
600 static void
collection_ensure_for_flags_async(SecretCollection * self,SecretCollectionFlags flags,GCancellable * cancellable,GSimpleAsyncResult * async)601 collection_ensure_for_flags_async (SecretCollection *self,
602                                    SecretCollectionFlags flags,
603                                    GCancellable *cancellable,
604                                    GSimpleAsyncResult *async)
605 {
606 	SecretCollectionFlags want_flags;
607 
608 	want_flags = flags & ~secret_collection_get_flags (self);
609 
610 	if (want_flags & SECRET_COLLECTION_LOAD_ITEMS) {
611 		secret_collection_load_items (self, cancellable,
612 		                              on_ensure_items, g_object_ref (async));
613 
614 	} else {
615 		g_simple_async_result_complete (async);
616 	}
617 }
618 
619 static void
on_init_service(GObject * source,GAsyncResult * result,gpointer user_data)620 on_init_service (GObject *source,
621                  GAsyncResult *result,
622                  gpointer user_data)
623 {
624 	GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
625 	SecretCollection *self = SECRET_COLLECTION (g_async_result_get_source_object (user_data));
626 	InitClosure *init = g_simple_async_result_get_op_res_gpointer (async);
627 	SecretService *service;
628 	GError *error = NULL;
629 
630 	service = secret_service_get_finish (result, &error);
631 	if (error == NULL) {
632 		collection_take_service (self, service);
633 		collection_ensure_for_flags_async (self, self->pv->init_flags,
634 		                                   init->cancellable, async);
635 
636 	} else {
637 		g_simple_async_result_take_error (async, error);
638 		g_simple_async_result_complete (async);
639 	}
640 
641 	g_object_unref (self);
642 	g_object_unref (async);
643 }
644 
645 static void
on_init_base(GObject * source,GAsyncResult * result,gpointer user_data)646 on_init_base (GObject *source,
647               GAsyncResult *result,
648               gpointer user_data)
649 {
650 	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
651 	SecretCollection *self = SECRET_COLLECTION (source);
652 	InitClosure *init = g_simple_async_result_get_op_res_gpointer (res);
653 	GDBusProxy *proxy = G_DBUS_PROXY (self);
654 	GError *error = NULL;
655 
656 	if (!secret_collection_async_initable_parent_iface->init_finish (G_ASYNC_INITABLE (self),
657 	                                                                 result, &error)) {
658 		g_simple_async_result_take_error (res, error);
659 		g_simple_async_result_complete (res);
660 
661 	} else if (!_secret_util_have_cached_properties (proxy)) {
662 		g_simple_async_result_set_error (res, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD,
663 		                                 "No such secret collection at path: %s",
664 		                                 g_dbus_proxy_get_object_path (proxy));
665 		g_simple_async_result_complete (res);
666 
667 	} else if (self->pv->service == NULL) {
668 		secret_service_get (SECRET_SERVICE_NONE, init->cancellable,
669 		                    on_init_service, g_object_ref (res));
670 
671 	} else {
672 		collection_ensure_for_flags_async (self, self->pv->init_flags,
673 		                                   init->cancellable, res);
674 	}
675 
676 	g_object_unref (res);
677 }
678 
679 static void
secret_collection_async_initable_init_async(GAsyncInitable * initable,int io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)680 secret_collection_async_initable_init_async (GAsyncInitable *initable,
681                                              int io_priority,
682                                              GCancellable *cancellable,
683                                              GAsyncReadyCallback callback,
684                                              gpointer user_data)
685 {
686 	GSimpleAsyncResult *res;
687 	InitClosure *closure;
688 
689 	res = g_simple_async_result_new (G_OBJECT (initable), callback, user_data,
690 	                                 secret_collection_async_initable_init_async);
691 	closure = g_slice_new0 (InitClosure);
692 	closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
693 	g_simple_async_result_set_op_res_gpointer (res, closure, init_closure_free);
694 
695 	secret_collection_async_initable_parent_iface->init_async (initable, io_priority,
696 	                                                           cancellable,
697 	                                                           on_init_base,
698 	                                                           g_object_ref (res));
699 
700 	g_object_unref (res);
701 }
702 
703 static gboolean
secret_collection_async_initable_init_finish(GAsyncInitable * initable,GAsyncResult * result,GError ** error)704 secret_collection_async_initable_init_finish (GAsyncInitable *initable,
705                                               GAsyncResult *result,
706                                               GError **error)
707 {
708 	SecretCollection *self = SECRET_COLLECTION (initable);
709 
710 	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (initable),
711 	                      secret_collection_async_initable_init_async), FALSE);
712 
713 	if (_secret_util_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
714 		return FALSE;
715 
716 	self->pv->constructing = FALSE;
717 	return TRUE;
718 }
719 
720 static void
secret_collection_async_initable_iface(GAsyncInitableIface * iface)721 secret_collection_async_initable_iface (GAsyncInitableIface *iface)
722 {
723 	secret_collection_async_initable_parent_iface = g_type_interface_peek_parent (iface);
724 
725 	iface->init_async = secret_collection_async_initable_init_async;
726 	iface->init_finish = secret_collection_async_initable_init_finish;
727 }
728 
729 typedef struct {
730 	GCancellable *cancellable;
731 	GHashTable *items;
732 	gint items_loading;
733 } ItemsClosure;
734 
735 static void
items_closure_free(gpointer data)736 items_closure_free (gpointer data)
737 {
738 	ItemsClosure *closure = data;
739 	g_clear_object (&closure->cancellable);
740 	g_hash_table_unref (closure->items);
741 	g_slice_free (ItemsClosure, closure);
742 }
743 
744 static void
on_load_item(GObject * source,GAsyncResult * result,gpointer user_data)745 on_load_item (GObject *source,
746               GAsyncResult *result,
747               gpointer user_data)
748 {
749 	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
750 	ItemsClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
751 	SecretCollection *self = SECRET_COLLECTION (g_async_result_get_source_object (user_data));
752 	const gchar *path;
753 	GError *error = NULL;
754 	SecretItem *item;
755 
756 	closure->items_loading--;
757 
758 	item = secret_item_new_for_dbus_path_finish (result, &error);
759 
760 	if (error != NULL)
761 		g_simple_async_result_take_error (res, error);
762 
763 	if (item != NULL) {
764 		path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (item));
765 		g_hash_table_insert (closure->items, g_strdup (path), item);
766 	}
767 
768 	if (closure->items_loading == 0) {
769 		collection_update_items (self, closure->items);
770 		g_simple_async_result_complete_in_idle (res);
771 	}
772 
773 	g_object_unref (self);
774 	g_object_unref (res);
775 }
776 
777 /**
778  * secret_collection_load_items:
779  * @self: the secret collection
780  * @cancellable: optional cancellation object
781  * @callback: called when the operation completes
782  * @user_data: data to be passed to the callback
783  *
784  * Ensure that the #SecretCollection proxy has loaded all the items present
785  * in the Secret Service. This affects the result of
786  * secret_collection_get_items().
787  *
788  * For collections returned from secret_service_get_collections() the items
789  * will have already been loaded.
790  *
791  * This method will return immediately and complete asynchronously.
792  */
793 void
secret_collection_load_items(SecretCollection * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)794 secret_collection_load_items (SecretCollection *self,
795                               GCancellable *cancellable,
796                               GAsyncReadyCallback callback,
797                               gpointer user_data)
798 {
799 	ItemsClosure *closure;
800 	SecretItem *item;
801 	GSimpleAsyncResult *res;
802 	const gchar *path;
803 	GVariant *paths;
804 	GVariantIter iter;
805 
806 	g_return_if_fail (SECRET_IS_COLLECTION (self));
807 	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
808 
809 	paths = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Items");
810 	g_return_if_fail (paths != NULL);
811 
812 	res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
813 	                                 secret_collection_load_items);
814 	closure = g_slice_new0 (ItemsClosure);
815 	closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
816 	closure->items = items_table_new ();
817 	g_simple_async_result_set_op_res_gpointer (res, closure, items_closure_free);
818 
819 	g_variant_iter_init (&iter, paths);
820 	while (g_variant_iter_loop (&iter, "&o", &path)) {
821 		item = _secret_collection_find_item_instance (self, path);
822 
823 		/* No such collection yet create a new one */
824 		if (item == NULL) {
825 			secret_item_new_for_dbus_path (self->pv->service, path, SECRET_ITEM_NONE,
826 			                               cancellable, on_load_item, g_object_ref (res));
827 			closure->items_loading++;
828 
829 		} else {
830 			g_hash_table_insert (closure->items, g_strdup (path), item);
831 		}
832 	}
833 
834 	if (closure->items_loading == 0) {
835 		collection_update_items (self, closure->items);
836 		g_simple_async_result_complete_in_idle (res);
837 	}
838 
839 	g_variant_unref (paths);
840 	g_object_unref (res);
841 }
842 
843 /**
844  * secret_collection_load_items_finish:
845  * @self: the secret collection
846  * @result: the asynchronous result passed to the callback
847  * @error: location to place an error on failure
848  *
849  * Complete an asynchronous operation to ensure that the #SecretCollection proxy
850  * has loaded all the items present in the Secret Service.
851  *
852  * Returns: whether the load was successful or not
853  */
854 gboolean
secret_collection_load_items_finish(SecretCollection * self,GAsyncResult * result,GError ** error)855 secret_collection_load_items_finish (SecretCollection *self,
856                                      GAsyncResult *result,
857                                      GError **error)
858 {
859 	g_return_val_if_fail (SECRET_IS_COLLECTION (self), FALSE);
860 	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
861 	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
862 	                      secret_collection_load_items), FALSE);
863 
864 	if (_secret_util_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
865 		return FALSE;
866 
867 	return TRUE;
868 }
869 
870 /**
871  * secret_collection_load_items_sync:
872  * @self: the secret collection
873  * @cancellable: optional cancellation object
874  * @error: location to place an error on failure
875  *
876  * Ensure that the #SecretCollection proxy has loaded all the items present
877  * in the Secret Service. This affects the result of
878  * secret_collection_get_items().
879  *
880  * For collections returned from secret_service_get_collections() the items
881  * will have already been loaded.
882  *
883  * This method may block indefinitely and should not be used in user interface
884  * threads.
885  *
886  * Returns: whether the load was successful or not
887  */
888 gboolean
secret_collection_load_items_sync(SecretCollection * self,GCancellable * cancellable,GError ** error)889 secret_collection_load_items_sync (SecretCollection *self,
890                                    GCancellable *cancellable,
891                                    GError **error)
892 {
893 	SecretItem *item;
894 	GHashTable *items;
895 	GVariant *paths;
896 	GVariantIter iter;
897 	const gchar *path;
898 	gboolean ret = TRUE;
899 
900 	g_return_val_if_fail (SECRET_IS_COLLECTION (self), FALSE);
901 	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
902 	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
903 
904 	paths = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Items");
905 	g_return_val_if_fail (paths != NULL, FALSE);
906 
907 	items = items_table_new ();
908 
909 	g_variant_iter_init (&iter, paths);
910 	while (g_variant_iter_next (&iter, "&o", &path)) {
911 		item = _secret_collection_find_item_instance (self, path);
912 
913 		/* No such collection yet create a new one */
914 		if (item == NULL) {
915 			item = secret_item_new_for_dbus_path_sync (self->pv->service, path,
916 			                                           SECRET_ITEM_NONE,
917 			                                           cancellable, error);
918 			if (item == NULL) {
919 				ret = FALSE;
920 				break;
921 			}
922 		}
923 
924 		g_hash_table_insert (items, g_strdup (path), item);
925 	}
926 
927 	if (ret)
928 		collection_update_items (self, items);
929 
930 	g_hash_table_unref (items);
931 	g_variant_unref (paths);
932 	return ret;
933 }
934 
935 /**
936  * secret_collection_refresh:
937  * @self: the collection
938  *
939  * Refresh the properties on this collection. This fires off a request to
940  * refresh, and the properties will be updated later.
941  *
942  * Calling this method is not normally necessary, as the secret service
943  * will notify the client when properties change.
944  */
945 void
secret_collection_refresh(SecretCollection * self)946 secret_collection_refresh (SecretCollection *self)
947 {
948 	g_return_if_fail (SECRET_IS_COLLECTION (self));
949 
950 	_secret_util_get_properties (G_DBUS_PROXY (self),
951 	                              secret_collection_refresh,
952 	                              self->pv->cancellable, NULL, NULL);
953 }
954 
955 typedef struct {
956 	GCancellable *cancellable;
957 	SecretCollection *collection;
958 	GHashTable *properties;
959 	gchar *alias;
960 	SecretCollectionCreateFlags flags;
961 } CreateClosure;
962 
963 static void
create_closure_free(gpointer data)964 create_closure_free (gpointer data)
965 {
966 	CreateClosure *closure = data;
967 	g_clear_object (&closure->cancellable);
968 	g_clear_object (&closure->collection);
969 	g_hash_table_unref (closure->properties);
970 	g_free (closure->alias);
971 	g_slice_free (CreateClosure, closure);
972 }
973 
974 static void
on_create_collection(GObject * source,GAsyncResult * result,gpointer user_data)975 on_create_collection (GObject *source,
976                       GAsyncResult *result,
977                       gpointer user_data)
978 {
979 	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
980 	CreateClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
981 	GError *error = NULL;
982 
983 	closure->collection = secret_collection_new_for_dbus_path_finish (result, &error);
984 	if (error != NULL)
985 		g_simple_async_result_take_error (res, error);
986 
987 	g_simple_async_result_complete (res);
988 	g_object_unref (res);
989 }
990 
991 static void
on_create_path(GObject * source,GAsyncResult * result,gpointer user_data)992 on_create_path (GObject *source,
993                 GAsyncResult *result,
994                 gpointer user_data)
995 {
996 	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
997 	CreateClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
998 	SecretService *service = SECRET_SERVICE (source);
999 	GError *error = NULL;
1000 	gchar *path;
1001 
1002 	path = secret_service_create_collection_dbus_path_finish (service, result, &error);
1003 	if (error == NULL) {
1004 		secret_collection_new_for_dbus_path (service, path, SECRET_COLLECTION_LOAD_ITEMS,
1005 		                                     closure->cancellable,
1006 		                                     on_create_collection, g_object_ref (res));
1007 	} else {
1008 		g_simple_async_result_take_error (res, error);
1009 		g_simple_async_result_complete (res);
1010 	}
1011 
1012 	g_object_unref (res);
1013 	g_free (path);
1014 }
1015 
1016 static void
on_create_service(GObject * source,GAsyncResult * result,gpointer user_data)1017 on_create_service (GObject *source,
1018                    GAsyncResult *result,
1019                    gpointer user_data)
1020 {
1021 	GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
1022 	CreateClosure *create = g_simple_async_result_get_op_res_gpointer (async);
1023 	GError *error = NULL;
1024 	SecretService *service;
1025 
1026 	service = secret_service_get_finish (result, &error);
1027 	if (error == NULL) {
1028 		secret_service_create_collection_dbus_path (service, create->properties,
1029 		                                            create->alias, create->flags,
1030 		                                            create->cancellable,
1031 		                                            on_create_path, g_object_ref (async));
1032 		g_object_unref (service);
1033 
1034 	} else {
1035 		g_simple_async_result_take_error (async, error);
1036 		g_simple_async_result_complete (async);
1037 	}
1038 
1039 	g_object_unref (async);
1040 }
1041 
1042 GHashTable *
_secret_collection_properties_new(const gchar * label)1043 _secret_collection_properties_new (const gchar *label)
1044 {
1045 	GHashTable *properties;
1046 	GVariant *value;
1047 
1048 	properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
1049 	                                    (GDestroyNotify)g_variant_unref);
1050 	value = g_variant_new_string (label);
1051 	g_hash_table_insert (properties,
1052 	                     SECRET_COLLECTION_INTERFACE ".Label",
1053 	                     g_variant_ref_sink (value));
1054 
1055 	return properties;
1056 }
1057 
1058 /**
1059  * secret_collection_create:
1060  * @service: (allow-none): a secret service object
1061  * @label: label for the new collection
1062  * @alias: (allow-none): alias to assign to the collection
1063  * @flags: currently unused
1064  * @cancellable: optional cancellation object
1065  * @callback: called when the operation completes
1066  * @user_data: data to pass to the callback
1067  *
1068  * Create a new collection in the secret service.
1069  *
1070  * This method returns immediately and completes asynchronously. The secret
1071  * service may prompt the user. secret_service_prompt() will be used to handle
1072  * any prompts that are required.
1073  *
1074  * An @alias is a well-known tag for a collection, such as 'default' (ie: the
1075  * default collection to store items in). This allows other applications to
1076  * easily identify and share a collection. If you specify an @alias, and a
1077  * collection with that alias already exists, then a new collection will not
1078  * be created. The previous one will be returned instead.
1079  *
1080  * If @service is NULL, then secret_service_get() will be called to get
1081  * the default #SecretService proxy.
1082  *
1083  */
1084 void
secret_collection_create(SecretService * service,const gchar * label,const gchar * alias,SecretCollectionCreateFlags flags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1085 secret_collection_create (SecretService *service,
1086                           const gchar *label,
1087                           const gchar *alias,
1088                           SecretCollectionCreateFlags flags,
1089                           GCancellable *cancellable,
1090                           GAsyncReadyCallback callback,
1091                           gpointer user_data)
1092 {
1093 	GSimpleAsyncResult *res;
1094 	CreateClosure *closure;
1095 
1096 	g_return_if_fail (service == NULL || SECRET_IS_SERVICE (service));
1097 	g_return_if_fail (label != NULL);
1098 	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1099 
1100 	res = g_simple_async_result_new (NULL, callback, user_data,
1101 	                                 secret_collection_create);
1102 	closure = g_slice_new0 (CreateClosure);
1103 	closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
1104 	closure->properties = _secret_collection_properties_new (label);
1105 	closure->alias = g_strdup (alias);
1106 	closure->flags = flags;
1107 	g_simple_async_result_set_op_res_gpointer (res, closure, create_closure_free);
1108 
1109 	if (service == NULL) {
1110 		secret_service_get (SECRET_SERVICE_NONE, cancellable,
1111 		                    on_create_service, g_object_ref (res));
1112 
1113 	} else {
1114 		secret_service_create_collection_dbus_path (service, closure->properties,
1115 		                                            closure->alias, closure->flags,
1116 		                                            closure->cancellable,
1117 		                                            on_create_path, g_object_ref (res));
1118 	}
1119 
1120 	g_object_unref (res);
1121 }
1122 
1123 /**
1124  * secret_collection_create_finish:
1125  * @result: the asynchronous result passed to the callback
1126  * @error: location to place an error on failure
1127  *
1128  * Finish operation to create a new collection in the secret service.
1129  *
1130  * Returns: (transfer full): the new collection, which should be unreferenced
1131  *          with g_object_unref()
1132  */
1133 SecretCollection *
secret_collection_create_finish(GAsyncResult * result,GError ** error)1134 secret_collection_create_finish (GAsyncResult *result,
1135                                  GError **error)
1136 {
1137 	GSimpleAsyncResult *res;
1138 	CreateClosure *closure;
1139 
1140 	g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL,
1141 	                      secret_collection_create), NULL);
1142 	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1143 
1144 	res = G_SIMPLE_ASYNC_RESULT (result);
1145 
1146 	if (_secret_util_propagate_error (res, error))
1147 		return NULL;
1148 
1149 	closure = g_simple_async_result_get_op_res_gpointer (res);
1150 	if (closure->collection == NULL)
1151 		return NULL;
1152 
1153 	return g_object_ref (closure->collection);
1154 }
1155 
1156 /**
1157  * secret_collection_create_sync:
1158  * @service: (allow-none): a secret service object
1159  * @label: label for the new collection
1160  * @alias: (allow-none): alias to assign to the collection
1161  * @flags: currently unused
1162  * @cancellable: optional cancellation object
1163  * @error: location to place an error on failure
1164  *
1165  * Create a new collection in the secret service.
1166  *
1167  * This method may block indefinitely and should not be used in user interface
1168  * threads. The secret service may prompt the user. secret_service_prompt()
1169  * will be used to handle any prompts that are required.
1170  *
1171  * An @alias is a well-known tag for a collection, such as 'default' (ie: the
1172  * default collection to store items in). This allows other applications to
1173  * easily identify and share a collection. If you specify an @alias, and a
1174  * collection with that alias already exists, then a new collection will not
1175  * be created. The previous one will be returned instead.
1176  *
1177  * If @service is NULL, then secret_service_get_sync() will be called to get
1178  * the default #SecretService proxy.
1179  *
1180  * Returns: (transfer full): the new collection, which should be unreferenced
1181  *          with g_object_unref()
1182  */
1183 SecretCollection *
secret_collection_create_sync(SecretService * service,const gchar * label,const gchar * alias,SecretCollectionCreateFlags flags,GCancellable * cancellable,GError ** error)1184 secret_collection_create_sync (SecretService *service,
1185                                const gchar *label,
1186                                const gchar *alias,
1187                                SecretCollectionCreateFlags flags,
1188                                GCancellable *cancellable,
1189                                GError **error)
1190 {
1191 	SecretCollection *collection;
1192 	GHashTable *properties;
1193 	gchar *path;
1194 
1195 	g_return_val_if_fail (service == NULL || SECRET_IS_SERVICE (service), NULL);
1196 	g_return_val_if_fail (label != NULL, NULL);
1197 	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
1198 	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1199 
1200 	if (service == NULL) {
1201 		service = secret_service_get_sync (SECRET_SERVICE_NONE, cancellable, error);
1202 		if (service == NULL)
1203 			return NULL;
1204 	} else {
1205 		g_object_ref (service);
1206 	}
1207 
1208 	properties = _secret_collection_properties_new (label);
1209 
1210 	path = secret_service_create_collection_dbus_path_sync (service, properties, alias,
1211 	                                                        flags, cancellable, error);
1212 
1213 	g_hash_table_unref (properties);
1214 
1215 	if (path == NULL) {
1216 		g_object_unref (service);
1217 		return NULL;
1218 	}
1219 
1220 	collection = secret_collection_new_for_dbus_path_sync (service, path,
1221 	                                                       SECRET_COLLECTION_LOAD_ITEMS,
1222 	                                                       cancellable, error);
1223 
1224 	g_object_unref (service);
1225 	g_free (path);
1226 
1227 	return collection;
1228 }
1229 
1230 typedef struct {
1231 	SecretCollection *collection;
1232 	GCancellable *cancellable;
1233 	GHashTable *items;
1234 	gchar **paths;
1235 	guint loading;
1236 	SecretSearchFlags flags;
1237 } SearchClosure;
1238 
1239 static void
search_closure_free(gpointer data)1240 search_closure_free (gpointer data)
1241 {
1242 	SearchClosure *closure = data;
1243 	g_object_unref (closure->collection);
1244 	g_clear_object (&closure->cancellable);
1245 	g_hash_table_unref (closure->items);
1246 	g_strfreev (closure->paths);
1247 	g_slice_free (SearchClosure, closure);
1248 }
1249 
1250 static void
search_closure_take_item(SearchClosure * closure,SecretItem * item)1251 search_closure_take_item (SearchClosure *closure,
1252                           SecretItem *item)
1253 {
1254 	const gchar *path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (item));
1255 	g_hash_table_insert (closure->items, (gpointer)path, item);
1256 }
1257 
1258 static void
on_search_secrets(GObject * source,GAsyncResult * result,gpointer user_data)1259 on_search_secrets (GObject *source,
1260                    GAsyncResult *result,
1261                    gpointer user_data)
1262 {
1263 	GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
1264 
1265 	/* Note that we ignore any unlock failure */
1266 	secret_item_load_secrets_finish (result, NULL);
1267 
1268 	g_simple_async_result_complete (async);
1269 	g_object_unref (async);
1270 }
1271 
1272 static void
on_search_unlocked(GObject * source,GAsyncResult * result,gpointer user_data)1273 on_search_unlocked (GObject *source,
1274                     GAsyncResult *result,
1275                     gpointer user_data)
1276 {
1277 	GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
1278 	SearchClosure *search = g_simple_async_result_get_op_res_gpointer (async);
1279 	GList *items;
1280 
1281 	/* Note that we ignore any unlock failure */
1282 	secret_service_unlock_finish (SECRET_SERVICE (source), result, NULL, NULL);
1283 
1284 	/* If loading secrets ... locked items automatically ignored */
1285 	if (search->flags & SECRET_SEARCH_LOAD_SECRETS) {
1286 		items = g_hash_table_get_values (search->items);
1287 		secret_item_load_secrets (items, search->cancellable,
1288 		                          on_search_secrets, g_object_ref (async));
1289 		g_list_free (items);
1290 
1291 	/* No additional options, just complete */
1292 	} else {
1293 		g_simple_async_result_complete (async);
1294 	}
1295 
1296 	g_object_unref (async);
1297 }
1298 
1299 static void
secret_search_unlock_load_or_complete(GSimpleAsyncResult * async,SearchClosure * search)1300 secret_search_unlock_load_or_complete (GSimpleAsyncResult *async,
1301                                        SearchClosure *search)
1302 {
1303 	GList *items;
1304 
1305 	/* If unlocking then unlock all the locked items */
1306 	if (search->flags & SECRET_SEARCH_UNLOCK) {
1307 		items = g_hash_table_get_values (search->items);
1308 		secret_service_unlock (secret_collection_get_service (search->collection),
1309 		                       items, search->cancellable,
1310 		                       on_search_unlocked, g_object_ref (async));
1311 		g_list_free (items);
1312 
1313 	/* If loading secrets ... locked items automatically ignored */
1314 	} else if (search->flags & SECRET_SEARCH_LOAD_SECRETS) {
1315 		items = g_hash_table_get_values (search->items);
1316 		secret_item_load_secrets (items, search->cancellable,
1317 		                          on_search_secrets, g_object_ref (async));
1318 		g_list_free (items);
1319 
1320 	/* No additional options, just complete */
1321 	} else {
1322 		g_simple_async_result_complete (async);
1323 	}
1324 }
1325 
1326 static void
on_search_loaded(GObject * source,GAsyncResult * result,gpointer user_data)1327 on_search_loaded (GObject *source,
1328                   GAsyncResult *result,
1329                   gpointer user_data)
1330 {
1331 	GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
1332 	SearchClosure *search = g_simple_async_result_get_op_res_gpointer (async);
1333 	GError *error = NULL;
1334 	SecretItem *item;
1335 
1336 	search->loading--;
1337 
1338 	item = secret_item_new_for_dbus_path_finish (result, &error);
1339 	if (error != NULL)
1340 		g_simple_async_result_take_error (async, error);
1341 
1342 	if (item != NULL)
1343 		search_closure_take_item (search, item);
1344 
1345 	/* We're done loading, lets go to the next step */
1346 	if (search->loading == 0)
1347 		secret_search_unlock_load_or_complete (async, search);
1348 
1349 	g_object_unref (async);
1350 }
1351 
1352 static void
on_search_paths(GObject * source,GAsyncResult * result,gpointer user_data)1353 on_search_paths (GObject *source,
1354                  GAsyncResult *result,
1355                  gpointer user_data)
1356 {
1357 	GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
1358 	SearchClosure *search = g_simple_async_result_get_op_res_gpointer (async);
1359 	SecretCollection *self = search->collection;
1360 	SecretService *service = secret_collection_get_service (self);
1361 	GError *error = NULL;
1362 	SecretItem *item;
1363 	gint want = 1;
1364 	gint i;
1365 
1366 	search->paths = secret_collection_search_for_dbus_paths_finish (self, result, &error);
1367 	if (error == NULL) {
1368 		want = 1;
1369 		if (search->flags & SECRET_SEARCH_ALL)
1370 			want = G_MAXINT;
1371 
1372 		for (i = 0; i < want && search->paths[i] != NULL; i++) {
1373 			item = _secret_collection_find_item_instance (self, search->paths[i]);
1374 			if (item == NULL) {
1375 				secret_item_new_for_dbus_path (service, search->paths[i], SECRET_ITEM_NONE,
1376 				                               search->cancellable, on_search_loaded,
1377 				                               g_object_ref (async));
1378 				search->loading++;
1379 			} else {
1380 				search_closure_take_item (search, item);
1381 			}
1382 
1383 		}
1384 
1385 		/* No items loading, complete operation now */
1386 		if (search->loading == 0)
1387 			secret_search_unlock_load_or_complete (async, search);
1388 
1389 	} else {
1390 		g_simple_async_result_take_error (async, error);
1391 		g_simple_async_result_complete (async);
1392 	}
1393 
1394 	g_object_unref (async);
1395 }
1396 
1397 /**
1398  * secret_collection_search:
1399  * @self: a secret collection
1400  * @schema: (allow-none): the schema for the attributes
1401  * @attributes: (element-type utf8 utf8): search for items matching these attributes
1402  * @flags: search option flags
1403  * @cancellable: optional cancellation object
1404  * @callback: called when the operation completes
1405  * @user_data: data to pass to the callback
1406  *
1407  * Search for items matching the @attributes in the @collection.
1408  * The @attributes should be a table of string keys and string values.
1409  *
1410  * If %SECRET_SEARCH_ALL is set in @flags, then all the items matching the
1411  * search will be returned. Otherwise only the first item will be returned.
1412  * This is almost always the unlocked item that was most recently stored.
1413  *
1414  * If %SECRET_SEARCH_UNLOCK is set in @flags, then items will be unlocked
1415  * if necessary. In either case, locked and unlocked items will match the
1416  * search and be returned. If the unlock fails, the search does not fail.
1417  *
1418  * If %SECRET_SEARCH_LOAD_SECRETS is set in @flags, then the items will have
1419  * their secret values loaded and available via secret_item_get_secret().
1420  *
1421  * This function returns immediately and completes asynchronously.
1422  */
1423 void
secret_collection_search(SecretCollection * self,const SecretSchema * schema,GHashTable * attributes,SecretSearchFlags flags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1424 secret_collection_search (SecretCollection *self,
1425                           const SecretSchema *schema,
1426                           GHashTable *attributes,
1427                           SecretSearchFlags flags,
1428                           GCancellable *cancellable,
1429                           GAsyncReadyCallback callback,
1430                           gpointer user_data)
1431 {
1432 	GSimpleAsyncResult *async;
1433 	SearchClosure *search;
1434 
1435 	g_return_if_fail (SECRET_IS_COLLECTION (self));
1436 	g_return_if_fail (attributes != NULL);
1437 	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1438 
1439 	/* Warnings raised already */
1440 	if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, TRUE))
1441 		return;
1442 
1443 	async = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
1444 	                                   secret_collection_search);
1445 	search = g_slice_new0 (SearchClosure);
1446 	search->collection = g_object_ref (self);
1447 	search->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
1448 	search->items = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
1449 	search->flags = flags;
1450 	g_simple_async_result_set_op_res_gpointer (async, search, search_closure_free);
1451 
1452 	secret_collection_search_for_dbus_paths (self, schema, attributes,
1453 	                                         cancellable, on_search_paths,
1454 	                                         g_object_ref (async));
1455 
1456 	g_object_unref (async);
1457 }
1458 
1459 /**
1460  * secret_collection_search_finish:
1461  * @self: the secret collection
1462  * @result: asynchronous result passed to callback
1463  * @error: location to place error on failure
1464  *
1465  * Complete asynchronous operation to search for items in a collection.
1466  *
1467  * Returns: (transfer full) (element-type Secret.Item):
1468  *          a list of items that matched the search
1469  */
1470 GList *
secret_collection_search_finish(SecretCollection * self,GAsyncResult * result,GError ** error)1471 secret_collection_search_finish (SecretCollection *self,
1472                                  GAsyncResult *result,
1473                                  GError **error)
1474 {
1475 	GSimpleAsyncResult *async;
1476 	SearchClosure *search;
1477 	GList *items = NULL;
1478 	SecretItem *item;
1479 	guint i;
1480 
1481 	g_return_val_if_fail (SECRET_IS_COLLECTION (self), NULL);
1482 	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1483 	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
1484 	                      secret_collection_search), NULL);
1485 
1486 	async = G_SIMPLE_ASYNC_RESULT (result);
1487 	if (_secret_util_propagate_error (async, error))
1488 		return NULL;
1489 
1490 	search = g_simple_async_result_get_op_res_gpointer (async);
1491 
1492 	for (i = 0; search->paths[i]; i++) {
1493 		item = g_hash_table_lookup (search->items, search->paths[i]);
1494 		if (item != NULL)
1495 			items = g_list_prepend (items, g_object_ref (item));
1496 	}
1497 
1498 	return g_list_reverse (items);
1499 }
1500 
1501 static gboolean
collection_load_items_sync(SecretCollection * self,GCancellable * cancellable,gchar ** paths,GList ** items,gint want,GError ** error)1502 collection_load_items_sync (SecretCollection *self,
1503                             GCancellable *cancellable,
1504                             gchar **paths,
1505                             GList **items,
1506                             gint want,
1507                             GError **error)
1508 {
1509 	SecretService *service = secret_collection_get_service (self);
1510 	SecretItem *item;
1511 	gint have = 0;
1512 	guint i;
1513 
1514 	for (i = 0; have < want && paths[i] != NULL; i++) {
1515 		item = _secret_collection_find_item_instance (self, paths[i]);
1516 		if (item == NULL)
1517 			item = secret_item_new_for_dbus_path_sync (service, paths[i], SECRET_ITEM_NONE,
1518 			                                           cancellable, error);
1519 		if (item == NULL) {
1520 			return FALSE;
1521 
1522 		} else {
1523 			*items = g_list_prepend (*items, item);
1524 			have++;
1525 		}
1526 	}
1527 
1528 	return TRUE;
1529 }
1530 
1531 /**
1532  * secret_collection_search_sync:
1533  * @self: a secret collection
1534  * @schema: (allow-none): the schema for the attributes
1535  * @attributes: (element-type utf8 utf8): search for items matching these attributes
1536  * @flags: search option flags
1537  * @cancellable: optional cancellation object
1538  * @error: location to place error on failure
1539  *
1540  * Search for items matching the @attributes in the @collection.
1541  * The @attributes should be a table of string keys and string values.
1542  *
1543  * If %SECRET_SEARCH_ALL is set in @flags, then all the items matching the
1544  * search will be returned. Otherwise only the first item will be returned.
1545  * This is almost always the unlocked item that was most recently stored.
1546  *
1547  * If %SECRET_SEARCH_UNLOCK is set in @flags, then items will be unlocked
1548  * if necessary. In either case, locked and unlocked items will match the
1549  * search and be returned. If the unlock fails, the search does not fail.
1550  *
1551  * If %SECRET_SEARCH_LOAD_SECRETS is set in @flags, then the items will have
1552  * their secret values loaded and available via secret_item_get_secret().
1553  *
1554  * This function may block indefinitely. Use the asynchronous version
1555  * in user interface threads.
1556  *
1557  * Returns: (transfer full) (element-type Secret.Item):
1558  *          a list of items that matched the search
1559  */
1560 GList *
secret_collection_search_sync(SecretCollection * self,const SecretSchema * schema,GHashTable * attributes,SecretSearchFlags flags,GCancellable * cancellable,GError ** error)1561 secret_collection_search_sync (SecretCollection *self,
1562                                const SecretSchema *schema,
1563                                GHashTable *attributes,
1564                                SecretSearchFlags flags,
1565                                GCancellable *cancellable,
1566                                GError **error)
1567 {
1568 	gchar **paths = NULL;
1569 	GList *items = NULL;
1570 	gboolean ret;
1571 	gint want;
1572 
1573 	g_return_val_if_fail (SECRET_IS_COLLECTION (self), NULL);
1574 	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
1575 	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1576 
1577 	/* Warnings raised already */
1578 	if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, TRUE))
1579 		return NULL;
1580 
1581 	paths = secret_collection_search_for_dbus_paths_sync (self, schema, attributes,
1582 	                                                      cancellable, error);
1583 	if (paths == NULL)
1584 		return NULL;
1585 
1586 	ret = TRUE;
1587 
1588 	want = 1;
1589 	if (flags & SECRET_SEARCH_ALL)
1590 		want = G_MAXINT;
1591 
1592 	ret = collection_load_items_sync (self, cancellable, paths,
1593 	                                  &items, want, error);
1594 
1595 	g_strfreev (paths);
1596 
1597 	if (!ret)
1598 		return NULL;
1599 
1600 	if (flags & SECRET_SEARCH_UNLOCK) {
1601 		secret_service_unlock_sync (secret_collection_get_service (self),
1602 		                            items, cancellable, NULL, NULL);
1603 	}
1604 
1605 	if (flags & SECRET_SEARCH_LOAD_SECRETS)
1606 		secret_item_load_secrets_sync (items, NULL, NULL);
1607 
1608 	return items;
1609 }
1610 
1611 static void
on_service_delete_path(GObject * source,GAsyncResult * result,gpointer user_data)1612 on_service_delete_path (GObject *source,
1613                         GAsyncResult *result,
1614                         gpointer user_data)
1615 {
1616 	GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
1617 	GError *error = NULL;
1618 
1619 	_secret_service_delete_path_finish (SECRET_SERVICE (source), result, &error);
1620 	if (error != NULL)
1621 		g_simple_async_result_take_error (async, error);
1622 	g_simple_async_result_complete (async);
1623 	g_object_unref (async);
1624 }
1625 /**
1626  * secret_collection_delete:
1627  * @self: a collection
1628  * @cancellable: optional cancellation object
1629  * @callback: called when the operation completes
1630  * @user_data: data to pass to the callback
1631  *
1632  * Delete this collection.
1633  *
1634  * This method returns immediately and completes asynchronously. The secret
1635  * service may prompt the user. secret_service_prompt() will be used to handle
1636  * any prompts that show up.
1637  */
1638 void
secret_collection_delete(SecretCollection * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1639 secret_collection_delete (SecretCollection *self,
1640                           GCancellable *cancellable,
1641                           GAsyncReadyCallback callback,
1642                           gpointer user_data)
1643 {
1644 	GSimpleAsyncResult *async;
1645 	const gchar *object_path;
1646 
1647 	g_return_if_fail (SECRET_IS_COLLECTION (self));
1648 	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1649 
1650 	async = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
1651 	                                   secret_collection_delete);
1652 
1653 	object_path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (self));
1654 	_secret_service_delete_path (self->pv->service, object_path, FALSE,
1655 	                             cancellable, on_service_delete_path,
1656 	                             g_object_ref (async));
1657 
1658 	g_object_unref (async);
1659 }
1660 
1661 /**
1662  * secret_collection_delete_finish:
1663  * @self: a collection
1664  * @result: asynchronous result passed to the callback
1665  * @error: location to place an error on failure
1666  *
1667  * Complete operation to delete this collection.
1668  *
1669  * Returns: whether the collection was successfully deleted or not
1670  */
1671 gboolean
secret_collection_delete_finish(SecretCollection * self,GAsyncResult * result,GError ** error)1672 secret_collection_delete_finish (SecretCollection *self,
1673                                  GAsyncResult *result,
1674                                  GError **error)
1675 {
1676 	g_return_val_if_fail (SECRET_IS_COLLECTION (self), FALSE);
1677 	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1678 	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
1679 	                      secret_collection_delete), FALSE);
1680 
1681 	if (_secret_util_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
1682 		return FALSE;
1683 
1684 	return TRUE;
1685 }
1686 
1687 /**
1688  * secret_collection_delete_sync:
1689  * @self: a collection
1690  * @cancellable: optional cancellation object
1691  * @error: location to place an error on failure
1692  *
1693  * Delete this collection.
1694  *
1695  * This method may block indefinitely and should not be used in user
1696  * interface threads. The secret service may prompt the user.
1697  * secret_service_prompt() will be used to handle any prompts that show up.
1698  *
1699  * Returns: whether the collection was successfully deleted or not
1700  */
1701 gboolean
secret_collection_delete_sync(SecretCollection * self,GCancellable * cancellable,GError ** error)1702 secret_collection_delete_sync (SecretCollection *self,
1703                                GCancellable *cancellable,
1704                                GError **error)
1705 {
1706 	SecretSync *sync;
1707 	gboolean ret;
1708 
1709 	g_return_val_if_fail (SECRET_IS_COLLECTION (self), FALSE);
1710 	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
1711 	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1712 
1713 	sync = _secret_sync_new ();
1714 	g_main_context_push_thread_default (sync->context);
1715 
1716 	secret_collection_delete (self, cancellable, _secret_sync_on_result, sync);
1717 
1718 	g_main_loop_run (sync->loop);
1719 
1720 	ret = secret_collection_delete_finish (self, sync->result, error);
1721 
1722 	g_main_context_pop_thread_default (sync->context);
1723 	_secret_sync_free (sync);
1724 
1725 	return ret;
1726 }
1727 
1728 /**
1729  * secret_collection_get_service:
1730  * @self: a collection
1731  *
1732  * Get the Secret Service object that this collection was created with.
1733  *
1734  * Returns: (transfer none): the Secret Service object
1735  */
1736 SecretService *
secret_collection_get_service(SecretCollection * self)1737 secret_collection_get_service (SecretCollection *self)
1738 {
1739 	g_return_val_if_fail (SECRET_IS_COLLECTION (self), NULL);
1740 	return self->pv->service;
1741 }
1742 
1743 /**
1744  * secret_collection_get_flags:
1745  * @self: the secret collection proxy
1746  *
1747  * Get the flags representing what features of the #SecretCollection proxy
1748  * have been initialized.
1749  *
1750  * Use secret_collection_load_items()  to initialize further features
1751  * and change the flags.
1752  *
1753  * Returns: the flags for features initialized
1754  */
1755 SecretCollectionFlags
secret_collection_get_flags(SecretCollection * self)1756 secret_collection_get_flags (SecretCollection *self)
1757 {
1758 	SecretCollectionFlags flags = 0;
1759 
1760 	g_return_val_if_fail (SECRET_IS_COLLECTION (self), SECRET_COLLECTION_NONE);
1761 
1762 	g_mutex_lock (&self->pv->mutex);
1763 
1764 	if (self->pv->items)
1765 		flags |= SECRET_COLLECTION_LOAD_ITEMS;
1766 
1767 	g_mutex_unlock (&self->pv->mutex);
1768 
1769 	return flags;
1770 }
1771 
1772 /**
1773  * secret_collection_get_items:
1774  * @self: a collection
1775  *
1776  * Get the list of items in this collection.
1777  *
1778  * Returns: (transfer full) (element-type Secret.Item): a list of items,
1779  * when done, the list should be freed with g_list_free, and each item should
1780  * be released with g_object_unref()
1781  */
1782 GList *
secret_collection_get_items(SecretCollection * self)1783 secret_collection_get_items (SecretCollection *self)
1784 {
1785 	GList *l, *items = NULL;
1786 
1787 	g_return_val_if_fail (SECRET_IS_COLLECTION (self), NULL);
1788 
1789 	g_mutex_lock (&self->pv->mutex);
1790 	if (self->pv->items)
1791 		items = g_hash_table_get_values (self->pv->items);
1792 	for (l = items; l != NULL; l = g_list_next (l))
1793 		g_object_ref (l->data);
1794 	g_mutex_unlock (&self->pv->mutex);
1795 
1796 	return items;
1797 }
1798 
1799 SecretItem *
_secret_collection_find_item_instance(SecretCollection * self,const gchar * item_path)1800 _secret_collection_find_item_instance (SecretCollection *self,
1801                                        const gchar *item_path)
1802 {
1803 	SecretItem *item = NULL;
1804 
1805 	g_mutex_lock (&self->pv->mutex);
1806 	if (self->pv->items)
1807 		item = g_hash_table_lookup (self->pv->items, item_path);
1808 	if (item != NULL)
1809 		g_object_ref (item);
1810 	g_mutex_unlock (&self->pv->mutex);
1811 
1812 	return item;
1813 }
1814 
1815 /**
1816  * secret_collection_get_label:
1817  * @self: a collection
1818  *
1819  * Get the label of this collection.
1820  *
1821  * Returns: (transfer full): the label, which should be freed with g_free()
1822  */
1823 gchar *
secret_collection_get_label(SecretCollection * self)1824 secret_collection_get_label (SecretCollection *self)
1825 {
1826 	GVariant *variant;
1827 	gchar *label;
1828 
1829 	g_return_val_if_fail (SECRET_IS_COLLECTION (self), NULL);
1830 
1831 	variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Label");
1832 	g_return_val_if_fail (variant != NULL, NULL);
1833 
1834 	label = g_variant_dup_string (variant, NULL);
1835 	g_variant_unref (variant);
1836 
1837 	return label;
1838 }
1839 
1840 /**
1841  * secret_collection_set_label:
1842  * @self: a collection
1843  * @label: a new label
1844  * @cancellable: optional cancellation object
1845  * @callback: called when the operation completes
1846  * @user_data: data to pass to the callback
1847  *
1848  * Set the label of this collection.
1849  *
1850  * This function returns immediately and completes asynchronously.
1851  */
1852 void
secret_collection_set_label(SecretCollection * self,const gchar * label,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1853 secret_collection_set_label (SecretCollection *self,
1854                              const gchar *label,
1855                              GCancellable *cancellable,
1856                              GAsyncReadyCallback callback,
1857                              gpointer user_data)
1858 {
1859 	g_return_if_fail (SECRET_IS_COLLECTION (self));
1860 	g_return_if_fail (label != NULL);
1861 
1862 	_secret_util_set_property (G_DBUS_PROXY (self), "Label",
1863 	                           g_variant_new_string (label),
1864 	                           secret_collection_set_label,
1865 	                           cancellable, callback, user_data);
1866 }
1867 
1868 /**
1869  * secret_collection_set_label_finish:
1870  * @self: a collection
1871  * @result: asynchronous result passed to callback
1872  * @error: location to place error on failure
1873  *
1874  * Complete asynchronous operation to set the label of this collection.
1875  *
1876  * Returns: whether the change was successful or not
1877  */
1878 gboolean
secret_collection_set_label_finish(SecretCollection * self,GAsyncResult * result,GError ** error)1879 secret_collection_set_label_finish (SecretCollection *self,
1880                                     GAsyncResult *result,
1881                                     GError **error)
1882 {
1883 	g_return_val_if_fail (SECRET_IS_COLLECTION (self), FALSE);
1884 
1885 	return _secret_util_set_property_finish (G_DBUS_PROXY (self),
1886 	                                         secret_collection_set_label,
1887 	                                         result, error);
1888 }
1889 
1890 /**
1891  * secret_collection_set_label_sync:
1892  * @self: a collection
1893  * @label: a new label
1894  * @cancellable: optional cancellation object
1895  * @error: location to place error on failure
1896  *
1897  * Set the label of this collection.
1898  *
1899  * This function may block indefinitely. Use the asynchronous version
1900  * in user interface threads.
1901  *
1902  * Returns: whether the change was successful or not
1903  */
1904 gboolean
secret_collection_set_label_sync(SecretCollection * self,const gchar * label,GCancellable * cancellable,GError ** error)1905 secret_collection_set_label_sync (SecretCollection *self,
1906                                   const gchar *label,
1907                                   GCancellable *cancellable,
1908                                   GError **error)
1909 {
1910 	g_return_val_if_fail (SECRET_IS_COLLECTION (self), FALSE);
1911 	g_return_val_if_fail (label != NULL, FALSE);
1912 
1913 	return _secret_util_set_property_sync (G_DBUS_PROXY (self), "Label",
1914 	                                       g_variant_new_string (label),
1915 	                                       cancellable, error);
1916 }
1917 
1918 /**
1919  * secret_collection_get_locked:
1920  * @self: a collection
1921  *
1922  * Get whether the collection is locked or not.
1923  *
1924  * Use secret_service_lock() or secret_service_unlock() to lock or unlock the
1925  * collection.
1926  *
1927  * Returns: whether the collection is locked or not
1928  */
1929 gboolean
secret_collection_get_locked(SecretCollection * self)1930 secret_collection_get_locked (SecretCollection *self)
1931 {
1932 	GVariant *variant;
1933 	gboolean locked;
1934 
1935 	g_return_val_if_fail (SECRET_IS_COLLECTION (self), TRUE);
1936 
1937 	variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Locked");
1938 	g_return_val_if_fail (variant != NULL, TRUE);
1939 
1940 	locked = g_variant_get_boolean (variant);
1941 	g_variant_unref (variant);
1942 
1943 	return locked;
1944 }
1945 
1946 /**
1947  * secret_collection_get_created:
1948  * @self: a collection
1949  *
1950  * Get the created date and time of the collection. The return value is
1951  * the number of seconds since the unix epoch, January 1st 1970.
1952  *
1953  * Returns: the created date and time
1954  */
1955 guint64
secret_collection_get_created(SecretCollection * self)1956 secret_collection_get_created (SecretCollection *self)
1957 {
1958 	GVariant *variant;
1959 	guint64 created;
1960 
1961 	g_return_val_if_fail (SECRET_IS_COLLECTION (self), TRUE);
1962 
1963 	variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Created");
1964 	g_return_val_if_fail (variant != NULL, 0);
1965 
1966 	created = g_variant_get_uint64 (variant);
1967 	g_variant_unref (variant);
1968 
1969 	return created;
1970 }
1971 
1972 /**
1973  * secret_collection_get_modified:
1974  * @self: a collection
1975  *
1976  * Get the modified date and time of the collection. The return value is
1977  * the number of seconds since the unix epoch, January 1st 1970.
1978  *
1979  * Returns: the modified date and time
1980  */
1981 guint64
secret_collection_get_modified(SecretCollection * self)1982 secret_collection_get_modified (SecretCollection *self)
1983 {
1984 	GVariant *variant;
1985 	guint64 modified;
1986 
1987 	g_return_val_if_fail (SECRET_IS_COLLECTION (self), TRUE);
1988 
1989 	variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self), "Modified");
1990 	g_return_val_if_fail (variant != NULL, 0);
1991 
1992 	modified = g_variant_get_uint64 (variant);
1993 	g_variant_unref (variant);
1994 
1995 	return modified;
1996 }
1997 
1998 
1999 typedef struct {
2000 	GCancellable *cancellable;
2001 	gchar *alias;
2002 	SecretCollectionFlags flags;
2003 	SecretCollection *collection;
2004 } ReadClosure;
2005 
2006 static void
read_closure_free(gpointer data)2007 read_closure_free (gpointer data)
2008 {
2009 	ReadClosure *read = data;
2010 	g_free (read->alias);
2011 	if (read->collection)
2012 		g_object_unref (read->collection);
2013 	if (read->cancellable)
2014 		g_object_unref (read->cancellable);
2015 	g_slice_free (ReadClosure, read);
2016 }
2017 
2018 static void
on_read_alias_collection(GObject * source,GAsyncResult * result,gpointer user_data)2019 on_read_alias_collection (GObject *source,
2020                           GAsyncResult *result,
2021                           gpointer user_data)
2022 {
2023 	GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
2024 	ReadClosure *read = g_simple_async_result_get_op_res_gpointer (async);
2025 	GError *error = NULL;
2026 
2027 	read->collection = secret_collection_new_for_dbus_path_finish (result, &error);
2028 	if (error != NULL)
2029 		g_simple_async_result_take_error (async, error);
2030 
2031 	g_simple_async_result_complete (async);
2032 	g_object_unref (async);
2033 }
2034 
2035 static void
on_read_alias_path(GObject * source,GAsyncResult * result,gpointer user_data)2036 on_read_alias_path (GObject *source,
2037                     GAsyncResult *result,
2038                     gpointer user_data)
2039 {
2040 	GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
2041 	ReadClosure *read = g_simple_async_result_get_op_res_gpointer (async);
2042 	SecretService *self = SECRET_SERVICE (source);
2043 	GError *error = NULL;
2044 	gchar *collection_path;
2045 
2046 	collection_path = secret_service_read_alias_dbus_path_finish (self, result, &error);
2047 	if (error == NULL) {
2048 
2049 		/* No collection for this alias */
2050 		if (collection_path == NULL) {
2051 			g_simple_async_result_complete (async);
2052 
2053 		} else {
2054 			read->collection = _secret_service_find_collection_instance (self,
2055 			                                                             collection_path);
2056 			if (read->collection != NULL) {
2057 
2058 				/* Make sure collection has necessary flags */
2059 				collection_ensure_for_flags_async (read->collection, read->flags,
2060 				                                   read->cancellable, async);
2061 
2062 			/* No collection loaded, but valid path, load */
2063 			} else {
2064 				secret_collection_new_for_dbus_path (self, collection_path,
2065 				                                     read->flags,
2066 				                                     read->cancellable,
2067 				                                     on_read_alias_collection,
2068 				                                     g_object_ref (async));
2069 			}
2070 		}
2071 
2072 	} else {
2073 		g_simple_async_result_take_error (async, error);
2074 		g_simple_async_result_complete (async);
2075 	}
2076 
2077 	g_free (collection_path);
2078 	g_object_unref (async);
2079 }
2080 
2081 static void
on_read_alias_service(GObject * source,GAsyncResult * result,gpointer user_data)2082 on_read_alias_service (GObject *source,
2083                        GAsyncResult *result,
2084                        gpointer user_data)
2085 {
2086 	GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
2087 	ReadClosure *read = g_simple_async_result_get_op_res_gpointer (async);
2088 	SecretService *service;
2089 	GError *error = NULL;
2090 
2091 	service = secret_service_get_finish (result, &error);
2092 	if (error == NULL) {
2093 		secret_service_read_alias_dbus_path (service, read->alias, read->cancellable,
2094 		                                     on_read_alias_path, g_object_ref (async));
2095 		g_object_unref (service);
2096 
2097 	} else {
2098 		g_simple_async_result_take_error (async, error);
2099 		g_simple_async_result_complete (async);
2100 	}
2101 
2102 	g_object_unref (async);
2103 }
2104 
2105 /**
2106  * secret_collection_for_alias:
2107  * @service: (allow-none): a secret service object
2108  * @alias: the alias to lookup
2109  * @flags: options for the collection initialization
2110  * @cancellable: (allow-none): optional cancellation object
2111  * @callback: called when the operation completes
2112  * @user_data: data to pass to the callback
2113  *
2114  * Lookup which collection is assigned to this alias. Aliases help determine
2115  * well known collections, such as 'default'.
2116  *
2117  * If @service is NULL, then secret_service_get() will be called to get
2118  * the default #SecretService proxy.
2119  *
2120  * This method will return immediately and complete asynchronously.
2121  */
2122 void
secret_collection_for_alias(SecretService * service,const gchar * alias,SecretCollectionFlags flags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2123 secret_collection_for_alias (SecretService *service,
2124                              const gchar *alias,
2125                              SecretCollectionFlags flags,
2126                              GCancellable *cancellable,
2127                              GAsyncReadyCallback callback,
2128                              gpointer user_data)
2129 {
2130 	GSimpleAsyncResult *async;
2131 	ReadClosure *read;
2132 
2133 	g_return_if_fail (service == NULL || SECRET_IS_SERVICE (service));
2134 	g_return_if_fail (alias != NULL);
2135 	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
2136 
2137 	async = g_simple_async_result_new (NULL, callback, user_data,
2138 	                                   secret_collection_for_alias);
2139 	read = g_slice_new0 (ReadClosure);
2140 	read->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
2141 	read->alias = g_strdup (alias);
2142 	read->flags = flags;
2143 	g_simple_async_result_set_op_res_gpointer (async, read, read_closure_free);
2144 
2145 	if (service == NULL) {
2146 		secret_service_get (SECRET_SERVICE_NONE, cancellable,
2147 		                    on_read_alias_service, g_object_ref (async));
2148 	} else {
2149 		secret_service_read_alias_dbus_path (service, read->alias, read->cancellable,
2150 		                                     on_read_alias_path, g_object_ref (async));
2151 	}
2152 
2153 	g_object_unref (async);
2154 }
2155 
2156 /**
2157  * secret_collection_for_alias_finish:
2158  * @result: asynchronous result passed to callback
2159  * @error: location to place error on failure
2160  *
2161  * Finish an asynchronous operation to lookup which collection is assigned
2162  * to an alias.
2163  *
2164  * Returns: (transfer full): the collection, or %NULL if none assigned to the alias
2165  */
2166 SecretCollection *
secret_collection_for_alias_finish(GAsyncResult * result,GError ** error)2167 secret_collection_for_alias_finish (GAsyncResult *result,
2168                                     GError **error)
2169 {
2170 	GSimpleAsyncResult *async;
2171 	ReadClosure *read;
2172 
2173 	g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL,
2174 	                      secret_collection_for_alias), NULL);
2175 	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2176 
2177 	async = G_SIMPLE_ASYNC_RESULT (result);
2178 	if (_secret_util_propagate_error (async, error))
2179 		return NULL;
2180 	read = g_simple_async_result_get_op_res_gpointer (async);
2181 	if (read->collection)
2182 		g_object_ref (read->collection);
2183 	return read->collection;
2184 }
2185 
2186 /**
2187  * secret_collection_for_alias_sync:
2188  * @service: (allow-none): a secret service object
2189  * @alias: the alias to lookup
2190  * @flags: options for the collection initialization
2191  * @cancellable: (allow-none): optional cancellation object
2192  * @error: location to place error on failure
2193  *
2194  * Lookup which collection is assigned to this alias. Aliases help determine
2195  * well known collections, such as 'default'.
2196  *
2197  * If @service is NULL, then secret_service_get_sync() will be called to get
2198  * the default #SecretService proxy.
2199  *
2200  * This method may block and should not be used in user interface threads.
2201  *
2202  * Returns: (transfer full): the collection, or %NULL if none assigned to the alias
2203  */
2204 SecretCollection *
secret_collection_for_alias_sync(SecretService * service,const gchar * alias,SecretCollectionFlags flags,GCancellable * cancellable,GError ** error)2205 secret_collection_for_alias_sync (SecretService *service,
2206                                   const gchar *alias,
2207                                   SecretCollectionFlags flags,
2208                                   GCancellable *cancellable,
2209                                   GError **error)
2210 {
2211 	SecretCollection *collection;
2212 	gchar *collection_path;
2213 
2214 	g_return_val_if_fail (service == NULL || SECRET_IS_SERVICE (service), NULL);
2215 	g_return_val_if_fail (alias != NULL, NULL);
2216 	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
2217 	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2218 
2219 	collection_path = secret_service_read_alias_dbus_path_sync (service, alias,
2220 	                                                            cancellable, error);
2221 	 /* No collection for this alias */
2222 	if (collection_path == NULL)
2223 		return NULL;
2224 
2225 	collection = _secret_service_find_collection_instance (service,
2226 	                                                       collection_path);
2227 
2228 	if (collection != NULL) {
2229 
2230 		/* Have a collection with all necessary flags */
2231 		if (!collection_ensure_for_flags_sync (collection, flags,
2232 		                                       cancellable, error)) {
2233 			g_object_unref (collection);
2234 			collection = NULL;
2235 		}
2236 
2237 	/* No collection loaded, but valid path, load */
2238 	} else {
2239 		collection = secret_collection_new_for_dbus_path_sync (service, collection_path,
2240 		                                                       flags, cancellable, error);
2241 	}
2242 
2243 	g_free (collection_path);
2244 	return collection;
2245 }
2246