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