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