1 /* libsecret - GLib wrapper for Secret Service
2 *
3 * Copyright 2011 Collabora Ltd.
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-private.h"
18 #include "secret-types.h"
19
20 #include <string.h>
21
22 /**
23 * SECTION:secret-error
24 * @title: SecretError
25 * @short_description: libsecret errors
26 *
27 * Various errors reported by the libsecret library. No error returned from
28 * the libsecret API is suitable for direct display to the user. It is up
29 * to the application to handle them appropriately.
30 *
31 * Stability: Stable
32 */
33
34 /**
35 * SECRET_ERROR:
36 *
37 * The error domain quark which denotes libsecret specific errors from the
38 * #SecretError enumeration.
39 */
40
41 /**
42 * SecretError:
43 * @SECRET_ERROR_PROTOCOL: received an invalid data or message from the Secret
44 * Service
45 * @SECRET_ERROR_IS_LOCKED: the item or collection is locked and the operation
46 * cannot be performed
47 * @SECRET_ERROR_NO_SUCH_OBJECT: no such item or collection found in the
48 * Secret Service
49 * @SECRET_ERROR_ALREADY_EXISTS: a relevant item or collection already exists
50 *
51 * Errors returned by the Secret Service. None of the errors are appropriate
52 * for display to the user.
53 */
54
55 static void
list_unref_free(GList * reflist)56 list_unref_free (GList *reflist)
57 {
58 GList *l;
59 for (l = reflist; l; l = g_list_next (l)) {
60 g_return_if_fail (G_IS_OBJECT (l->data));
61 g_object_unref (l->data);
62 }
63 g_list_free (reflist);
64 }
65
66 static GList *
list_ref_copy(GList * reflist)67 list_ref_copy (GList *reflist)
68 {
69 GList *l, *copy = g_list_copy (reflist);
70 for (l = copy; l; l = g_list_next (l)) {
71 g_return_val_if_fail (G_IS_OBJECT (l->data), NULL);
72 g_object_ref (l->data);
73 }
74 return copy;
75 }
76
77 GType
_secret_list_get_type(void)78 _secret_list_get_type (void)
79 {
80 static GType type = 0;
81 if (!type)
82 type = g_boxed_type_register_static ("SecretObjectList",
83 (GBoxedCopyFunc)list_ref_copy,
84 (GBoxedFreeFunc)list_unref_free);
85 return type;
86
87 }
88
89 GQuark
secret_error_get_quark(void)90 secret_error_get_quark (void)
91 {
92 static volatile gsize quark = 0;
93
94 static const GDBusErrorEntry entries[] = {
95 { SECRET_ERROR_IS_LOCKED, "org.freedesktop.Secret.Error.IsLocked", },
96 { SECRET_ERROR_NO_SUCH_OBJECT, "org.freedesktop.Secret.Error.NoSuchObject", },
97 { SECRET_ERROR_ALREADY_EXISTS, "org.freedesktop.Secret.Error.AlreadyExists" },
98 };
99
100 g_dbus_error_register_error_domain ("secret-error", &quark,
101 entries, G_N_ELEMENTS (entries));
102
103 return quark;
104 }
105
106 gboolean
_secret_util_propagate_error(GSimpleAsyncResult * async,GError ** error)107 _secret_util_propagate_error (GSimpleAsyncResult *async,
108 GError **error)
109 {
110 if (!g_simple_async_result_propagate_error (async, error))
111 return FALSE;
112
113 _secret_util_strip_remote_error (error);
114 return TRUE;
115 }
116
117 void
_secret_util_strip_remote_error(GError ** error)118 _secret_util_strip_remote_error (GError **error)
119 {
120 gchar *remote;
121
122 if (error == NULL || *error == NULL)
123 return;
124
125 remote = g_dbus_error_get_remote_error (*error);
126 if (remote) {
127 if (g_dbus_error_strip_remote_error (*error)) {
128 g_info ("Remote error from secret service: %s: %s", remote, (*error)->message);
129 }
130 g_free (remote);
131 }
132 }
133
134 gchar *
_secret_util_parent_path(const gchar * path)135 _secret_util_parent_path (const gchar *path)
136 {
137 const gchar *pos;
138
139 g_return_val_if_fail (path != NULL, NULL);
140
141 pos = strrchr (path, '/');
142 g_return_val_if_fail (pos != NULL, NULL);
143 g_return_val_if_fail (pos != path, NULL);
144
145 return g_strndup (path, pos - path);
146 }
147
148 gboolean
_secret_util_empty_path(const gchar * path)149 _secret_util_empty_path (const gchar *path)
150 {
151 g_return_val_if_fail (path != NULL, TRUE);
152 return (g_str_equal (path, "") || g_str_equal (path, "/"));
153 }
154
155 gchar *
_secret_util_collection_to_path(const gchar * collection)156 _secret_util_collection_to_path (const gchar *collection)
157 {
158 if (collection == NULL)
159 collection = SECRET_COLLECTION_DEFAULT;
160 if (strchr (collection, '/') == NULL)
161 return g_strdup_printf ("/org/freedesktop/secrets/aliases/%s", collection);
162 return g_strdup (collection);
163 }
164
165 GVariant *
_secret_util_variant_for_properties(GHashTable * properties)166 _secret_util_variant_for_properties (GHashTable *properties)
167 {
168 GHashTableIter iter;
169 GVariantBuilder builder;
170 const gchar *name;
171 GVariant *value;
172
173 g_return_val_if_fail (properties != NULL, NULL);
174
175 g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
176
177 g_hash_table_iter_init (&iter, properties);
178 while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&value))
179 g_variant_builder_add (&builder, "{sv}", name, value);
180
181 return g_variant_builder_end (&builder);
182 }
183
184 static void
process_get_all_reply(GDBusProxy * proxy,GVariant * retval)185 process_get_all_reply (GDBusProxy *proxy,
186 GVariant *retval)
187 {
188 const gchar *invalidated_properties[1] = { NULL };
189 GVariant *changed_properties;
190 GVariantIter *iter;
191 GVariant *value;
192 gchar *key;
193
194 if (!g_variant_is_of_type (retval, G_VARIANT_TYPE ("(a{sv})"))) {
195 g_warning ("Value for GetAll reply with type `%s' does not match `(a{sv})'",
196 g_variant_get_type_string (retval));
197 return;
198 }
199
200 g_variant_get (retval, "(a{sv})", &iter);
201 while (g_variant_iter_loop (iter, "{sv}", &key, &value))
202 g_dbus_proxy_set_cached_property (proxy, key, value);
203 g_variant_iter_free (iter);
204
205 g_variant_get (retval, "(@a{sv})", &changed_properties);
206 g_signal_emit_by_name (proxy, "g-properties-changed",
207 changed_properties, invalidated_properties);
208 g_variant_unref (changed_properties);
209 }
210
211 static void
on_get_properties(GObject * source,GAsyncResult * result,gpointer user_data)212 on_get_properties (GObject *source,
213 GAsyncResult *result,
214 gpointer user_data)
215 {
216 GTask *task = G_TASK (user_data);
217 GDBusProxy *proxy = G_DBUS_PROXY (g_task_get_source_object (task));
218 GError *error = NULL;
219 GVariant *retval;
220
221 retval = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
222
223 if (error == NULL) {
224 process_get_all_reply (proxy, retval);
225 g_task_return_boolean (task, TRUE);
226 } else {
227 g_task_return_error (task, g_steal_pointer (&error));
228 }
229 if (retval != NULL)
230 g_variant_unref (retval);
231
232 g_clear_object (&task);
233 }
234
235 void
_secret_util_get_properties(GDBusProxy * proxy,gpointer result_tag,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)236 _secret_util_get_properties (GDBusProxy *proxy,
237 gpointer result_tag,
238 GCancellable *cancellable,
239 GAsyncReadyCallback callback,
240 gpointer user_data)
241 {
242 GTask *task;
243
244 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
245
246 task = g_task_new (proxy, cancellable, callback, user_data);
247 g_task_set_source_tag (task, result_tag);
248
249 g_dbus_connection_call (g_dbus_proxy_get_connection (proxy),
250 g_dbus_proxy_get_name (proxy),
251 g_dbus_proxy_get_object_path (proxy),
252 "org.freedesktop.DBus.Properties", "GetAll",
253 g_variant_new ("(s)", g_dbus_proxy_get_interface_name (proxy)),
254 G_VARIANT_TYPE ("(a{sv})"),
255 G_DBUS_CALL_FLAGS_NONE, -1,
256 cancellable, on_get_properties,
257 g_steal_pointer (&task));
258
259 g_clear_object (&task);
260 }
261
262 gboolean
_secret_util_get_properties_finish(GDBusProxy * proxy,gpointer result_tag,GAsyncResult * result,GError ** error)263 _secret_util_get_properties_finish (GDBusProxy *proxy,
264 gpointer result_tag,
265 GAsyncResult *result,
266 GError **error)
267 {
268 g_return_val_if_fail (g_task_is_valid (result, proxy), FALSE);
269 g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == result_tag, FALSE);
270 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
271
272 if (!g_task_propagate_boolean (G_TASK (result), error)) {
273 _secret_util_strip_remote_error (error);
274 return FALSE;
275 }
276
277 return TRUE;
278 }
279
280 typedef struct {
281 gchar *property;
282 GVariant *value;
283 gboolean result;
284 } SetClosure;
285
286 static void
set_closure_free(gpointer data)287 set_closure_free (gpointer data)
288 {
289 SetClosure *closure = data;
290 g_free (closure->property);
291 g_variant_unref (closure->value);
292 g_slice_free (SetClosure, closure);
293 }
294
295 static void
on_set_property(GObject * source,GAsyncResult * result,gpointer user_data)296 on_set_property (GObject *source,
297 GAsyncResult *result,
298 gpointer user_data)
299 {
300 GTask *task = G_TASK (user_data);
301 SetClosure *closure = g_task_get_task_data (task);
302 GDBusProxy *proxy = G_DBUS_PROXY (g_task_get_source_object (user_data));
303 GError *error = NULL;
304 GVariant *retval;
305 gboolean success = FALSE;
306
307 retval = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source),
308 result, &error);
309 if (error != NULL) {
310 g_task_return_error (task, g_steal_pointer (&error));
311 } else {
312 success = (retval != NULL);
313
314 if (success) {
315 g_dbus_proxy_set_cached_property (proxy, closure->property, closure->value);
316 g_variant_unref (retval);
317 }
318
319 g_task_return_boolean (task, success);
320 }
321
322 g_clear_object (&task);
323 }
324
325 void
_secret_util_set_property(GDBusProxy * proxy,const gchar * property,GVariant * value,gpointer result_tag,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)326 _secret_util_set_property (GDBusProxy *proxy,
327 const gchar *property,
328 GVariant *value,
329 gpointer result_tag,
330 GCancellable *cancellable,
331 GAsyncReadyCallback callback,
332 gpointer user_data)
333 {
334 GTask *task;
335 SetClosure *closure;
336
337 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
338
339 task = g_task_new (proxy, cancellable, callback, user_data);
340 g_task_set_source_tag (task, result_tag);
341 closure = g_slice_new0 (SetClosure);
342 closure->property = g_strdup (property);
343 closure->value = g_variant_ref_sink (value);
344 g_task_set_task_data (task, closure, set_closure_free);
345
346 g_dbus_connection_call (g_dbus_proxy_get_connection (proxy),
347 g_dbus_proxy_get_name (proxy),
348 g_dbus_proxy_get_object_path (proxy),
349 SECRET_PROPERTIES_INTERFACE,
350 "Set",
351 g_variant_new ("(ssv)",
352 g_dbus_proxy_get_interface_name (proxy),
353 property,
354 closure->value),
355 G_VARIANT_TYPE ("()"),
356 G_DBUS_CALL_FLAGS_NO_AUTO_START, -1,
357 cancellable, on_set_property,
358 g_steal_pointer (&task));
359
360 g_clear_object (&task);
361 }
362
363 gboolean
_secret_util_set_property_finish(GDBusProxy * proxy,gpointer result_tag,GAsyncResult * result,GError ** error)364 _secret_util_set_property_finish (GDBusProxy *proxy,
365 gpointer result_tag,
366 GAsyncResult *result,
367 GError **error)
368 {
369 g_return_val_if_fail (g_task_is_valid (result, proxy), FALSE);
370 g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == result_tag, FALSE);
371 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
372
373 if (!g_task_propagate_boolean (G_TASK (result), error)) {
374 _secret_util_strip_remote_error (error);
375 return FALSE;
376 }
377
378 return TRUE;
379 }
380
381 gboolean
_secret_util_set_property_sync(GDBusProxy * proxy,const gchar * property,GVariant * value,GCancellable * cancellable,GError ** error)382 _secret_util_set_property_sync (GDBusProxy *proxy,
383 const gchar *property,
384 GVariant *value,
385 GCancellable *cancellable,
386 GError **error)
387 {
388 gboolean result = FALSE;
389 GVariant *retval;
390
391 g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
392 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
393
394 g_variant_ref_sink (value);
395
396 retval = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (proxy),
397 g_dbus_proxy_get_name (proxy),
398 g_dbus_proxy_get_object_path (proxy),
399 SECRET_PROPERTIES_INTERFACE,
400 "Set",
401 g_variant_new ("(ssv)",
402 g_dbus_proxy_get_interface_name (proxy),
403 property,
404 value),
405 G_VARIANT_TYPE ("()"),
406 G_DBUS_CALL_FLAGS_NO_AUTO_START, -1,
407 cancellable, error);
408
409 if (retval != NULL) {
410 result = TRUE;
411 g_variant_unref (retval);
412 g_dbus_proxy_set_cached_property (proxy, property, value);
413 }
414
415 g_variant_unref (value);
416
417 return result;
418 }
419
420 gboolean
_secret_util_have_cached_properties(GDBusProxy * proxy)421 _secret_util_have_cached_properties (GDBusProxy *proxy)
422 {
423 gchar **names;
424
425 names = g_dbus_proxy_get_cached_property_names (proxy);
426 g_strfreev (names);
427
428 return names != NULL;
429 }
430
431 SecretSync *
_secret_sync_new(void)432 _secret_sync_new (void)
433 {
434 SecretSync *sync;
435
436 sync = g_new0 (SecretSync, 1);
437
438 sync->context = g_main_context_new ();
439 sync->loop = g_main_loop_new (sync->context, FALSE);
440
441 return sync;
442 }
443
444 void
_secret_sync_free(gpointer data)445 _secret_sync_free (gpointer data)
446 {
447 SecretSync *sync = data;
448
449 while (g_main_context_iteration (sync->context, FALSE));
450
451 g_clear_object (&sync->result);
452 g_main_loop_unref (sync->loop);
453 g_main_context_unref (sync->context);
454 g_free (sync);
455 }
456
457 void
_secret_sync_on_result(GObject * source,GAsyncResult * result,gpointer user_data)458 _secret_sync_on_result (GObject *source,
459 GAsyncResult *result,
460 gpointer user_data)
461 {
462 SecretSync *sync = user_data;
463 g_assert (sync->result == NULL);
464 sync->result = g_object_ref (result);
465 g_main_loop_quit (sync->loop);
466 }
467