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