secret-item.c 60.9 KB
Newer Older
Stef Walter's avatar
Stef Walter committed
1
/* libsecret - GLib wrapper for Secret Service
2
 *
3
 * Copyright 2012 Red Hat Inc.
4
5
6
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
Stef Walter's avatar
Stef Walter committed
7
 * by the Free Software Foundation; either version 2.1 of the licence or (at
8
9
10
 * your option) any later version.
 *
 * See the included COPYING file for more information.
Stef Walter's avatar
Stef Walter committed
11
12
 *
 * Author: Stef Walter <stefw@gnome.org>
13
14
15
16
 */

#include "config.h"

Stef Walter's avatar
Stef Walter committed
17
18
#include "secret-collection.h"
#include "secret-dbus-generated.h"
19
#include "secret-enum-types.h"
Stef Walter's avatar
Stef Walter committed
20
#include "secret-item.h"
21
#include "secret-paths.h"
Stef Walter's avatar
Stef Walter committed
22
23
24
25
#include "secret-private.h"
#include "secret-service.h"
#include "secret-types.h"
#include "secret-value.h"
26
27
28

#include <glib/gi18n-lib.h>

Stef Walter's avatar
Stef Walter committed
29
30
31
32
33
34
35
36
/**
 * SECTION:secret-item
 * @title: SecretItem
 * @short_description: A secret item
 *
 * #SecretItem represents a secret item stored in the Secret Service.
 *
 * Each item has a value, represented by a #SecretValue, which can be
Stef Walter's avatar
Stef Walter committed
37
 * retrieved by secret_item_get_secret() or set by secret_item_set_secret().
Stef Walter's avatar
Stef Walter committed
38
39
40
41
42
43
44
45
46
47
 * The item is only available when the item is not locked.
 *
 * Items can be locked or unlocked using the secret_service_lock() or
 * secret_service_unlock() functions. The Secret Service may not be able to
 * unlock individual items, and may unlock an entire collection when a single
 * item is unlocked.
 *
 * Each item has a set of attributes, which are used to locate the item later.
 * These are not stored or transferred in a secure manner. Each attribute has
 * a string name and a string value. Use secret_service_search() to search for
48
 * items based on their attributes, and secret_item_set_attributes() to change
Stef Walter's avatar
Stef Walter committed
49
50
51
 * the attributes associated with an item.
 *
 * Items can be created with secret_item_create() or secret_service_store().
52
 *
53
54
55
 * These functions have an unstable API and may change across versions. Use
 * <literal>libsecret-unstable</literal> package to access them.
 *
Stef Walter's avatar
Stef Walter committed
56
 * Stability: Unstable
Stef Walter's avatar
Stef Walter committed
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
 */

/**
 * SecretItem:
 *
 * A proxy object representing a secret item in the Secret Service.
 */

/**
 * SecretItemClass:
 * @parent_class: the parent class
 *
 * The class for #SecretItem.
 */

72
73
/**
 * SecretItemFlags:
Stef Walter's avatar
Stef Walter committed
74
75
 * @SECRET_ITEM_NONE: no flags
 * @SECRET_ITEM_LOAD_SECRET: a secret has been (or should be) loaded for #SecretItem
76
 *
Stef Walter's avatar
Stef Walter committed
77
 * Flags which determine which parts of the #SecretItem proxy are initialized.
78
79
 */

80
81
82
83
84
85
86
87
/**
 * SecretItemCreateFlags:
 * @SECRET_ITEM_CREATE_NONE: no flags
 * @SECRET_ITEM_CREATE_REPLACE: replace an item with the same attributes.
 *
 * Flags for secret_item_create().
 */

88
89
enum {
	PROP_0,
90
	PROP_SERVICE,
91
	PROP_FLAGS,
92
93
94
95
96
	PROP_ATTRIBUTES,
	PROP_LABEL,
	PROP_LOCKED,
	PROP_CREATED,
	PROP_MODIFIED
97
98
};

99
struct _SecretItemPrivate {
100
	/* No changes between construct and finalize */
Stef Walter's avatar
Stef Walter committed
101
	SecretService *service;
102
	SecretItemFlags init_flags;
103
	GCancellable *cancellable;
104
105
106
107

	/* Locked by mutex */
	GMutex mutex;
	SecretValue *value;
108
};
109

Stef Walter's avatar
Stef Walter committed
110
static GInitableIface *secret_item_initable_parent_iface = NULL;
111

Stef Walter's avatar
Stef Walter committed
112
static GAsyncInitableIface *secret_item_async_initable_parent_iface = NULL;
113

Stef Walter's avatar
Stef Walter committed
114
static void   secret_item_initable_iface         (GInitableIface *iface);
115

Stef Walter's avatar
Stef Walter committed
116
static void   secret_item_async_initable_iface   (GAsyncInitableIface *iface);
117

Stef Walter's avatar
Stef Walter committed
118
119
120
G_DEFINE_TYPE_WITH_CODE (SecretItem, secret_item, G_TYPE_DBUS_PROXY,
                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, secret_item_initable_iface);
                         G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, secret_item_async_initable_iface);
121
);
122
123

static void
Stef Walter's avatar
Stef Walter committed
124
secret_item_init (SecretItem *self)
125
{
Stef Walter's avatar
Stef Walter committed
126
	self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, SECRET_TYPE_ITEM, SecretItemPrivate);
127
	self->pv->cancellable = g_cancellable_new ();
128
	g_mutex_init (&self->pv->mutex);
129
130
131
132
133
134
135
}

static void
on_set_attributes (GObject *source,
                   GAsyncResult *result,
                   gpointer user_data)
{
Stef Walter's avatar
Stef Walter committed
136
	SecretItem *self = SECRET_ITEM (user_data);
137
138
	GError *error = NULL;

Stef Walter's avatar
Stef Walter committed
139
	secret_item_set_attributes_finish (self, result, &error);
140
	if (error != NULL) {
Stef Walter's avatar
Stef Walter committed
141
		g_warning ("couldn't set SecretItem Attributes: %s", error->message);
142
143
144
145
146
147
148
149
150
151
152
		g_error_free (error);
	}

	g_object_unref (self);
}

static void
on_set_label (GObject *source,
              GAsyncResult *result,
              gpointer user_data)
{
Stef Walter's avatar
Stef Walter committed
153
	SecretItem *self = SECRET_ITEM (user_data);
154
155
	GError *error = NULL;

Stef Walter's avatar
Stef Walter committed
156
	secret_item_set_label_finish (self, result, &error);
157
	if (error != NULL) {
Stef Walter's avatar
Stef Walter committed
158
		g_warning ("couldn't set SecretItem Label: %s", error->message);
159
160
161
162
163
164
		g_error_free (error);
	}

	g_object_unref (self);
}

165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182

static void
item_take_service (SecretItem *self,
                   SecretService *service)
{
	if (service == NULL)
		return;

	g_return_if_fail (self->pv->service == NULL);

	self->pv->service = service;
	g_object_add_weak_pointer (G_OBJECT (self->pv->service),
	                           (gpointer *)&self->pv->service);

	/* Yes, we expect that the service will stay around */
	g_object_unref (service);
}

183
static void
Stef Walter's avatar
Stef Walter committed
184
secret_item_set_property (GObject *obj,
185
186
187
                          guint prop_id,
                          const GValue *value,
                          GParamSpec *pspec)
188
{
Stef Walter's avatar
Stef Walter committed
189
	SecretItem *self = SECRET_ITEM (obj);
190
191

	switch (prop_id) {
192
	case PROP_SERVICE:
193
		item_take_service (self, g_value_dup_object (value));
194
		break;
195
196
197
	case PROP_FLAGS:
		self->pv->init_flags = g_value_get_flags (value);
		break;
198
	case PROP_ATTRIBUTES:
199
		secret_item_set_attributes (self, NULL, g_value_get_boxed (value),
200
201
		                            self->pv->cancellable, on_set_attributes,
		                            g_object_ref (self));
202
203
		break;
	case PROP_LABEL:
Stef Walter's avatar
Stef Walter committed
204
		secret_item_set_label (self, g_value_get_string (value),
205
206
		                       self->pv->cancellable, on_set_label,
		                       g_object_ref (self));
207
208
209
210
211
212
213
214
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
		break;
	}
}

static void
Stef Walter's avatar
Stef Walter committed
215
secret_item_get_property (GObject *obj,
216
217
218
                          guint prop_id,
                          GValue *value,
                          GParamSpec *pspec)
219
{
Stef Walter's avatar
Stef Walter committed
220
	SecretItem *self = SECRET_ITEM (obj);
221
222

	switch (prop_id) {
223
224
225
	case PROP_SERVICE:
		g_value_set_object (value, self->pv->service);
		break;
226
227
228
	case PROP_FLAGS:
		g_value_set_flags (value, secret_item_get_flags (self));
		break;
229
	case PROP_ATTRIBUTES:
Stef Walter's avatar
Stef Walter committed
230
		g_value_take_boxed (value, secret_item_get_attributes (self));
231
232
		break;
	case PROP_LABEL:
Stef Walter's avatar
Stef Walter committed
233
		g_value_take_string (value, secret_item_get_label (self));
234
235
		break;
	case PROP_LOCKED:
Stef Walter's avatar
Stef Walter committed
236
		g_value_set_boolean (value, secret_item_get_locked (self));
237
238
		break;
	case PROP_CREATED:
Stef Walter's avatar
Stef Walter committed
239
		g_value_set_uint64 (value, secret_item_get_created (self));
240
241
		break;
	case PROP_MODIFIED:
Stef Walter's avatar
Stef Walter committed
242
		g_value_set_uint64 (value, secret_item_get_modified (self));
243
244
245
246
247
248
249
250
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
		break;
	}
}

static void
Stef Walter's avatar
Stef Walter committed
251
secret_item_dispose (GObject *obj)
252
{
Stef Walter's avatar
Stef Walter committed
253
	SecretItem *self = SECRET_ITEM (obj);
254

255
	g_cancellable_cancel (self->pv->cancellable);
256

Stef Walter's avatar
Stef Walter committed
257
	G_OBJECT_CLASS (secret_item_parent_class)->dispose (obj);
258
259
260
}

static void
Stef Walter's avatar
Stef Walter committed
261
secret_item_finalize (GObject *obj)
262
{
Stef Walter's avatar
Stef Walter committed
263
	SecretItem *self = SECRET_ITEM (obj);
264

265
266
267
	if (self->pv->service)
		g_object_remove_weak_pointer (G_OBJECT (self->pv->service),
		                              (gpointer *)&self->pv->service);
268

269
	g_object_unref (self->pv->cancellable);
270
	g_mutex_clear (&self->pv->mutex);
271

Stef Walter's avatar
Stef Walter committed
272
	G_OBJECT_CLASS (secret_item_parent_class)->finalize (obj);
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
}

static void
handle_property_changed (GObject *object,
                         const gchar *property_name)
{
	if (g_str_equal (property_name, "Attributes"))
		g_object_notify (object, "attributes");

	else if (g_str_equal (property_name, "Label"))
		g_object_notify (object, "label");

	else if (g_str_equal (property_name, "Locked"))
		g_object_notify (object, "locked");

	else if (g_str_equal (property_name, "Created"))
		g_object_notify (object, "created");

	else if (g_str_equal (property_name, "Modified"))
		g_object_notify (object, "modified");
}

static void
Stef Walter's avatar
Stef Walter committed
296
secret_item_properties_changed (GDBusProxy *proxy,
297
298
                                GVariant *changed_properties,
                                const gchar* const *invalidated_properties)
299
300
301
302
303
304
305
306
307
308
309
310
311
{
	GObject *obj = G_OBJECT (proxy);
	gchar *property_name;
	GVariantIter iter;
	GVariant *value;

	g_object_freeze_notify (obj);

	g_variant_iter_init (&iter, changed_properties);
	while (g_variant_iter_loop (&iter, "{sv}", &property_name, &value))
		handle_property_changed (obj, property_name);

	g_object_thaw_notify (obj);
312
313
314
}

static void
Stef Walter's avatar
Stef Walter committed
315
secret_item_class_init (SecretItemClass *klass)
316
{
317
318
319
	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
	GDBusProxyClass *proxy_class = G_DBUS_PROXY_CLASS (klass);

Stef Walter's avatar
Stef Walter committed
320
321
322
323
	gobject_class->get_property = secret_item_get_property;
	gobject_class->set_property = secret_item_set_property;
	gobject_class->dispose = secret_item_dispose;
	gobject_class->finalize = secret_item_finalize;
324

Stef Walter's avatar
Stef Walter committed
325
	proxy_class->g_properties_changed = secret_item_properties_changed;
326

Stef Walter's avatar
Stef Walter committed
327
328
329
330
	/**
	 * SecretItem:service:
	 *
	 * The #SecretService object that this item is associated with and
331
	 * uses to interact with the actual D-Bus Secret Service.
Stef Walter's avatar
Stef Walter committed
332
	 */
333
334
	g_object_class_install_property (gobject_class, PROP_SERVICE,
	            g_param_spec_object ("service", "Service", "Secret Service",
Stef Walter's avatar
Stef Walter committed
335
	                                 SECRET_TYPE_SERVICE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
336

337
	/**
338
	 * SecretItem:flags:
339
340
341
342
343
344
345
346
347
	 *
	 * A set of flags describing which parts of the secret item have
	 * been initialized.
	 */
	g_object_class_install_property (gobject_class, PROP_FLAGS,
	             g_param_spec_flags ("flags", "Flags", "Item flags",
	                                 secret_item_flags_get_type (), SECRET_ITEM_NONE,
	                                 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));

Stef Walter's avatar
Stef Walter committed
348
349
350
351
352
	/**
	 * SecretItem:attributes:
	 *
	 * The attributes set on this item. Attributes are used to locate an
	 * item. They are not guaranteed to be stored or transferred securely.
353
354
355
	 *
	 * Type: GLib.HashTable(utf8,utf8)
	 * Transfer: full
Stef Walter's avatar
Stef Walter committed
356
	 */
357
358
	g_object_class_install_property (gobject_class, PROP_ATTRIBUTES,
	             g_param_spec_boxed ("attributes", "Attributes", "Item attributes",
359
	                                 G_TYPE_HASH_TABLE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
360

Stef Walter's avatar
Stef Walter committed
361
362
363
364
365
366
367
368
369
	/**
	 * SecretItem:label:
	 *
	 * The human readable label for the item.
	 *
	 * Setting this property will result in the label of the item being
	 * set asynchronously. To properly track the changing of the label use the
	 * secret_item_set_label() function.
	 */
370
371
	g_object_class_install_property (gobject_class, PROP_LABEL,
	            g_param_spec_string ("label", "Label", "Item label",
372
	                                 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
373

Stef Walter's avatar
Stef Walter committed
374
375
376
377
378
379
380
381
382
	/**
	 * SecretItem:locked:
	 *
	 * Whether the item is locked or not. An item may not be independently
	 * lockable separate from other items in its collection.
	 *
	 * To lock or unlock a item use the secret_service_lock() or
	 * secret_service_unlock() functions.
	 */
383
384
	g_object_class_install_property (gobject_class, PROP_LOCKED,
	           g_param_spec_boolean ("locked", "Locked", "Item locked",
385
	                                 TRUE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
386

Stef Walter's avatar
Stef Walter committed
387
388
389
390
391
392
	/**
	 * SecretItem:created:
	 *
	 * The date and time (in seconds since the UNIX epoch) that this
	 * item was created.
	 */
393
394
	g_object_class_install_property (gobject_class, PROP_CREATED,
	            g_param_spec_uint64 ("created", "Created", "Item creation date",
395
	                                 0UL, G_MAXUINT64, 0UL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
396

Stef Walter's avatar
Stef Walter committed
397
398
399
400
401
402
	/**
	 * SecretItem:modified:
	 *
	 * The date and time (in seconds since the UNIX epoch) that this
	 * item was last modified.
	 */
403
404
	g_object_class_install_property (gobject_class, PROP_MODIFIED,
	            g_param_spec_uint64 ("modified", "Modified", "Item modified date",
405
406
	                                 0UL, G_MAXUINT64, 0UL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

Stef Walter's avatar
Stef Walter committed
407
	g_type_class_add_private (gobject_class, sizeof (SecretItemPrivate));
408
409
}

410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
typedef struct {
	GCancellable *cancellable;
} InitClosure;

static void
init_closure_free (gpointer data)
{
	InitClosure *closure = data;
	g_clear_object (&closure->cancellable);
	g_slice_free (InitClosure, closure);
}

static gboolean
item_ensure_for_flags_sync (SecretItem *self,
                            SecretItemFlags flags,
                            GCancellable *cancellable,
                            GError **error)
{
	if (flags & SECRET_ITEM_LOAD_SECRET && !secret_item_get_locked (self)) {
		if (!secret_item_load_secret_sync (self, cancellable, error))
			return FALSE;
	}

	return TRUE;
}


static void
on_init_load_secret (GObject *source,
                     GAsyncResult *result,
                     gpointer user_data)
{
	GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
	SecretItem *self = SECRET_ITEM (source);
	GError *error = NULL;

	if (!secret_item_load_secret_finish (self, result, &error))
		g_simple_async_result_take_error (async, error);

	g_simple_async_result_complete (async);
	g_object_unref (async);
}

static void
item_ensure_for_flags_async (SecretItem *self,
                             SecretItemFlags flags,
                             GSimpleAsyncResult *async)
{
	InitClosure *init = g_simple_async_result_get_op_res_gpointer (async);

	if (flags & SECRET_ITEM_LOAD_SECRET && !secret_item_get_locked (self))
		secret_item_load_secret (self, init->cancellable,
		                         on_init_load_secret, g_object_ref (async));

	else
		g_simple_async_result_complete (async);
}

468
static gboolean
Stef Walter's avatar
Stef Walter committed
469
secret_item_initable_init (GInitable *initable,
470
471
                           GCancellable *cancellable,
                           GError **error)
472
{
473
	SecretItem *self;
474
	SecretService *service;
475
476
	GDBusProxy *proxy;

Stef Walter's avatar
Stef Walter committed
477
	if (!secret_item_initable_parent_iface->init (initable, cancellable, error))
478
479
480
481
		return FALSE;

	proxy = G_DBUS_PROXY (initable);

Stef Walter's avatar
Stef Walter committed
482
	if (!_secret_util_have_cached_properties (proxy)) {
483
484
485
486
487
488
		g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD,
		             "No such secret item at path: %s",
		             g_dbus_proxy_get_object_path (proxy));
		return FALSE;
	}

489
	self = SECRET_ITEM (initable);
490
491
492
493
494
495
496
497
	if (!self->pv->service) {
		service = secret_service_get_sync (SECRET_SERVICE_NONE, cancellable, error);
		if (service == NULL)
			return FALSE;
		else
			item_take_service (self, service);
	}

498
	return item_ensure_for_flags_sync (self, self->pv->init_flags, cancellable, error);
499
500
501
}

static void
Stef Walter's avatar
Stef Walter committed
502
secret_item_initable_iface (GInitableIface *iface)
503
{
Stef Walter's avatar
Stef Walter committed
504
	secret_item_initable_parent_iface = g_type_interface_peek_parent (iface);
505

Stef Walter's avatar
Stef Walter committed
506
	iface->init = secret_item_initable_init;
507
508
}

509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
static void
on_init_service (GObject *source,
                 GAsyncResult *result,
                 gpointer user_data)
{
	GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
	SecretItem *self = SECRET_ITEM (g_async_result_get_source_object (user_data));
	SecretService *service;
	GError *error = NULL;

	service = secret_service_get_finish (result, &error);
	if (error == NULL) {
		item_take_service (self, service);
		item_ensure_for_flags_async (self, self->pv->init_flags, async);

	} else {
		g_simple_async_result_take_error (async, error);
		g_simple_async_result_complete (async);
	}

	g_object_unref (self);
	g_object_unref (async);
}

533
534
535
536
537
538
static void
on_init_base (GObject *source,
              GAsyncResult *result,
              gpointer user_data)
{
	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
539
	InitClosure *init = g_simple_async_result_get_op_res_gpointer (res);
Stef Walter's avatar
Stef Walter committed
540
	SecretItem *self = SECRET_ITEM (source);
541
542
543
	GDBusProxy *proxy = G_DBUS_PROXY (self);
	GError *error = NULL;

Stef Walter's avatar
Stef Walter committed
544
	if (!secret_item_async_initable_parent_iface->init_finish (G_ASYNC_INITABLE (self),
545
	                                                           result, &error)) {
546
		g_simple_async_result_take_error (res, error);
547
		g_simple_async_result_complete (res);
548

Stef Walter's avatar
Stef Walter committed
549
	} else if (!_secret_util_have_cached_properties (proxy)) {
550
551
552
		g_simple_async_result_set_error (res, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD,
		                                 "No such secret item at path: %s",
		                                 g_dbus_proxy_get_object_path (proxy));
553
554
		g_simple_async_result_complete (res);

555
556
557
558
	} else if (self->pv->service == NULL) {
		secret_service_get (SECRET_SERVICE_NONE, init->cancellable,
		                    on_init_service, g_object_ref (res));

559
560
	} else {
		item_ensure_for_flags_async (self, self->pv->init_flags, res);
561
562
563
564
565
566
	}

	g_object_unref (res);
}

static void
Stef Walter's avatar
Stef Walter committed
567
secret_item_async_initable_init_async (GAsyncInitable *initable,
568
569
570
571
                                       int io_priority,
                                       GCancellable *cancellable,
                                       GAsyncReadyCallback callback,
                                       gpointer user_data)
572
573
{
	GSimpleAsyncResult *res;
574
	InitClosure *init;
575
576

	res = g_simple_async_result_new (G_OBJECT (initable), callback, user_data,
Stef Walter's avatar
Stef Walter committed
577
	                                 secret_item_async_initable_init_async);
578
579
580
	init = g_slice_new0 (InitClosure);
	init->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
	g_simple_async_result_set_op_res_gpointer (res, init, init_closure_free);
581

Stef Walter's avatar
Stef Walter committed
582
	secret_item_async_initable_parent_iface->init_async (initable, io_priority,
583
584
585
	                                                     cancellable,
	                                                     on_init_base,
	                                                     g_object_ref (res));
586
587
588
589
590

	g_object_unref (res);
}

static gboolean
Stef Walter's avatar
Stef Walter committed
591
secret_item_async_initable_init_finish (GAsyncInitable *initable,
592
593
                                        GAsyncResult *result,
                                        GError **error)
594
595
{
	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (initable),
Stef Walter's avatar
Stef Walter committed
596
	                      secret_item_async_initable_init_async), FALSE);
597
598
599
600
601
602
603

	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
		return FALSE;

	return TRUE;
}

604
static void
Stef Walter's avatar
Stef Walter committed
605
secret_item_async_initable_iface (GAsyncInitableIface *iface)
606
{
Stef Walter's avatar
Stef Walter committed
607
	secret_item_async_initable_parent_iface = g_type_interface_peek_parent (iface);
608

Stef Walter's avatar
Stef Walter committed
609
610
	iface->init_async = secret_item_async_initable_init_async;
	iface->init_finish = secret_item_async_initable_init_finish;
611
612
}

613
614
615
616
617
618
619
620
621
622
/**
 * secret_item_refresh:
 * @self: the collection
 *
 * Refresh the properties on this item. This fires off a request to
 * refresh, and the properties will be updated later.
 *
 * Calling this method is not normally necessary, as the secret service
 * will notify the client when properties change.
 */
623
void
Stef Walter's avatar
Stef Walter committed
624
secret_item_refresh (SecretItem *self)
625
{
Stef Walter's avatar
Stef Walter committed
626
	g_return_if_fail (SECRET_IS_ITEM (self));
627

Stef Walter's avatar
Stef Walter committed
628
	_secret_util_get_properties (G_DBUS_PROXY (self),
629
630
	                             secret_item_refresh,
	                             NULL, NULL, NULL);
631
632
}

633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
void
_secret_item_set_cached_secret (SecretItem *self,
                                SecretValue *value)
{
	SecretValue *other = NULL;
	gboolean updated = FALSE;

	g_return_if_fail (SECRET_IS_ITEM (self));

	if (value != NULL)
		secret_value_ref (value);

	g_mutex_lock (&self->pv->mutex);

	if (value != self->pv->value) {
		other = self->pv->value;
		self->pv->value = value;
		updated = TRUE;
	} else {
		other = value;
	}

	g_mutex_unlock (&self->pv->mutex);

	if (other != NULL)
		secret_value_unref (other);

	if (updated)
		g_object_notify (G_OBJECT (self), "flags");
}
663
664
665

typedef struct {
	GCancellable *cancellable;
Stef Walter's avatar
Stef Walter committed
666
	SecretItem *item;
667
	SecretValue *value;
668
669
670
671
672
673
674
675
} CreateClosure;

static void
create_closure_free (gpointer data)
{
	CreateClosure *closure = data;
	g_clear_object (&closure->cancellable);
	g_clear_object (&closure->item);
676
	secret_value_unref (closure->value);
677
678
679
680
681
682
683
684
685
686
687
688
	g_slice_free (CreateClosure, closure);
}

static void
on_create_item (GObject *source,
                GAsyncResult *result,
                gpointer user_data)
{
	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
	CreateClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
	GError *error = NULL;

689
	closure->item = secret_item_new_for_dbus_path_finish (result, &error);
690
691
692
	if (error != NULL)
		g_simple_async_result_take_error (res, error);

693
694
695
	/* As a convenince mark down the SecretValue on the item */
	_secret_item_set_cached_secret (closure->item, closure->value);

696
697
698
699
700
701
702
703
704
705
706
	g_simple_async_result_complete (res);
	g_object_unref (res);
}

static void
on_create_path (GObject *source,
                GAsyncResult *result,
                gpointer user_data)
{
	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
	CreateClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
Stef Walter's avatar
Stef Walter committed
707
	SecretService *service = SECRET_SERVICE (source);
708
709
710
	GError *error = NULL;
	gchar *path;

711
	path = secret_service_create_item_dbus_path_finish (service, result, &error);
712
	if (error == NULL) {
713
714
715
		secret_item_new_for_dbus_path (service, path, SECRET_ITEM_NONE,
		                               closure->cancellable, on_create_item,
		                               g_object_ref (res));
716
717
718
719
720
721
722
723
724
	} else {
		g_simple_async_result_take_error (res, error);
		g_simple_async_result_complete (res);
	}

	g_object_unref (res);
}

static GHashTable *
Stef Walter's avatar
Stef Walter committed
725
item_properties_new (const gchar *label,
726
                     const SecretSchema *schema,
727
728
                     GHashTable *attributes)
{
729
	const gchar *schema_name = NULL;
730
731
732
	GHashTable *properties;
	GVariant *value;

733
734
735
	if (schema != NULL)
		schema_name = schema->name;

736
737
738
739
740
	properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
	                                    (GDestroyNotify)g_variant_unref);

	value = g_variant_new_string (label);
	g_hash_table_insert (properties,
Stef Walter's avatar
Stef Walter committed
741
	                     SECRET_ITEM_INTERFACE ".Label",
742
743
	                     g_variant_ref_sink (value));

744
	value = _secret_attributes_to_variant (attributes, schema_name);
745
	g_hash_table_insert (properties,
Stef Walter's avatar
Stef Walter committed
746
	                     SECRET_ITEM_INTERFACE ".Attributes",
747
748
749
750
751
	                     g_variant_ref_sink (value));

	return properties;
}

752
753
754
/**
 * secret_item_create:
 * @collection: a secret collection to create this item in
755
 * @schema: (allow-none): the schema for the attributes
756
 * @attributes: (element-type utf8 utf8): attributes for the new item
757
 * @label: label for the new item
758
 * @value: secret value for the new item
759
 * @flags: flags for the creation of the new item
Stef Walter's avatar
Stef Walter committed
760
 * @cancellable: optional cancellation object
761
762
763
764
765
 * @callback: called when the operation completes
 * @user_data: data to pass to the callback
 *
 * Create a new item in the secret service.
 *
766
767
768
 * If the @flags contains %SECRET_ITEM_CREATE_REPLACE, then the secret
 * service will search for an item matching the @attributes, and update that item
 * instead of creating a new one.
769
770
771
772
773
 *
 * This method may block indefinitely and should not be used in user interface
 * threads. The secret service may prompt the user. secret_service_prompt()
 * will be used to handle any prompts that are required.
 */
774
void
Stef Walter's avatar
Stef Walter committed
775
secret_item_create (SecretCollection *collection,
776
                    const SecretSchema *schema,
777
                    GHashTable *attributes,
778
                    const gchar *label,
779
                    SecretValue *value,
780
                    SecretItemCreateFlags flags,
781
782
783
                    GCancellable *cancellable,
                    GAsyncReadyCallback callback,
                    gpointer user_data)
784
{
Stef Walter's avatar
Stef Walter committed
785
	SecretService *service = NULL;
786
787
788
789
790
	const gchar *collection_path;
	GSimpleAsyncResult *res;
	CreateClosure *closure;
	GHashTable *properties;

Stef Walter's avatar
Stef Walter committed
791
	g_return_if_fail (SECRET_IS_COLLECTION (collection));
792
793
794
795
796
	g_return_if_fail (label != NULL);
	g_return_if_fail (attributes != NULL);
	g_return_if_fail (value != NULL);
	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));

797
798
799
800
	/* Warnings raised already */
	if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, FALSE))
		return;

801
	res = g_simple_async_result_new (NULL, callback, user_data,
Stef Walter's avatar
Stef Walter committed
802
	                                 secret_item_create);
803
804
	closure = g_slice_new0 (CreateClosure);
	closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
805
	closure->value = secret_value_ref (value);
806
807
	g_simple_async_result_set_op_res_gpointer (res, closure, create_closure_free);

808
	properties = item_properties_new (label, schema, attributes);
809
810
811
812
	g_object_get (collection, "service", &service, NULL);

	collection_path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (collection));

813
	secret_service_create_item_dbus_path (service, collection_path, properties,
814
	                                      value, flags, cancellable,
815
	                                      on_create_path, g_object_ref (res));
816
817
818
819
820
821

	g_hash_table_unref (properties);
	g_object_unref (service);
	g_object_unref (res);
}

822
823
824
825
826
827
828
829
830
831
/**
 * secret_item_create_finish:
 * @result: the asynchronous result passed to the callback
 * @error: location to place an error on failure
 *
 * Finish operation to create a new item in the secret service.
 *
 * Returns: (transfer full): the new item, which should be unreferenced
 *          with g_object_unref()
 */
Stef Walter's avatar
Stef Walter committed
832
833
SecretItem *
secret_item_create_finish (GAsyncResult *result,
834
                           GError **error)
835
836
837
838
839
{
	GSimpleAsyncResult *res;
	CreateClosure *closure;

	g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL,
Stef Walter's avatar
Stef Walter committed
840
	                      secret_item_create), NULL);
841
842
843
844
845
846
847
848
849
850
851
852
853
854
	g_return_val_if_fail (error == NULL || *error == NULL, NULL);

	res = G_SIMPLE_ASYNC_RESULT (result);

	if (g_simple_async_result_propagate_error (res, error))
		return NULL;

	closure = g_simple_async_result_get_op_res_gpointer (res);
	if (closure->item == NULL)
		return NULL;

	return g_object_ref (closure->item);
}

855
856
857
/**
 * secret_item_create_sync:
 * @collection: a secret collection to create this item in
858
 * @schema: (allow-none): the schema for the attributes
859
 * @attributes: (element-type utf8 utf8): attributes for the new item
860
 * @label: label for the new item
861
 * @value: secret value for the new item
862
 * @flags: flags for the creation of the new item
863
864
865
866
867
 * @cancellable: optional cancellation object
 * @error: location to place an error on failure
 *
 * Create a new item in the secret service.
 *
868
869
870
 * If the @flags contains %SECRET_ITEM_CREATE_REPLACE, then the secret
 * service will search for an item matching the @attributes, and update that item
 * instead of creating a new one.
871
872
873
874
875
876
877
878
 *
 * This method may block indefinitely and should not be used in user interface
 * threads. The secret service may prompt the user. secret_service_prompt()
 * will be used to handle any prompts that are required.
 *
 * Returns: (transfer full): the new item, which should be unreferenced
 *          with g_object_unref()
 */
Stef Walter's avatar
Stef Walter committed
879
880
SecretItem *
secret_item_create_sync (SecretCollection *collection,
881
                         const SecretSchema *schema,
882
                         GHashTable *attributes,
883
                         const gchar *label,
884
                         SecretValue *value,
885
                         SecretItemCreateFlags flags,
886
887
                         GCancellable *cancellable,
                         GError **error)
888
{
Stef Walter's avatar
Stef Walter committed
889
	SecretService *service = NULL;
890
	const gchar *collection_path;
Stef Walter's avatar
Stef Walter committed
891
	SecretItem *item = NULL;
892
893
894
	GHashTable *properties;
	gchar *path;

Stef Walter's avatar
Stef Walter committed
895
	g_return_val_if_fail (SECRET_IS_COLLECTION (collection), NULL);
896
897
898
899
900
901
	g_return_val_if_fail (label != NULL, NULL);
	g_return_val_if_fail (attributes != NULL, NULL);
	g_return_val_if_fail (value != NULL, NULL);
	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
	g_return_val_if_fail (error == NULL || *error == NULL, NULL);

902
903
904
905
906
	/* Warnings raised already */
	if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, FALSE))
		return NULL;

	properties = item_properties_new (label, schema, attributes);
907
908
909
910
	g_object_get (collection, "service", &service, NULL);

	collection_path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (collection));

911
	path = secret_service_create_item_dbus_path_sync (service, collection_path, properties,
912
	                                                  value, flags, cancellable, error);
913
914

	if (path != NULL) {
915
916
		item = secret_item_new_for_dbus_path_sync (service, path, SECRET_ITEM_NONE,
		                                           cancellable, error);
917
918
919
920
921
922
923
924
925
		g_free (path);
	}

	g_hash_table_unref (properties);
	g_object_unref (service);

	return item;
}

926
927
928
929
930
931
static void
on_item_deleted (GObject *source,
                 GAsyncResult *result,
                 gpointer user_data)
{
	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
Stef Walter's avatar
Stef Walter committed
932
	SecretItem *self = SECRET_ITEM (g_async_result_get_source_object (user_data));
933
934
	GError *error = NULL;

935
	if (_secret_service_delete_path_finish (SECRET_SERVICE (source), result, &error))
936
		g_simple_async_result_set_op_res_gboolean (res, TRUE);
937
938
939

	if (error != NULL)
		g_simple_async_result_take_error (res, error);
940

941
942
943
	g_simple_async_result_complete (res);
	g_object_unref (self);
	g_object_unref (res);
944
945
}

946
/**
Stef Walter's avatar
Stef Walter committed
947
948
 * secret_item_delete:
 * @self: an item
949
950
951
952
 * @cancellable: optional cancellation object
 * @callback: called when the operation completes
 * @user_data: data to pass to the callback
 *
Stef Walter's avatar
Stef Walter committed
953
 * Delete this item.
954
955
956
957
958
 *
 * This method returns immediately and completes asynchronously. The secret
 * service may prompt the user. secret_service_prompt() will be used to handle
 * any prompts that show up.
 */
959
void
Stef Walter's avatar
Stef Walter committed
960
secret_item_delete (SecretItem *self,
961
962
963
                    GCancellable *cancellable,
                    GAsyncReadyCallback callback,
                    gpointer user_data)
964
{
965
	GSimpleAsyncResult *res;
966
967
	const gchar *object_path;

Stef Walter's avatar
Stef Walter committed
968
	g_return_if_fail (SECRET_IS_ITEM (self));
969
970
971
	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));

	object_path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (self));
972
	res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
Stef Walter's avatar
Stef Walter committed
973
	                                 secret_item_delete);
974

Stef Walter's avatar
Stef Walter committed
975
	_secret_service_delete_path (self->pv->service, object_path, TRUE,
976
	                             cancellable, on_item_deleted, g_object_ref (res));
977
978

	g_object_unref (res);
979
980
}

981
982
983
984
985
986
987
988
989
990
/**
 * secret_item_delete_finish:
 * @self: an item
 * @result: asynchronous result passed to the callback
 * @error: location to place an error on failure
 *
 * Complete asynchronous operation to delete the secret item.
 *
 * Returns: whether the item was successfully deleted or not
 */
991
gboolean
Stef Walter's avatar
Stef Walter committed
992
secret_item_delete_finish (SecretItem *self,
993
994
                           GAsyncResult *result,
                           GError **error)
995
{
996
	GSimpleAsyncResult *res;
997

Stef Walter's avatar
Stef Walter committed
998
	g_return_val_if_fail (SECRET_IS_ITEM (self), FALSE);
999
	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1000
	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
Stef Walter's avatar
Stef Walter committed
1001
	                      secret_item_delete), FALSE);
1002
1003

	res = G_SIMPLE_ASYNC_RESULT (result);
1004

1005
1006
1007
1008
	if (g_simple_async_result_propagate_error (res, error))
		return FALSE;

	return g_simple_async_result_get_op_res_gboolean (res);
1009
1010
}

1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
/**
 * secret_item_delete_sync:
 * @self: an item
 * @cancellable: optional cancellation object
 * @error: location to place an error on failure
 *
 * Delete this secret item.
 *
 * This method may block indefinitely and should not be used in user
 * interface threads. The secret service may prompt the user.
 * secret_service_prompt() will be used to handle any prompts that show up.
 *
 * Returns: whether the item was successfully deleted or not
 */
1025
gboolean
Stef Walter's avatar
Stef Walter committed
1026
secret_item_delete_sync (SecretItem *self,
1027
1028
                         GCancellable *cancellable,
                         GError **error)
1029
{
Stef Walter's avatar
Stef Walter committed
1030
	SecretSync *sync;
1031
	gboolean ret;
1032

Stef Walter's avatar
Stef Walter committed
1033
	g_return_val_if_fail (SECRET_IS_ITEM (self), FALSE);
1034
1035
1036
	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

Stef Walter's avatar
Stef Walter committed
1037
	sync = _secret_sync_new ();
1038
1039
	g_main_context_push_thread_default (sync->context);

Stef Walter's avatar
Stef Walter committed
1040
	secret_item_delete (self, cancellable, _secret_sync_on_result, sync);
1041
1042
1043

	g_main_loop_run (sync->loop);

Stef Walter's avatar
Stef Walter committed
1044
	ret = secret_item_delete_finish (self, sync->result, error);
1045
1046

	g_main_context_pop_thread_default (sync->context);
Stef Walter's avatar
Stef Walter committed
1047
	_secret_sync_free (sync);
1048
1049

	return ret;
1050
1051
}

1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
/**
 * secret_item_get_flags:
 * @self: the secret item proxy
 *
 * Get the flags representing what features of the #SecretItem proxy
 * have been initialized.
 *
 * Use secret_item_load_secret() to initialize further features
 * and change the flags.
 *
 * Returns: the flags for features initialized
 */
SecretItemFlags
secret_item_get_flags (SecretItem *self)
{
	SecretServiceFlags flags = 0;

	g_return_val_if_fail (SECRET_IS_ITEM (self), SECRET_ITEM_NONE);

	g_mutex_lock (&self->pv->mutex);

	if (self->pv->value)
		flags |= SECRET_ITEM_LOAD_SECRET;

	g_mutex_unlock (&self->pv->mutex);

	return flags;

}

1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
/**
 * secret_item_get_service:
 * @self: an item
 *
 * Get the Secret Service object that this item was created with.
 *
 * Returns: (transfer none): the Secret Service object
 */
SecretService *
secret_item_get_service (SecretItem *self)
{
1093
	g_return_val_if_fail (SECRET_IS_ITEM (self), NULL);
1094
1095
1096
1097
	return self->pv->service;
}


1098
1099
1100
1101
1102
1103
1104
/**
 * secret_item_get_secret:
 * @self: an item
 *
 * Get the secret value of this item. If this item is locked or the secret
 * has not yet been loaded then this will return %NULL.
 *
Stef Walter's avatar
Stef Walter committed
1105
 * To load the secret call the secret_item_load_secret() method.
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
 *
 * Returns: (transfer full) (allow-none): the secret value which should be
 *          released with secret_value_unref(), or %NULL
 */
SecretValue *
secret_item_get_secret (SecretItem *self)
{
	SecretValue *value = NULL;

	g_return_val_if_fail (SECRET_IS_ITEM (self), NULL);

	g_mutex_lock (&self->pv->mutex);

	if (self->pv->value)
		value = secret_value_ref (self->pv->value);

	g_mutex_unlock (&self->pv->mutex);

	return value;
}


1128
1129
typedef struct {
	GCancellable *cancellable;
1130
} LoadClosure;
1131
1132

static void
1133
load_closure_free (gpointer data)
1134
{
1135
	LoadClosure *closure = data;
1136
	g_clear_object (&closure->cancellable);
1137
	g_slice_free (LoadClosure, closure);
1138
1139
}

1140
static void
1141
1142
1143
on_item_load_secret (GObject *source,
                     GAsyncResult *result,
                     gpointer user_data)