Use upstream tag
[platform/upstream/libsecret.git] / libsecret / secret-util.c
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
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 *
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
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
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
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
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_message ("Remote error from secret service: %s: %s",
129                                    remote, (*error)->message);
130                 }
131                 g_free (remote);
132         }
133 }
134
135 gchar *
136 _secret_util_parent_path (const gchar *path)
137 {
138         const gchar *pos;
139
140         g_return_val_if_fail (path != NULL, NULL);
141
142         pos = strrchr (path, '/');
143         g_return_val_if_fail (pos != NULL, NULL);
144         g_return_val_if_fail (pos != path, NULL);
145
146         return g_strndup (path, pos - path);
147 }
148
149 gboolean
150 _secret_util_empty_path (const gchar *path)
151 {
152         g_return_val_if_fail (path != NULL, TRUE);
153         return (g_str_equal (path, "") || g_str_equal (path, "/"));
154 }
155
156 gchar *
157 _secret_util_collection_to_path (const gchar *collection)
158 {
159         if (collection == NULL)
160                 collection = SECRET_COLLECTION_DEFAULT;
161         if (strchr (collection, '/') == NULL)
162                 return g_strdup_printf ("/org/freedesktop/secrets/aliases/%s", collection);
163         return g_strdup (collection);
164 }
165
166 GVariant *
167 _secret_util_variant_for_properties (GHashTable *properties)
168 {
169         GHashTableIter iter;
170         GVariantBuilder builder;
171         const gchar *name;
172         GVariant *value;
173
174         g_return_val_if_fail (properties != NULL, NULL);
175
176         g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
177
178         g_hash_table_iter_init (&iter, properties);
179         while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&value))
180                 g_variant_builder_add (&builder, "{sv}", name, value);
181
182         return g_variant_builder_end (&builder);
183 }
184
185 static void
186 process_get_all_reply (GDBusProxy *proxy,
187                        GVariant *retval)
188 {
189         const gchar *invalidated_properties[1] = { NULL };
190         GVariant *changed_properties;
191         GVariantIter *iter;
192         GVariant *value;
193         gchar *key;
194
195         if (!g_variant_is_of_type (retval, G_VARIANT_TYPE ("(a{sv})"))) {
196                 g_warning ("Value for GetAll reply with type `%s' does not match `(a{sv})'",
197                            g_variant_get_type_string (retval));
198                 return;
199         }
200
201         g_variant_get (retval, "(a{sv})", &iter);
202         while (g_variant_iter_loop (iter, "{sv}", &key, &value))
203                 g_dbus_proxy_set_cached_property (proxy, key, value);
204         g_variant_iter_free (iter);
205
206         g_variant_get (retval, "(@a{sv})", &changed_properties);
207         g_signal_emit_by_name (proxy, "g-properties-changed",
208                                changed_properties, invalidated_properties);
209         g_variant_unref (changed_properties);
210 }
211
212 static void
213 on_get_properties (GObject *source,
214                    GAsyncResult *result,
215                    gpointer user_data)
216 {
217         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
218         GDBusProxy *proxy = G_DBUS_PROXY (g_async_result_get_source_object (user_data));
219         GError *error = NULL;
220         GVariant *retval;
221
222         retval = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
223
224         if (error == NULL) {
225                 process_get_all_reply (proxy, retval);
226         } else {
227                 g_simple_async_result_take_error (res, error);
228         }
229         if (retval != NULL)
230                 g_variant_unref (retval);
231
232         g_simple_async_result_complete (res);
233         g_object_unref (proxy);
234         g_object_unref (res);
235 }
236
237 void
238 _secret_util_get_properties (GDBusProxy *proxy,
239                              gpointer result_tag,
240                              GCancellable *cancellable,
241                              GAsyncReadyCallback callback,
242                              gpointer user_data)
243 {
244         GSimpleAsyncResult *res;
245
246         g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
247
248         res = g_simple_async_result_new (G_OBJECT (proxy), callback, user_data, result_tag);
249
250         g_dbus_connection_call (g_dbus_proxy_get_connection (proxy),
251                                 g_dbus_proxy_get_name (proxy),
252                                 g_dbus_proxy_get_object_path (proxy),
253                                 "org.freedesktop.DBus.Properties", "GetAll",
254                                 g_variant_new ("(s)", g_dbus_proxy_get_interface_name (proxy)),
255                                 G_VARIANT_TYPE ("(a{sv})"),
256                                 G_DBUS_CALL_FLAGS_NONE, -1,
257                                 cancellable, on_get_properties,
258                                 g_object_ref (res));
259
260         g_object_unref (res);
261 }
262
263 gboolean
264 _secret_util_get_properties_finish (GDBusProxy *proxy,
265                                     gpointer result_tag,
266                                     GAsyncResult *result,
267                                     GError **error)
268 {
269         GSimpleAsyncResult *res;
270
271         g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (proxy), result_tag), FALSE);
272         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
273
274         res = G_SIMPLE_ASYNC_RESULT (result);
275
276         if (_secret_util_propagate_error (res, error))
277                 return FALSE;
278
279         return TRUE;
280 }
281
282 typedef struct {
283         gchar *property;
284         GVariant *value;
285         gboolean result;
286 } SetClosure;
287
288 static void
289 set_closure_free (gpointer data)
290 {
291         SetClosure *closure = data;
292         g_free (closure->property);
293         g_variant_unref (closure->value);
294         g_slice_free (SetClosure, closure);
295 }
296
297 static void
298 on_set_property (GObject *source,
299                  GAsyncResult *result,
300                  gpointer user_data)
301 {
302         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
303         SetClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
304         GDBusProxy *proxy = G_DBUS_PROXY (g_async_result_get_source_object (user_data));
305         GError *error = NULL;
306         GVariant *retval;
307
308         retval = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source),
309                                                 result, &error);
310         if (error != NULL) {
311                 g_simple_async_result_take_error (res, error);
312         }
313         if (retval != NULL)
314                 g_variant_unref (retval);
315
316         closure->result = retval != NULL;
317         if (closure->result)
318                 g_dbus_proxy_set_cached_property (proxy, closure->property, closure->value);
319
320         g_simple_async_result_complete (res);
321         g_object_unref (proxy);
322         g_object_unref (res);
323 }
324
325 void
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         GSimpleAsyncResult *res;
335         SetClosure *closure;
336
337         g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
338
339         res = g_simple_async_result_new (G_OBJECT (proxy), callback, user_data, result_tag);
340         closure = g_slice_new0 (SetClosure);
341         closure->property = g_strdup (property);
342         closure->value = g_variant_ref_sink (value);
343         g_simple_async_result_set_op_res_gpointer (res, closure, set_closure_free);
344
345         g_dbus_connection_call (g_dbus_proxy_get_connection (proxy),
346                                 g_dbus_proxy_get_name (proxy),
347                                 g_dbus_proxy_get_object_path (proxy),
348                                 SECRET_PROPERTIES_INTERFACE,
349                                 "Set",
350                                 g_variant_new ("(ssv)",
351                                                g_dbus_proxy_get_interface_name (proxy),
352                                                property,
353                                                closure->value),
354                                 G_VARIANT_TYPE ("()"),
355                                 G_DBUS_CALL_FLAGS_NO_AUTO_START, -1,
356                                 cancellable, on_set_property,
357                                 g_object_ref (res));
358
359         g_object_unref (res);
360 }
361
362 gboolean
363 _secret_util_set_property_finish (GDBusProxy *proxy,
364                                   gpointer result_tag,
365                                   GAsyncResult *result,
366                                   GError **error)
367 {
368         GSimpleAsyncResult *res;
369         SetClosure *closure;
370
371         g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (proxy), result_tag), FALSE);
372         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
373
374         res = G_SIMPLE_ASYNC_RESULT (result);
375
376         if (_secret_util_propagate_error (res, error))
377                 return FALSE;
378
379         closure = g_simple_async_result_get_op_res_gpointer (res);
380         return closure->result;
381 }
382
383 gboolean
384 _secret_util_set_property_sync (GDBusProxy *proxy,
385                                 const gchar *property,
386                                 GVariant *value,
387                                 GCancellable *cancellable,
388                                 GError **error)
389 {
390         gboolean result = FALSE;
391         GVariant *retval;
392
393         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
394         g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
395
396         g_variant_ref_sink (value);
397
398         retval = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (proxy),
399                                               g_dbus_proxy_get_name (proxy),
400                                               g_dbus_proxy_get_object_path (proxy),
401                                               SECRET_PROPERTIES_INTERFACE,
402                                               "Set",
403                                               g_variant_new ("(ssv)",
404                                                              g_dbus_proxy_get_interface_name (proxy),
405                                                              property,
406                                                              value),
407                                               G_VARIANT_TYPE ("()"),
408                                               G_DBUS_CALL_FLAGS_NO_AUTO_START, -1,
409                                               cancellable, error);
410
411         if (retval != NULL) {
412                 result = TRUE;
413                 g_variant_unref (retval);
414                 g_dbus_proxy_set_cached_property (proxy, property, value);
415         }
416
417         g_variant_unref (value);
418
419         return result;
420 }
421
422 gboolean
423 _secret_util_have_cached_properties (GDBusProxy *proxy)
424 {
425         gchar **names;
426
427         names = g_dbus_proxy_get_cached_property_names (proxy);
428         g_strfreev (names);
429
430         return names != NULL;
431 }
432
433 SecretSync *
434 _secret_sync_new (void)
435 {
436         SecretSync *sync;
437
438         sync = g_new0 (SecretSync, 1);
439
440         sync->context = g_main_context_new ();
441         sync->loop = g_main_loop_new (sync->context, FALSE);
442
443         return sync;
444 }
445
446 void
447 _secret_sync_free (gpointer data)
448 {
449         SecretSync *sync = data;
450
451         while (g_main_context_iteration (sync->context, FALSE));
452
453         g_clear_object (&sync->result);
454         g_main_loop_unref (sync->loop);
455         g_main_context_unref (sync->context);
456 }
457
458 void
459 _secret_sync_on_result (GObject *source,
460                         GAsyncResult *result,
461                         gpointer user_data)
462 {
463         SecretSync *sync = user_data;
464         g_assert (sync->result == NULL);
465         sync->result = g_object_ref (result);
466         g_main_loop_quit (sync->loop);
467 }