1 /* Representation of the account manager as presented to plugins. This is
2 * deliberately a "smaller" API than McdAccountManager.
3 *
4 * Copyright © 2010 Nokia Corporation
5 * Copyright © 2010-2012 Collabora Ltd.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 *
22 */
23 #include "config.h"
24 #include "mcd-storage.h"
25
26 #include "mcd-account.h"
27 #include "mcd-account-config.h"
28 #include "mcd-debug.h"
29 #include "mcd-misc.h"
30 #include "plugin-loader.h"
31
32 #include <errno.h>
33 #include <string.h>
34
35 #include <telepathy-glib/telepathy-glib.h>
36 #include <telepathy-glib/telepathy-glib-dbus.h>
37
38 #include "mission-control-plugins/implementation.h"
39
40 /* these pseudo-plugins take care of the actual account storage/retrieval */
41 #include "mcd-account-manager-default.h"
42
43 #if ENABLE_LIBACCOUNTS_SSO
44 #include "mcd-account-manager-sso.h"
45 # ifdef ACCOUNTS_GLIB_HIDDEN_SERVICE_TYPE
46 # include "mcd-storage-ag-hidden.h"
47 # endif
48 #endif
49
50 #define MAX_KEY_LENGTH (DBUS_MAXIMUM_NAME_LENGTH + 6)
51
52 static GList *stores = NULL;
53 static void sort_and_cache_plugins (void);
54
55 enum {
56 PROP_DBUS_DAEMON = 1,
57 };
58
59 struct _McdStorageClass {
60 GObjectClass parent;
61 };
62
63 static void plugin_iface_init (McpAccountManagerIface *iface,
64 gpointer unused G_GNUC_UNUSED);
65
66 G_DEFINE_TYPE_WITH_CODE (McdStorage, mcd_storage,
67 G_TYPE_OBJECT,
68 G_IMPLEMENT_INTERFACE (MCP_TYPE_ACCOUNT_MANAGER, plugin_iface_init))
69
70 typedef struct {
71 /* owned string => GVariant
72 * e.g. { 'DisplayName': <'Frederick Bloggs'> } */
73 GHashTable *attributes;
74 /* owned string => owned GVariant
75 * e.g. { 'account': <'fred@example.com'>, 'password': <'foo'> } */
76 GHashTable *parameters;
77 /* owned string => owned string escaped as if for a keyfile
78 * e.g. { 'account': 'fred@example.com', 'password': 'foo' }
79 * keys of @parameters and @escaped_parameters are disjoint */
80 GHashTable *escaped_parameters;
81 /* set of owned strings
82 * e.g. { 'password': 'password' } */
83 GHashTable *secrets;
84 } McdStorageAccount;
85
86 static void
mcd_storage_account_free(gpointer p)87 mcd_storage_account_free (gpointer p)
88 {
89 McdStorageAccount *sa = p;
90
91 g_hash_table_unref (sa->attributes);
92 g_hash_table_unref (sa->parameters);
93 g_hash_table_unref (sa->escaped_parameters);
94 g_hash_table_unref (sa->secrets);
95 g_slice_free (McdStorageAccount, sa);
96 }
97
98 static void
mcd_storage_init(McdStorage * self)99 mcd_storage_init (McdStorage *self)
100 {
101 self->accounts = g_hash_table_new_full (g_str_hash, g_str_equal,
102 g_free, mcd_storage_account_free);
103 }
104
105 static void
storage_finalize(GObject * object)106 storage_finalize (GObject *object)
107 {
108 McdStorage *self = MCD_STORAGE (object);
109 GObjectFinalizeFunc finalize =
110 G_OBJECT_CLASS (mcd_storage_parent_class)->finalize;
111
112 g_hash_table_unref (self->accounts);
113 self->accounts = NULL;
114
115 if (finalize != NULL)
116 finalize (object);
117 }
118
119 static void
storage_dispose(GObject * object)120 storage_dispose (GObject *object)
121 {
122 McdStorage *self = MCD_STORAGE (object);
123 GObjectFinalizeFunc dispose =
124 G_OBJECT_CLASS (mcd_storage_parent_class)->dispose;
125
126 tp_clear_object (&self->dbusd);
127
128 if (dispose != NULL)
129 dispose (object);
130 }
131
132 static void
storage_set_property(GObject * obj,guint prop_id,const GValue * val,GParamSpec * pspec)133 storage_set_property (GObject *obj, guint prop_id,
134 const GValue *val, GParamSpec *pspec)
135 {
136 McdStorage *self = MCD_STORAGE (obj);
137
138 switch (prop_id)
139 {
140 case PROP_DBUS_DAEMON:
141 tp_clear_object (&self->dbusd);
142 self->dbusd = TP_DBUS_DAEMON (g_value_dup_object (val));
143 break;
144
145 default:
146 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
147 break;
148 }
149 }
150
151 static void
storage_get_property(GObject * obj,guint prop_id,GValue * val,GParamSpec * pspec)152 storage_get_property (GObject *obj, guint prop_id,
153 GValue *val, GParamSpec *pspec)
154 {
155 McdStorage *self = MCD_STORAGE (obj);
156
157 switch (prop_id)
158 {
159 case PROP_DBUS_DAEMON:
160 g_value_set_object (val, self->dbusd);
161 break;
162 default:
163 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
164 break;
165 }
166 }
167
168 static void
mcd_storage_class_init(McdStorageClass * cls)169 mcd_storage_class_init (McdStorageClass *cls)
170 {
171 GObjectClass *object_class = (GObjectClass *) cls;
172 GParamSpec *spec = g_param_spec_object ("dbus-daemon",
173 "DBus daemon",
174 "DBus daemon",
175 TP_TYPE_DBUS_DAEMON,
176 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
177
178 object_class->set_property = storage_set_property;
179 object_class->get_property = storage_get_property;
180 object_class->dispose = storage_dispose;
181 object_class->finalize = storage_finalize;
182
183 g_object_class_install_property (object_class, PROP_DBUS_DAEMON, spec);
184 }
185
186 McdStorage *
mcd_storage_new(TpDBusDaemon * dbus_daemon)187 mcd_storage_new (TpDBusDaemon *dbus_daemon)
188 {
189 return g_object_new (MCD_TYPE_STORAGE,
190 "dbus-daemon", dbus_daemon,
191 NULL);
192 }
193
194 static gchar *
mcd_keyfile_escape_variant(GVariant * variant)195 mcd_keyfile_escape_variant (GVariant *variant)
196 {
197 GValue value = G_VALUE_INIT;
198 gchar *ret;
199
200 dbus_g_value_parse_g_variant (variant, &value);
201
202 if (G_IS_VALUE (&value))
203 {
204 ret = mcd_keyfile_escape_value (&value);
205 g_value_unset (&value);
206 }
207 else
208 {
209 gchar *printed = g_variant_print (variant, TRUE);
210
211 ret = NULL;
212 g_warning ("Unable to translate variant %s", printed);
213 g_free (printed);
214 }
215
216 return ret;
217 }
218
219 static McdStorageAccount *
lookup_account(McdStorage * self,const gchar * account)220 lookup_account (McdStorage *self,
221 const gchar *account)
222 {
223 return g_hash_table_lookup (self->accounts, account);
224 }
225
226 static McdStorageAccount *
ensure_account(McdStorage * self,const gchar * account)227 ensure_account (McdStorage *self,
228 const gchar *account)
229 {
230 McdStorageAccount *sa = lookup_account (self, account);
231
232 if (sa == NULL)
233 {
234 sa = g_slice_new (McdStorageAccount);
235 sa->attributes = g_hash_table_new_full (g_str_hash, g_str_equal,
236 g_free, (GDestroyNotify) g_variant_unref);
237 sa->parameters = g_hash_table_new_full (g_str_hash, g_str_equal,
238 g_free, (GDestroyNotify) g_variant_unref);
239 sa->escaped_parameters = g_hash_table_new_full (g_str_hash, g_str_equal,
240 g_free, g_free);
241 sa->secrets = g_hash_table_new_full (g_str_hash, g_str_equal,
242 g_free, NULL);
243 g_hash_table_insert (self->accounts, g_strdup (account), sa);
244 }
245
246 return sa;
247 }
248
249 static gchar *
get_value(const McpAccountManager * ma,const gchar * account,const gchar * key)250 get_value (const McpAccountManager *ma,
251 const gchar *account,
252 const gchar *key)
253 {
254 McdStorage *self = MCD_STORAGE (ma);
255 McdStorageAccount *sa = lookup_account (self, account);
256 GVariant *variant;
257 gchar *ret;
258
259 if (sa == NULL)
260 return NULL;
261
262 if (g_str_has_prefix (key, "param-"))
263 {
264 variant = g_hash_table_lookup (sa->parameters, key + 6);
265
266 if (variant != NULL)
267 {
268 ret = mcd_keyfile_escape_variant (variant);
269 g_variant_unref (variant);
270 return ret;
271 }
272 else
273 {
274 /* OK, we don't have it as a variant. How about the keyfile-escaped
275 * version? */
276 return g_strdup (g_hash_table_lookup (sa->escaped_parameters,
277 key + 6));
278 }
279 }
280 else
281 {
282 variant = g_hash_table_lookup (sa->attributes, key);
283
284 if (variant != NULL)
285 {
286 ret = mcd_keyfile_escape_variant (variant);
287 g_variant_unref (variant);
288 return ret;
289 }
290 else
291 {
292 return NULL;
293 }
294 }
295 }
296
297 static struct {
298 const gchar *type;
299 const gchar *name;
300 } known_attributes[] = {
301 /* Please keep this sorted by type, then by name. */
302
303 /* Structs */
304 { "(uss)", MC_ACCOUNTS_KEY_AUTOMATIC_PRESENCE },
305
306 /* Array of object path */
307 { "ao", MC_ACCOUNTS_KEY_SUPERSEDES },
308
309 /* Array of string */
310 { "as", MC_ACCOUNTS_KEY_URI_SCHEMES },
311
312 /* Booleans */
313 { "b", MC_ACCOUNTS_KEY_ALWAYS_DISPATCH },
314 { "b", MC_ACCOUNTS_KEY_CONNECT_AUTOMATICALLY },
315 { "b", MC_ACCOUNTS_KEY_ENABLED },
316 { "b", MC_ACCOUNTS_KEY_HAS_BEEN_ONLINE },
317 { "b", MC_ACCOUNTS_KEY_HIDDEN },
318
319 /* Strings */
320 { "s", MC_ACCOUNTS_KEY_AUTO_PRESENCE_MESSAGE },
321 { "s", MC_ACCOUNTS_KEY_AUTO_PRESENCE_STATUS },
322 { "s", MC_ACCOUNTS_KEY_AVATAR_MIME },
323 { "s", MC_ACCOUNTS_KEY_AVATAR_TOKEN },
324 { "s", MC_ACCOUNTS_KEY_DISPLAY_NAME },
325 { "s", MC_ACCOUNTS_KEY_ICON },
326 { "s", MC_ACCOUNTS_KEY_MANAGER },
327 { "s", MC_ACCOUNTS_KEY_NICKNAME },
328 { "s", MC_ACCOUNTS_KEY_NORMALIZED_NAME },
329 { "s", MC_ACCOUNTS_KEY_PROTOCOL },
330 { "s", MC_ACCOUNTS_KEY_SERVICE },
331
332 /* Integers */
333 { "u", MC_ACCOUNTS_KEY_AUTO_PRESENCE_TYPE },
334
335 { NULL, NULL }
336 };
337
338 const gchar *
mcd_storage_get_attribute_type(const gchar * attribute)339 mcd_storage_get_attribute_type (const gchar *attribute)
340 {
341 guint i;
342
343 for (i = 0; known_attributes[i].type != NULL; i++)
344 {
345 if (!tp_strdiff (attribute, known_attributes[i].name))
346 return known_attributes[i].type;
347 }
348
349 /* special case for mcd-account-conditions.c */
350 if (g_str_has_prefix (attribute, "condition-"))
351 return "s";
352
353 return NULL;
354 }
355
356 gboolean
mcd_storage_init_value_for_attribute(GValue * value,const gchar * attribute)357 mcd_storage_init_value_for_attribute (GValue *value,
358 const gchar *attribute)
359 {
360 const gchar *s = mcd_storage_get_attribute_type (attribute);
361
362 if (s == NULL)
363 return FALSE;
364
365 switch (s[0])
366 {
367 case 's':
368 g_value_init (value, G_TYPE_STRING);
369 return TRUE;
370
371 case 'b':
372 g_value_init (value, G_TYPE_BOOLEAN);
373 return TRUE;
374
375 case 'u':
376 /* this seems wrong but it's how we've always done it */
377 g_value_init (value, G_TYPE_INT);
378 return TRUE;
379
380 case 'a':
381 {
382 switch (s[1])
383 {
384 case 'o':
385 g_value_init (value, TP_ARRAY_TYPE_OBJECT_PATH_LIST);
386 return TRUE;
387
388 case 's':
389 g_value_init (value, G_TYPE_STRV);
390 return TRUE;
391 }
392 }
393 break;
394
395 case '(':
396 {
397 if (!tp_strdiff (s, "(uss)"))
398 {
399 g_value_init (value, TP_STRUCT_TYPE_SIMPLE_PRESENCE);
400 return TRUE;
401 }
402 }
403 break;
404 }
405
406 return FALSE;
407 }
408
409 static gboolean
mcpa_init_value_for_attribute(const McpAccountManager * mcpa,GValue * value,const gchar * attribute)410 mcpa_init_value_for_attribute (const McpAccountManager *mcpa,
411 GValue *value,
412 const gchar *attribute)
413 {
414 return mcd_storage_init_value_for_attribute (value, attribute);
415 }
416
417 static void
mcpa_set_attribute(const McpAccountManager * ma,const gchar * account,const gchar * attribute,GVariant * value,McpAttributeFlags flags)418 mcpa_set_attribute (const McpAccountManager *ma,
419 const gchar *account,
420 const gchar *attribute,
421 GVariant *value,
422 McpAttributeFlags flags)
423 {
424 McdStorage *self = MCD_STORAGE (ma);
425 McdStorageAccount *sa = ensure_account (self, account);
426
427 if (value != NULL)
428 {
429 g_hash_table_insert (sa->attributes, g_strdup (attribute),
430 g_variant_ref_sink (value));
431 }
432 else
433 {
434 g_hash_table_remove (sa->attributes, attribute);
435 }
436 }
437
438 static void
mcpa_set_parameter(const McpAccountManager * ma,const gchar * account,const gchar * parameter,GVariant * value,McpParameterFlags flags)439 mcpa_set_parameter (const McpAccountManager *ma,
440 const gchar *account,
441 const gchar *parameter,
442 GVariant *value,
443 McpParameterFlags flags)
444 {
445 McdStorage *self = MCD_STORAGE (ma);
446 McdStorageAccount *sa = ensure_account (self, account);
447
448 g_hash_table_remove (sa->parameters, parameter);
449 g_hash_table_remove (sa->escaped_parameters, parameter);
450
451 if (value != NULL)
452 g_hash_table_insert (sa->parameters, g_strdup (parameter),
453 g_variant_ref_sink (value));
454
455 if (flags & MCP_PARAMETER_FLAG_SECRET)
456 {
457 DEBUG ("flagging %s parameter %s as secret", account, parameter);
458 g_hash_table_add (sa->secrets, g_strdup (parameter));
459 }
460 }
461
462 static void
set_value(const McpAccountManager * ma,const gchar * account,const gchar * key,const gchar * value)463 set_value (const McpAccountManager *ma,
464 const gchar *account,
465 const gchar *key,
466 const gchar *value)
467 {
468 McdStorage *self = MCD_STORAGE (ma);
469 McdStorageAccount *sa = ensure_account (self, account);
470
471 if (g_str_has_prefix (key, "param-"))
472 {
473 g_hash_table_remove (sa->parameters, key + 6);
474 g_hash_table_remove (sa->escaped_parameters, key + 6);
475
476 if (value != NULL)
477 g_hash_table_insert (sa->escaped_parameters, g_strdup (key + 6),
478 g_strdup (value));
479 }
480 else
481 {
482 if (value != NULL)
483 {
484 GValue tmp = G_VALUE_INIT;
485 GError *error = NULL;
486
487 if (!mcd_storage_init_value_for_attribute (&tmp, key))
488 {
489 g_warning ("Not sure what the type of '%s' is, assuming string",
490 key);
491 g_value_init (&tmp, G_TYPE_STRING);
492 }
493
494 if (mcd_keyfile_unescape_value (value, &tmp, &error))
495 {
496 g_hash_table_insert (sa->attributes, g_strdup (key),
497 g_variant_ref_sink (dbus_g_value_build_g_variant (&tmp)));
498 g_value_unset (&tmp);
499 }
500 else
501 {
502 g_warning ("Could not decode attribute '%s':'%s' from plugin: %s",
503 key, value, error->message);
504 g_error_free (error);
505 g_hash_table_remove (sa->attributes, key);
506 }
507 }
508 else
509 {
510 g_hash_table_remove (sa->attributes, key);
511 }
512 }
513 }
514
515 static GStrv
list_keys(const McpAccountManager * ma,const gchar * account)516 list_keys (const McpAccountManager *ma,
517 const gchar * account)
518 {
519 McdStorage *self = MCD_STORAGE (ma);
520 GPtrArray *ret = g_ptr_array_new ();
521 McdStorageAccount *sa = lookup_account (self, account);
522
523 if (sa != NULL)
524 {
525 GHashTableIter iter;
526 gpointer k;
527
528 g_hash_table_iter_init (&iter, sa->attributes);
529
530 while (g_hash_table_iter_next (&iter, &k, NULL))
531 g_ptr_array_add (ret, g_strdup (k));
532
533 g_hash_table_iter_init (&iter, sa->parameters);
534
535 while (g_hash_table_iter_next (&iter, &k, NULL))
536 g_ptr_array_add (ret, g_strdup_printf ("param-%s", (gchar *) k));
537 }
538
539 g_ptr_array_add (ret, NULL);
540 return (GStrv) g_ptr_array_free (ret, FALSE);
541 }
542
543 static gboolean
is_secret(const McpAccountManager * ma,const gchar * account,const gchar * key)544 is_secret (const McpAccountManager *ma,
545 const gchar *account,
546 const gchar *key)
547 {
548 McdStorage *self = MCD_STORAGE (ma);
549 McdStorageAccount *sa = lookup_account (self, account);
550
551 if (sa == NULL || !g_str_has_prefix (key, "param-"))
552 return FALSE;
553
554 return g_hash_table_contains (sa->secrets, key + 6);
555 }
556
557 static void
mcd_storage_make_secret(McdStorage * self,const gchar * account,const gchar * key)558 mcd_storage_make_secret (McdStorage *self,
559 const gchar *account,
560 const gchar *key)
561 {
562 McdStorageAccount *sa;
563
564 g_return_if_fail (MCD_IS_STORAGE (self));
565 g_return_if_fail (account != NULL);
566 g_return_if_fail (key != NULL);
567
568 if (!g_str_has_prefix (key, "param-"))
569 return;
570
571 DEBUG ("flagging %s parameter %s as secret", account, key + 6);
572 sa = ensure_account (self, account);
573 g_hash_table_add (sa->secrets, g_strdup (key + 6));
574 }
575
576 static void
make_secret(const McpAccountManager * ma,const gchar * account,const gchar * key)577 make_secret (const McpAccountManager *ma,
578 const gchar *account,
579 const gchar *key)
580 {
581 mcd_storage_make_secret (MCD_STORAGE (ma), account, key);
582 }
583
584 static gchar *
unique_name(const McpAccountManager * ma,const gchar * manager,const gchar * protocol,const GHashTable * params)585 unique_name (const McpAccountManager *ma,
586 const gchar *manager,
587 const gchar *protocol,
588 const GHashTable *params)
589 {
590 McdStorage *self = MCD_STORAGE (ma);
591 const gchar *base = NULL;
592 gchar *esc_manager, *esc_protocol, *esc_base;
593 guint i;
594 gsize base_len = strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
595 DBusGConnection *connection = tp_proxy_get_dbus_connection (self->dbusd);
596
597 base = tp_asv_get_string (params, "account");
598
599 if (base == NULL)
600 base = "account";
601
602 esc_manager = tp_escape_as_identifier (manager);
603 esc_protocol = g_strdelimit (g_strdup (protocol), "-", '_');
604 esc_base = tp_escape_as_identifier (base);
605
606 for (i = 0; i < G_MAXUINT; i++)
607 {
608 gchar *path = g_strdup_printf (
609 TP_ACCOUNT_OBJECT_PATH_BASE "%s/%s/%s%u",
610 esc_manager, esc_protocol, esc_base, i);
611
612 if (!g_hash_table_contains (self->accounts, path + base_len) &&
613 dbus_g_connection_lookup_g_object (connection, path) == NULL)
614 {
615 gchar *ret = g_strdup (path + base_len);
616
617 g_free (path);
618 return ret;
619 }
620
621 g_free (path);
622 }
623
624 return NULL;
625 }
626
627 /* sort in descending order of priority (ie higher prio => earlier in list) */
628 static gint
account_storage_cmp(gconstpointer a,gconstpointer b)629 account_storage_cmp (gconstpointer a, gconstpointer b)
630 {
631 gint pa = mcp_account_storage_priority (a);
632 gint pb = mcp_account_storage_priority (b);
633
634 if (pa > pb) return -1;
635 if (pa < pb) return 1;
636
637 return 0;
638 }
639
640 static void
add_storage_plugin(McpAccountStorage * plugin)641 add_storage_plugin (McpAccountStorage *plugin)
642 {
643 stores = g_list_insert_sorted (stores, plugin, account_storage_cmp);
644 }
645
646 static void
add_libaccounts_plugins_if_enabled(void)647 add_libaccounts_plugins_if_enabled (void)
648 {
649 #if ENABLE_LIBACCOUNTS_SSO
650 add_storage_plugin (MCP_ACCOUNT_STORAGE (mcd_account_manager_sso_new ()));
651 # ifdef ACCOUNTS_GLIB_HIDDEN_SERVICE_TYPE
652 add_storage_plugin (MCP_ACCOUNT_STORAGE (mcd_storage_ag_hidden_new ()));
653 # endif
654 #endif
655 }
656
657 static void
sort_and_cache_plugins()658 sort_and_cache_plugins ()
659 {
660 const GList *p;
661 static gboolean plugins_cached = FALSE;
662
663 if (plugins_cached)
664 return;
665
666 /* not guaranteed to have been called, but idempotent: */
667 _mcd_plugin_loader_init ();
668
669 /* Add compiled-in plugins */
670 add_storage_plugin (MCP_ACCOUNT_STORAGE (mcd_account_manager_default_new ()));
671 add_libaccounts_plugins_if_enabled ();
672
673 for (p = mcp_list_objects(); p != NULL; p = g_list_next (p))
674 {
675 if (MCP_IS_ACCOUNT_STORAGE (p->data))
676 {
677 McpAccountStorage *plugin = g_object_ref (p->data);
678
679 add_storage_plugin (plugin);
680 }
681 }
682
683 for (p = stores; p != NULL; p = g_list_next (p))
684 {
685 McpAccountStorage *plugin = p->data;
686
687 DEBUG ("found plugin %s [%s; priority %d]\n%s",
688 mcp_account_storage_name (plugin),
689 g_type_name (G_TYPE_FROM_INSTANCE (plugin)),
690 mcp_account_storage_priority (plugin),
691 mcp_account_storage_description (plugin));
692 }
693
694 plugins_cached = TRUE;
695 }
696
697 void
mcd_storage_connect_signal(const gchar * signame,GCallback func,gpointer user_data)698 mcd_storage_connect_signal (const gchar *signame,
699 GCallback func,
700 gpointer user_data)
701 {
702 GList *p;
703
704 for (p = stores; p != NULL; p = g_list_next (p))
705 {
706 McpAccountStorage *plugin = p->data;
707
708 DEBUG ("connecting handler to %s plugin signal %s ",
709 mcp_account_storage_name (plugin), signame);
710 g_signal_connect (plugin, signame, func, user_data);
711 }
712 }
713
714 /*
715 * mcd_storage_load:
716 * @storage: An object implementing the #McdStorage interface
717 *
718 * Load the long term account settings storage into our internal cache.
719 * Should only really be called during startup, ie before our DBus names
720 * have been claimed and other people might be relying on responses from us.
721 */
722 void
mcd_storage_load(McdStorage * self)723 mcd_storage_load (McdStorage *self)
724 {
725 McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self);
726 GList *store = NULL;
727
728 g_return_if_fail (MCD_IS_STORAGE (self));
729
730 sort_and_cache_plugins ();
731
732 store = g_list_last (stores);
733
734 /* fetch accounts stored in plugins, in reverse priority so higher prio *
735 * plugins can overwrite lower prio ones' account data */
736 while (store != NULL)
737 {
738 GList *account;
739 McpAccountStorage *plugin = store->data;
740 GList *stored = mcp_account_storage_list (plugin, ma);
741 const gchar *pname = mcp_account_storage_name (plugin);
742 const gint prio = mcp_account_storage_priority (plugin);
743
744 DEBUG ("listing from plugin %s [prio: %d]", pname, prio);
745 for (account = stored; account != NULL; account = g_list_next (account))
746 {
747 gchar *name = account->data;
748
749 DEBUG ("fetching %s from plugin %s [prio: %d]", name, pname, prio);
750 mcd_storage_add_account_from_plugin (self, plugin, name);
751 g_free (name);
752 }
753
754 /* already freed the contents, just need to free the list itself */
755 g_list_free (stored);
756 store = g_list_previous (store);
757 }
758 }
759
760 /*
761 * mcd_storage_dup_accounts:
762 * @storage: An object implementing the #McdStorage interface
763 * @n: place for the number of accounts to be written to (or %NULL)
764 *
765 * Returns: a newly allocated GStrv containing the unique account names,
766 * which must be freed by the caller with g_strfreev().
767 */
768 GStrv
mcd_storage_dup_accounts(McdStorage * self,gsize * n)769 mcd_storage_dup_accounts (McdStorage *self,
770 gsize *n)
771 {
772 GPtrArray *ret = g_ptr_array_new ();
773 GHashTableIter iter;
774 gpointer k, v;
775
776 g_hash_table_iter_init (&iter, self->accounts);
777
778 while (g_hash_table_iter_next (&iter, &k, &v))
779 {
780 McdStorageAccount *sa = v;
781
782 if (g_hash_table_size (sa->attributes) > 0)
783 g_ptr_array_add (ret, g_strdup (k));
784 }
785
786 g_ptr_array_add (ret, NULL);
787 return (GStrv) g_ptr_array_free (ret, FALSE);
788 }
789
790 /*
791 * mcd_storage_dup_attributes:
792 * @storage: An object implementing the #McdStorage interface
793 * @account: unique name of the account
794 * @n: place for the number of attributes to be written to (or %NULL)
795 *
796 * Returns: a newly allocated GStrv containing the names of all the
797 * attributes or parameters currently stored for @account. Must be
798 * freed by the caller with g_strfreev().
799 */
800 GStrv
mcd_storage_dup_attributes(McdStorage * self,const gchar * account,gsize * n)801 mcd_storage_dup_attributes (McdStorage *self,
802 const gchar *account,
803 gsize *n)
804 {
805 GPtrArray *ret = g_ptr_array_new ();
806 McdStorageAccount *sa = lookup_account (self, account);
807
808 if (sa != NULL)
809 {
810 GHashTableIter iter;
811 gpointer k;
812
813 g_hash_table_iter_init (&iter, sa->attributes);
814
815 while (g_hash_table_iter_next (&iter, &k, NULL))
816 g_ptr_array_add (ret, g_strdup (k));
817 }
818
819 g_ptr_array_add (ret, NULL);
820 return (GStrv) g_ptr_array_free (ret, FALSE);
821 }
822
823 /*
824 * mcd_storage_get_plugin:
825 * @storage: An object implementing the #McdStorage interface
826 * @account: unique name of the account
827 *
828 * Returns: the #McpAccountStorage object which is handling the account,
829 * if any (if a new account has not yet been flushed to storage this can
830 * be %NULL).
831 *
832 * Plugins are kept in permanent storage and can never be unloaded, so
833 * the returned pointer need not be reffed or unreffed. (Indeed, it's
834 * probably safer not to)
835 */
836 McpAccountStorage *
mcd_storage_get_plugin(McdStorage * self,const gchar * account)837 mcd_storage_get_plugin (McdStorage *self,
838 const gchar *account)
839 {
840 GList *store = stores;
841 McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self);
842 McpAccountStorage *owner = NULL;
843
844 g_return_val_if_fail (MCD_IS_STORAGE (self), NULL);
845 g_return_val_if_fail (account != NULL, NULL);
846
847 for (; store != NULL && owner == NULL; store = g_list_next (store))
848 {
849 McpAccountStorage *plugin = store->data;
850
851 if (mcp_account_storage_owns (plugin, ma, account))
852 owner = plugin;
853 }
854
855 return owner;
856 }
857
858 /*
859 * mcd_storage_dup_string:
860 * @storage: An object implementing the #McdStorage interface
861 * @account: unique name of the account
862 * @attribute: name of the attribute to be retrieved (which must not be a
863 * parameter)
864 *
865 * Returns: a newly allocated gchar * which must be freed with g_free().
866 */
867 gchar *
mcd_storage_dup_string(McdStorage * self,const gchar * account,const gchar * attribute)868 mcd_storage_dup_string (McdStorage *self,
869 const gchar *account,
870 const gchar *attribute)
871 {
872 GValue tmp = G_VALUE_INIT;
873 gchar *ret;
874
875 g_return_val_if_fail (MCD_IS_STORAGE (self), NULL);
876 g_return_val_if_fail (account != NULL, NULL);
877 g_return_val_if_fail (attribute != NULL, NULL);
878 g_return_val_if_fail (!g_str_has_prefix (attribute, "param-"), NULL);
879
880 g_value_init (&tmp, G_TYPE_STRING);
881
882 if (!mcd_storage_get_attribute (self, account, attribute, &tmp, NULL))
883 return NULL;
884
885 ret = g_value_dup_string (&tmp);
886 g_value_unset (&tmp);
887 return ret;
888 }
889
890 static gboolean
mcd_storage_coerce_variant_to_value(GVariant * variant,GValue * value,GError ** error)891 mcd_storage_coerce_variant_to_value (GVariant *variant,
892 GValue *value,
893 GError **error)
894 {
895 GValue tmp = G_VALUE_INIT;
896 gboolean ret;
897 gchar *escaped;
898
899 dbus_g_value_parse_g_variant (variant, &tmp);
900
901 if (G_VALUE_TYPE (&tmp) == G_VALUE_TYPE (value))
902 {
903 memcpy (value, &tmp, sizeof (tmp));
904 return TRUE;
905 }
906
907 /* This is really pretty stupid but it'll do for now.
908 * FIXME: implement a better similar-type-coercion mechanism than
909 * round-tripping through a GKeyFile. */
910 escaped = mcd_keyfile_escape_value (&tmp);
911 ret = mcd_keyfile_unescape_value (escaped, value, error);
912 g_free (escaped);
913 g_value_unset (&tmp);
914 return ret;
915 }
916
917 /*
918 * mcd_storage_get_attribute:
919 * @storage: An object implementing the #McdStorage interface
920 * @account: unique name of the account
921 * @attribute: name of the attribute to be retrieved, e.g. 'DisplayName'
922 * @value: location to return the value, initialized to the right #GType
923 * @error: a place to store any #GError<!-- -->s that occur
924 */
925 gboolean
mcd_storage_get_attribute(McdStorage * self,const gchar * account,const gchar * attribute,GValue * value,GError ** error)926 mcd_storage_get_attribute (McdStorage *self,
927 const gchar *account,
928 const gchar *attribute,
929 GValue *value,
930 GError **error)
931 {
932 McdStorageAccount *sa;
933 GVariant *variant;
934
935 g_return_val_if_fail (MCD_IS_STORAGE (self), FALSE);
936 g_return_val_if_fail (account != NULL, FALSE);
937 g_return_val_if_fail (attribute != NULL, FALSE);
938 g_return_val_if_fail (!g_str_has_prefix (attribute, "param-"), FALSE);
939
940 sa = lookup_account (self, account);
941
942 if (sa == NULL)
943 {
944 g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
945 "Account %s does not exist", account);
946 return FALSE;
947 }
948
949 variant = g_hash_table_lookup (sa->attributes, attribute);
950
951 if (variant == NULL)
952 {
953 g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
954 "Setting '%s' not stored by account %s", attribute, account);
955 return FALSE;
956 }
957
958 return mcd_storage_coerce_variant_to_value (variant, value, error);
959 }
960
961 /*
962 * mcd_storage_get_parameter:
963 * @storage: An object implementing the #McdStorage interface
964 * @account: unique name of the account
965 * @parameter: name of the parameter to be retrieved, e.g. 'account'
966 * @value: location to return the value, initialized to the right #GType
967 * @error: a place to store any #GError<!-- -->s that occur
968 */
969 gboolean
mcd_storage_get_parameter(McdStorage * self,const gchar * account,const gchar * parameter,GValue * value,GError ** error)970 mcd_storage_get_parameter (McdStorage *self,
971 const gchar *account,
972 const gchar *parameter,
973 GValue *value,
974 GError **error)
975 {
976 McdStorageAccount *sa;
977 const gchar *escaped;
978 GVariant *variant;
979
980 g_return_val_if_fail (MCD_IS_STORAGE (self), FALSE);
981 g_return_val_if_fail (account != NULL, FALSE);
982 g_return_val_if_fail (parameter != NULL, FALSE);
983
984 sa = lookup_account (self, account);
985
986 if (sa == NULL)
987 {
988 g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
989 "Account %s does not exist", account);
990 return FALSE;
991 }
992
993 variant = g_hash_table_lookup (sa->parameters, parameter);
994
995 if (variant != NULL)
996 return mcd_storage_coerce_variant_to_value (variant, value, error);
997
998 /* OK, we don't have it as a variant. How about the keyfile-escaped
999 * version? */
1000 escaped = g_hash_table_lookup (sa->escaped_parameters, parameter);
1001
1002 if (escaped == NULL)
1003 {
1004 g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
1005 "Parameter '%s' not stored by account %s", parameter, account);
1006 return FALSE;
1007 }
1008
1009 return mcd_keyfile_unescape_value (escaped, value, error);
1010 }
1011
1012 static gboolean
mcpa_unescape_value_from_keyfile(const McpAccountManager * unused G_GNUC_UNUSED,const gchar * escaped,GValue * value,GError ** error)1013 mcpa_unescape_value_from_keyfile (const McpAccountManager *unused G_GNUC_UNUSED,
1014 const gchar *escaped,
1015 GValue *value,
1016 GError **error)
1017 {
1018 return mcd_keyfile_unescape_value (escaped, value, error);
1019 }
1020
1021 /*
1022 * @escaped: a keyfile-escaped string
1023 * @value: a #GValue initialized with a supported #GType
1024 * @error: used to raise an error if %FALSE is returned
1025 *
1026 * Try to interpret @escaped as a value of the type of @value. If we can,
1027 * write the resulting value into @value and return %TRUE.
1028 *
1029 * Returns: %TRUE if @escaped could be interpreted as a value of that type
1030 */
1031 gboolean
mcd_keyfile_unescape_value(const gchar * escaped,GValue * value,GError ** error)1032 mcd_keyfile_unescape_value (const gchar *escaped,
1033 GValue *value,
1034 GError **error)
1035 {
1036 GKeyFile *keyfile;
1037 gboolean ret;
1038
1039 g_return_val_if_fail (escaped != NULL, FALSE);
1040 g_return_val_if_fail (G_IS_VALUE (value), FALSE);
1041
1042 keyfile = g_key_file_new ();
1043 g_key_file_set_value (keyfile, "g", "k", escaped);
1044 ret = mcd_keyfile_get_value (keyfile, "g", "k", value, error);
1045 g_key_file_free (keyfile);
1046 return ret;
1047 }
1048
1049 /*
1050 * mcd_keyfile_get_value:
1051 * @keyfile: A #GKeyFile
1052 * @group: name of a group
1053 * @key: name of a key
1054 * @value: location to return the value, initialized to the right #GType
1055 * @error: a place to store any #GError<!-- -->s that occur
1056 */
1057 gboolean
mcd_keyfile_get_value(GKeyFile * keyfile,const gchar * group,const gchar * key,GValue * value,GError ** error)1058 mcd_keyfile_get_value (GKeyFile *keyfile,
1059 const gchar *group,
1060 const gchar *key,
1061 GValue *value,
1062 GError **error)
1063 {
1064 gboolean ret = FALSE;
1065 GType type;
1066
1067 g_return_val_if_fail (keyfile != NULL, FALSE);
1068 g_return_val_if_fail (group != NULL, FALSE);
1069 g_return_val_if_fail (key != NULL, FALSE);
1070 g_return_val_if_fail (G_IS_VALUE (value), FALSE);
1071
1072 type = G_VALUE_TYPE (value);
1073
1074 switch (type)
1075 {
1076 case G_TYPE_STRING:
1077 {
1078 gchar *v_string = g_key_file_get_string (keyfile, group,
1079 key, error);
1080
1081 if (v_string != NULL)
1082 {
1083 g_value_take_string (value, v_string);
1084 ret = TRUE;
1085 }
1086 /* else error is already set */
1087 }
1088 break;
1089
1090 case G_TYPE_INT:
1091 {
1092 GError *e = NULL;
1093 gint v_int = g_key_file_get_integer (keyfile, group,
1094 key, &e);
1095
1096 if (e != NULL)
1097 {
1098 g_propagate_error (error, e);
1099 }
1100 else
1101 {
1102 g_value_set_int (value, v_int);
1103 ret = TRUE;
1104 }
1105 }
1106 break;
1107
1108 case G_TYPE_INT64:
1109 {
1110 GError *e = NULL;
1111 gint64 v_int = g_key_file_get_int64 (keyfile, group,
1112 key, &e);
1113
1114 if (e != NULL)
1115 {
1116 g_propagate_error (error, e);
1117 }
1118 else
1119 {
1120 g_value_set_int64 (value, v_int);
1121 ret = TRUE;
1122 }
1123 }
1124 break;
1125
1126 case G_TYPE_UINT:
1127 {
1128 GError *e = NULL;
1129 guint64 v_uint = g_key_file_get_uint64 (keyfile, group,
1130 key, &e);
1131
1132 if (e != NULL)
1133 {
1134 g_propagate_error (error, e);
1135 }
1136 else if (v_uint > G_MAXUINT32)
1137 {
1138 g_set_error (error, MCD_ACCOUNT_ERROR,
1139 MCD_ACCOUNT_ERROR_GET_PARAMETER,
1140 "Parameter '%s' out of range for an unsigned 32-bit "
1141 "integer: %" G_GUINT64_FORMAT, key, v_uint);
1142 }
1143 else
1144 {
1145 g_value_set_uint (value, v_uint);
1146 ret = TRUE;
1147 }
1148 }
1149 break;
1150
1151 case G_TYPE_UCHAR:
1152 {
1153 GError *e = NULL;
1154 gint v_int = g_key_file_get_integer (keyfile, group,
1155 key, &e);
1156
1157 if (e != NULL)
1158 {
1159 g_propagate_error (error, e);
1160 }
1161 else if (v_int < 0 || v_int > 0xFF)
1162 {
1163 g_set_error (error, MCD_ACCOUNT_ERROR,
1164 MCD_ACCOUNT_ERROR_GET_PARAMETER,
1165 "Parameter '%s' out of range for an unsigned byte: "
1166 "%d", key, v_int);
1167 }
1168 else
1169 {
1170 g_value_set_uchar (value, v_int);
1171 ret = TRUE;
1172 }
1173 }
1174 break;
1175
1176 case G_TYPE_UINT64:
1177 {
1178 GError *e = NULL;
1179 guint64 v_uint = g_key_file_get_uint64 (keyfile, group,
1180 key, &e);
1181
1182 if (e != NULL)
1183 {
1184 g_propagate_error (error, e);
1185 }
1186 else
1187 {
1188 g_value_set_uint64 (value, v_uint);
1189 ret = TRUE;
1190 }
1191 }
1192 break;
1193
1194 case G_TYPE_BOOLEAN:
1195 {
1196 GError *e = NULL;
1197 gboolean v_bool = g_key_file_get_boolean (keyfile, group,
1198 key, &e);
1199
1200 if (e != NULL)
1201 {
1202 g_propagate_error (error, e);
1203 }
1204 else
1205 {
1206 g_value_set_boolean (value, v_bool);
1207 ret = TRUE;
1208 }
1209 }
1210 break;
1211
1212 case G_TYPE_DOUBLE:
1213 {
1214 GError *e = NULL;
1215 gdouble v_double = g_key_file_get_double (keyfile, group,
1216 key, &e);
1217
1218 if (e != NULL)
1219 {
1220 g_propagate_error (error, e);
1221 }
1222 else
1223 {
1224 g_value_set_double (value, v_double);
1225 ret = TRUE;
1226 }
1227 }
1228 break;
1229
1230 default:
1231 if (type == G_TYPE_STRV)
1232 {
1233 gchar **v = g_key_file_get_string_list (keyfile, group,
1234 key, NULL, error);
1235
1236 if (v != NULL)
1237 {
1238 g_value_take_boxed (value, v);
1239 ret = TRUE;
1240 }
1241 }
1242 else if (type == DBUS_TYPE_G_OBJECT_PATH)
1243 {
1244 gchar *v_string = g_key_file_get_string (keyfile, group,
1245 key, error);
1246
1247 if (v_string == NULL)
1248 {
1249 /* do nothing, error is already set */
1250 }
1251 else if (!tp_dbus_check_valid_object_path (v_string, NULL))
1252 {
1253 g_set_error (error, MCD_ACCOUNT_ERROR,
1254 MCD_ACCOUNT_ERROR_GET_PARAMETER,
1255 "Invalid object path %s", v_string);
1256 g_free (v_string);
1257 }
1258 else
1259 {
1260 g_value_take_boxed (value, v_string);
1261 ret = TRUE;
1262 }
1263 }
1264 else if (type == TP_ARRAY_TYPE_OBJECT_PATH_LIST)
1265 {
1266 gchar **v = g_key_file_get_string_list (keyfile, group,
1267 key, NULL, error);
1268
1269 if (v != NULL)
1270 {
1271 gchar **iter;
1272 GPtrArray *arr = g_ptr_array_new ();
1273
1274 for (iter = v; iter != NULL && *iter != NULL; iter++)
1275 {
1276 if (!g_variant_is_object_path (*iter))
1277 {
1278 g_set_error (error, MCD_ACCOUNT_ERROR,
1279 MCD_ACCOUNT_ERROR_GET_PARAMETER,
1280 "Invalid object path %s stored in keyfile", *iter);
1281 g_strfreev (v);
1282 v = NULL;
1283 break;
1284 }
1285 }
1286
1287 for (iter = v; iter != NULL && *iter != NULL; iter++)
1288 {
1289 /* transfer ownership from v to arr */
1290 g_ptr_array_add (arr, *iter);
1291 }
1292
1293 /* not g_strfreev - the strings' ownership has been
1294 * transferred */
1295 g_free (v);
1296
1297 g_value_take_boxed (value, arr);
1298 ret = TRUE;
1299 }
1300 }
1301 else if (type == TP_STRUCT_TYPE_SIMPLE_PRESENCE)
1302 {
1303 gchar **v = g_key_file_get_string_list (keyfile, group,
1304 key, NULL, error);
1305
1306 if (v == NULL)
1307 {
1308 /* error is already set, do nothing */
1309 }
1310 else if (g_strv_length (v) != 3)
1311 {
1312 g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
1313 "Invalid simple-presence structure stored in keyfile");
1314 }
1315 else
1316 {
1317 guint64 u;
1318 gchar *endptr;
1319
1320 errno = 0;
1321 u = g_ascii_strtoull (v[0], &endptr, 10);
1322
1323 if (errno != 0 || *endptr != '\0' || u > G_MAXUINT32)
1324 {
1325 g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
1326 "Invalid presence type stored in keyfile: %s", v[0]);
1327 }
1328 else
1329 {
1330 /* a syntactically valid simple presence */
1331 g_value_take_boxed (value,
1332 tp_value_array_build (3,
1333 G_TYPE_UINT, (guint) u,
1334 G_TYPE_STRING, v[1],
1335 G_TYPE_STRING, v[2],
1336 G_TYPE_INVALID));
1337 ret = TRUE;
1338 }
1339 }
1340
1341 g_strfreev (v);
1342 }
1343 else
1344 {
1345 gchar *message =
1346 g_strdup_printf ("cannot get key %s from group %s: "
1347 "unknown type %s",
1348 key, group, g_type_name (type));
1349
1350 g_warning ("%s: %s", G_STRFUNC, message);
1351 g_set_error (error, MCD_ACCOUNT_ERROR,
1352 MCD_ACCOUNT_ERROR_GET_PARAMETER,
1353 "%s", message);
1354 g_free (message);
1355 }
1356 }
1357
1358 return ret;
1359 }
1360
1361 /*
1362 * mcd_storage_get_boolean:
1363 * @storage: An object implementing the #McdStorage interface
1364 * @account: unique name of the account
1365 * @key: name of the attribute to be retrieved
1366 *
1367 * Returns: a #gboolean. Unset/unparseable values are returned as %FALSE
1368 */
1369 gboolean
mcd_storage_get_boolean(McdStorage * self,const gchar * account,const gchar * attribute)1370 mcd_storage_get_boolean (McdStorage *self,
1371 const gchar *account,
1372 const gchar *attribute)
1373 {
1374 GValue tmp = G_VALUE_INIT;
1375
1376 g_return_val_if_fail (MCD_IS_STORAGE (self), FALSE);
1377 g_return_val_if_fail (account != NULL, FALSE);
1378 g_return_val_if_fail (attribute != NULL, FALSE);
1379 g_return_val_if_fail (!g_str_has_prefix (attribute, "param-"), FALSE);
1380
1381 g_value_init (&tmp, G_TYPE_BOOLEAN);
1382
1383 if (!mcd_storage_get_attribute (self, account, attribute, &tmp, NULL))
1384 return FALSE;
1385
1386 return g_value_get_boolean (&tmp);
1387 }
1388
1389 /*
1390 * mcd_storage_get_integer:
1391 * @storage: An object implementing the #McdStorage interface
1392 * @account: unique name of the account
1393 * @attribute: name of the attribute to be retrieved
1394 *
1395 * Returns: a #gint. Unset or non-numeric values are returned as 0
1396 */
1397 gint
mcd_storage_get_integer(McdStorage * self,const gchar * account,const gchar * attribute)1398 mcd_storage_get_integer (McdStorage *self,
1399 const gchar *account,
1400 const gchar *attribute)
1401 {
1402 GValue tmp = G_VALUE_INIT;
1403
1404 g_return_val_if_fail (MCD_IS_STORAGE (self), 0);
1405 g_return_val_if_fail (account != NULL, 0);
1406 g_return_val_if_fail (attribute != NULL, 0);
1407 g_return_val_if_fail (!g_str_has_prefix (attribute, "param-"), 0);
1408
1409 g_value_init (&tmp, G_TYPE_INT);
1410
1411 if (!mcd_storage_get_attribute (self, account, attribute, &tmp, NULL))
1412 return FALSE;
1413
1414 return g_value_get_int (&tmp);
1415 }
1416
1417 static void
update_storage(McdStorage * self,const gchar * account,const gchar * key,GVariant * variant,const gchar * escaped,gboolean secret)1418 update_storage (McdStorage *self,
1419 const gchar *account,
1420 const gchar *key,
1421 GVariant *variant,
1422 const gchar *escaped,
1423 gboolean secret)
1424 {
1425 GList *store;
1426 gboolean done = FALSE;
1427 gboolean parameter = g_str_has_prefix (key, "param-");
1428 McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self);
1429
1430 if (secret)
1431 mcd_storage_make_secret (self, account, key);
1432
1433 /* we're deleting, which is unconditional, no need to check if anyone *
1434 * claims this setting for themselves */
1435 if (escaped == NULL)
1436 done = TRUE;
1437
1438 for (store = stores; store != NULL; store = g_list_next (store))
1439 {
1440 McpAccountStorage *plugin = store->data;
1441 const gchar *pn = mcp_account_storage_name (plugin);
1442
1443 if (done)
1444 {
1445 DEBUG ("MCP:%s -> delete %s.%s", pn, account, key);
1446 mcp_account_storage_delete (plugin, ma, account, key);
1447 }
1448 else if (variant != NULL && !parameter &&
1449 mcp_account_storage_set_attribute (plugin, ma, account, key, variant,
1450 MCP_ATTRIBUTE_FLAG_NONE))
1451 {
1452 done = TRUE;
1453 DEBUG ("MCP:%s -> store attribute %s.%s", pn, account, key);
1454 }
1455 else if (variant != NULL && parameter &&
1456 mcp_account_storage_set_parameter (plugin, ma, account, key + 6,
1457 variant,
1458 secret ? MCP_PARAMETER_FLAG_SECRET : MCP_PARAMETER_FLAG_NONE))
1459 {
1460 done = TRUE;
1461 DEBUG ("MCP:%s -> store parameter %s.%s", pn, account, key);
1462 }
1463 else
1464 {
1465 done = mcp_account_storage_set (plugin, ma, account, key, escaped);
1466 DEBUG ("MCP:%s -> %s %s.%s",
1467 pn, done ? "store" : "ignore", account, key);
1468 }
1469 }
1470 }
1471
1472 /*
1473 * mcd_storage_set_string:
1474 * @storage: An object implementing the #McdStorage interface
1475 * @account: the unique name of an account
1476 * @key: the name of the attribute
1477 * @value: the value to be stored (or %NULL to erase it)
1478 *
1479 * Copies and stores the supplied @value (or removes it if %NULL) to the
1480 * internal cache.
1481 *
1482 * Returns: a #gboolean indicating whether the cache actually required an
1483 * update (so that the caller can decide whether to request a commit to
1484 * long term storage or not). %TRUE indicates the cache was updated and
1485 * may not be in sync with the store any longer, %FALSE indicates we already
1486 * held the value supplied.
1487 */
1488 gboolean
mcd_storage_set_string(McdStorage * self,const gchar * account,const gchar * attribute,const gchar * val)1489 mcd_storage_set_string (McdStorage *self,
1490 const gchar *account,
1491 const gchar *attribute,
1492 const gchar *val)
1493 {
1494 gboolean updated;
1495
1496 g_return_val_if_fail (MCD_IS_STORAGE (self), FALSE);
1497 g_return_val_if_fail (account != NULL, FALSE);
1498 g_return_val_if_fail (attribute != NULL, FALSE);
1499 g_return_val_if_fail (!g_str_has_prefix (attribute, "param-"), FALSE);
1500
1501 if (val == NULL)
1502 {
1503 updated = mcd_storage_set_attribute (self, account, attribute, NULL);
1504 }
1505 else
1506 {
1507 GValue tmp = G_VALUE_INIT;
1508
1509 g_value_init (&tmp, G_TYPE_STRING);
1510 g_value_set_string (&tmp, val);
1511 updated = mcd_storage_set_attribute (self, account, attribute, &tmp);
1512 g_value_unset (&tmp);
1513 }
1514
1515 return updated;
1516 }
1517
1518 /*
1519 * mcd_storage_set_attribute:
1520 * @storage: An object implementing the #McdStorage interface
1521 * @account: the unique name of an account
1522 * @attribute: the name of the attribute, e.g. "DisplayName"
1523 * @value: the value to be stored (or %NULL to erase it)
1524 *
1525 * Copies and stores the supplied @value (or removes it if %NULL) in the
1526 * internal cache.
1527 *
1528 * Returns: a #gboolean indicating whether the cache actually required an
1529 * update (so that the caller can decide whether to request a commit to
1530 * long term storage or not). %TRUE indicates the cache was updated and
1531 * may not be in sync with the store any longer, %FALSE indicates we already
1532 * held the value supplied.
1533 */
1534 gboolean
mcd_storage_set_attribute(McdStorage * self,const gchar * account,const gchar * attribute,const GValue * value)1535 mcd_storage_set_attribute (McdStorage *self,
1536 const gchar *account,
1537 const gchar *attribute,
1538 const GValue *value)
1539 {
1540 McdStorageAccount *sa;
1541 GVariant *old_v;
1542 GVariant *new_v;
1543 gboolean updated = FALSE;
1544
1545 g_return_val_if_fail (MCD_IS_STORAGE (self), FALSE);
1546 g_return_val_if_fail (account != NULL, FALSE);
1547 g_return_val_if_fail (attribute != NULL, FALSE);
1548 g_return_val_if_fail (!g_str_has_prefix (attribute, "param-"), FALSE);
1549
1550 sa = ensure_account (self, account);
1551
1552 if (value != NULL)
1553 new_v = g_variant_ref_sink (dbus_g_value_build_g_variant (value));
1554 else
1555 new_v = NULL;
1556
1557 old_v = g_hash_table_lookup (sa->attributes, attribute);
1558
1559 if (!mcd_nullable_variant_equal (old_v, new_v))
1560 {
1561 gchar *escaped = NULL;
1562
1563 /* First put it in the attributes hash table. (Watch out, this might
1564 * invalidate old_v.) */
1565 if (new_v == NULL)
1566 g_hash_table_remove (sa->attributes, attribute);
1567 else
1568 g_hash_table_insert (sa->attributes, g_strdup (attribute),
1569 g_variant_ref (new_v));
1570
1571 /* OK now we have to escape it in a stupid way for plugins */
1572 if (value != NULL)
1573 escaped = mcd_keyfile_escape_value (value);
1574
1575 update_storage (self, account, attribute, new_v, escaped, FALSE);
1576 g_free (escaped);
1577 updated = TRUE;
1578 }
1579
1580 tp_clear_pointer (&new_v, g_variant_unref);
1581 return updated;
1582 }
1583
1584 /*
1585 * mcd_storage_set_parameter:
1586 * @storage: An object implementing the #McdStorage interface
1587 * @account: the unique name of an account
1588 * @parameter: the name of the parameter, e.g. "account"
1589 * @value: the value to be stored (or %NULL to erase it)
1590 * @secret: whether the value is confidential (might get stored in the
1591 * keyring, for example)
1592 *
1593 * Copies and stores the supplied @value (or removes it if %NULL) in the
1594 * internal cache.
1595 *
1596 * Returns: a #gboolean indicating whether the cache actually required an
1597 * update (so that the caller can decide whether to request a commit to
1598 * long term storage or not). %TRUE indicates the cache was updated and
1599 * may not be in sync with the store any longer, %FALSE indicates we already
1600 * held the value supplied.
1601 */
1602 gboolean
mcd_storage_set_parameter(McdStorage * self,const gchar * account,const gchar * parameter,const GValue * value,gboolean secret)1603 mcd_storage_set_parameter (McdStorage *self,
1604 const gchar *account,
1605 const gchar *parameter,
1606 const GValue *value,
1607 gboolean secret)
1608 {
1609 GVariant *old_v;
1610 GVariant *new_v = NULL;
1611 const gchar *old_escaped;
1612 gchar *new_escaped = NULL;
1613 McdStorageAccount *sa;
1614 gboolean updated = FALSE;
1615
1616 g_return_val_if_fail (MCD_IS_STORAGE (self), FALSE);
1617 g_return_val_if_fail (account != NULL, FALSE);
1618 g_return_val_if_fail (parameter != NULL, FALSE);
1619
1620 sa = ensure_account (self, account);
1621
1622 if (value != NULL)
1623 {
1624 new_escaped = mcd_keyfile_escape_value (value);
1625 new_v = g_variant_ref_sink (dbus_g_value_build_g_variant (value));
1626 }
1627
1628 old_v = g_hash_table_lookup (sa->parameters, parameter);
1629 old_escaped = g_hash_table_lookup (sa->escaped_parameters, parameter);
1630
1631 if (old_v != NULL)
1632 updated = !mcd_nullable_variant_equal (old_v, new_v);
1633 else if (old_escaped != NULL)
1634 updated = tp_strdiff (old_escaped, new_escaped);
1635 else
1636 updated = (value != NULL);
1637
1638 if (updated)
1639 {
1640 gchar key[MAX_KEY_LENGTH];
1641
1642 g_hash_table_remove (sa->parameters, parameter);
1643 g_hash_table_remove (sa->escaped_parameters, parameter);
1644
1645 if (new_v != NULL)
1646 g_hash_table_insert (sa->parameters, g_strdup (parameter),
1647 g_variant_ref (new_v));
1648
1649 g_snprintf (key, sizeof (key), "param-%s", parameter);
1650 update_storage (self, account, key, new_v, new_escaped, secret);
1651 return TRUE;
1652 }
1653
1654 g_free (new_escaped);
1655 tp_clear_pointer (&new_v, g_variant_unref);
1656 return updated;
1657 }
1658
1659 static gchar *
mcpa_escape_value_for_keyfile(const McpAccountManager * unused G_GNUC_UNUSED,const GValue * value)1660 mcpa_escape_value_for_keyfile (const McpAccountManager *unused G_GNUC_UNUSED,
1661 const GValue *value)
1662 {
1663 return mcd_keyfile_escape_value (value);
1664 }
1665
1666 /*
1667 * @value: a populated #GValue of a supported #GType
1668 *
1669 * Escape the contents of @value to go in a #GKeyFile. Return the
1670 * value that would go in the keyfile.
1671 *
1672 * For instance, for a boolean value TRUE this would return "true",
1673 * and for a string containing one space, it would return "\s".
1674 */
1675 gchar *
mcd_keyfile_escape_value(const GValue * value)1676 mcd_keyfile_escape_value (const GValue *value)
1677 {
1678 GKeyFile *keyfile;
1679 gchar *ret;
1680
1681 g_return_val_if_fail (G_IS_VALUE (value), NULL);
1682
1683 keyfile = g_key_file_new ();
1684 mcd_keyfile_set_value (keyfile, "g", "k", value);
1685 ret = g_key_file_get_value (keyfile, "g", "k", NULL);
1686 g_key_file_free (keyfile);
1687 return ret;
1688 }
1689
1690 static gchar *
mcpa_escape_variant_for_keyfile(const McpAccountManager * unused G_GNUC_UNUSED,GVariant * variant)1691 mcpa_escape_variant_for_keyfile (const McpAccountManager *unused G_GNUC_UNUSED,
1692 GVariant *variant)
1693 {
1694 return mcd_keyfile_escape_variant (variant);
1695 }
1696
1697 /*
1698 * mcd_keyfile_set_value:
1699 * @keyfile: a keyfile
1700 * @name: the name of a group
1701 * @key: the key in the group
1702 * @value: the value to be stored (or %NULL to erase it)
1703 *
1704 * Copies and stores the supplied @value (or removes it if %NULL) to the
1705 * internal cache.
1706 *
1707 * Returns: a #gboolean indicating whether the cache actually required an
1708 * update (so that the caller can decide whether to request a commit to
1709 * long term storage or not). %TRUE indicates the cache was updated and
1710 * may not be in sync with the store any longer, %FALSE indicates we already
1711 * held the value supplied.
1712 */
1713 gboolean
mcd_keyfile_set_value(GKeyFile * keyfile,const gchar * name,const gchar * key,const GValue * value)1714 mcd_keyfile_set_value (GKeyFile *keyfile,
1715 const gchar *name,
1716 const gchar *key,
1717 const GValue *value)
1718 {
1719 g_return_val_if_fail (name != NULL, FALSE);
1720 g_return_val_if_fail (key != NULL, FALSE);
1721
1722 if (value == NULL)
1723 {
1724 gchar *old = g_key_file_get_value (keyfile, name, key, NULL);
1725 gboolean updated = (old != NULL);
1726
1727 g_free (old);
1728 g_key_file_remove_key (keyfile, name, key, NULL);
1729 return updated;
1730 }
1731 else
1732 {
1733 gboolean updated = FALSE;
1734 gchar *old = g_key_file_get_value (keyfile, name, key, NULL);
1735 gchar *new = NULL;
1736 gchar *buf = NULL;
1737
1738 switch (G_VALUE_TYPE (value))
1739 {
1740 case G_TYPE_STRING:
1741 g_key_file_set_string (keyfile, name, key,
1742 g_value_get_string (value));
1743 break;
1744
1745 case G_TYPE_UINT:
1746 buf = g_strdup_printf ("%u", g_value_get_uint (value));
1747 break;
1748
1749 case G_TYPE_INT:
1750 g_key_file_set_integer (keyfile, name, key,
1751 g_value_get_int (value));
1752 break;
1753
1754 case G_TYPE_BOOLEAN:
1755 g_key_file_set_boolean (keyfile, name, key,
1756 g_value_get_boolean (value));
1757 break;
1758
1759 case G_TYPE_UCHAR:
1760 buf = g_strdup_printf ("%u", g_value_get_uchar (value));
1761 break;
1762
1763 case G_TYPE_UINT64:
1764 buf = g_strdup_printf ("%" G_GUINT64_FORMAT,
1765 g_value_get_uint64 (value));
1766 break;
1767
1768 case G_TYPE_INT64:
1769 buf = g_strdup_printf ("%" G_GINT64_FORMAT,
1770 g_value_get_int64 (value));
1771 break;
1772
1773 case G_TYPE_DOUBLE:
1774 g_key_file_set_double (keyfile, name, key,
1775 g_value_get_double (value));
1776 break;
1777
1778 default:
1779 if (G_VALUE_HOLDS (value, G_TYPE_STRV))
1780 {
1781 gchar **strings = g_value_get_boxed (value);
1782
1783 g_key_file_set_string_list (keyfile, name, key,
1784 (const gchar **)strings,
1785 g_strv_length (strings));
1786 }
1787 else if (G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH))
1788 {
1789 g_key_file_set_string (keyfile, name, key,
1790 g_value_get_boxed (value));
1791 }
1792 else if (G_VALUE_HOLDS (value, TP_ARRAY_TYPE_OBJECT_PATH_LIST))
1793 {
1794 GPtrArray *arr = g_value_get_boxed (value);
1795
1796 g_key_file_set_string_list (keyfile, name, key,
1797 (const gchar * const *) arr->pdata, arr->len);
1798 }
1799 else if (G_VALUE_HOLDS (value, TP_STRUCT_TYPE_SIMPLE_PRESENCE))
1800 {
1801 guint type;
1802 /* enough for "4294967296" + \0 */
1803 gchar printf_buf[11];
1804 const gchar * strv[4] = { NULL, NULL, NULL, NULL };
1805
1806 tp_value_array_unpack (g_value_get_boxed (value), 3,
1807 &type,
1808 &(strv[1]),
1809 &(strv[2]));
1810 g_snprintf (printf_buf, sizeof (printf_buf), "%u", type);
1811 strv[0] = printf_buf;
1812
1813 g_key_file_set_string_list (keyfile, name, key, strv, 3);
1814 }
1815 else
1816 {
1817 g_warning ("Unexpected param type %s",
1818 G_VALUE_TYPE_NAME (value));
1819 return FALSE;
1820 }
1821 }
1822
1823 if (buf != NULL)
1824 g_key_file_set_string (keyfile, name, key, buf);
1825
1826 new = g_key_file_get_value (keyfile, name, key, NULL);
1827
1828 if (tp_strdiff (old, new))
1829 updated = TRUE;
1830
1831 g_free (new);
1832 g_free (buf);
1833 g_free (old);
1834
1835 return updated;
1836 }
1837 }
1838
1839 /*
1840 * mcd_storage_create_account:
1841 * @storage: An object implementing the #McdStorage interface
1842 * @provider: the desired storage provider, or %NULL
1843 * @manager: the name of the manager
1844 * @protocol: the name of the protocol
1845 * @params: A gchar * / GValue * hash table of account parameters
1846 * @error: a #GError to fill when returning %NULL
1847 *
1848 * Create a new account in storage. This should not store any
1849 * information on the long term storage until mcd_storage_commit() is called.
1850 *
1851 * See mcp_account_storage_create().
1852 *
1853 * Returns: the unique name to use for the new account, or %NULL on error.
1854 */
1855 gchar *
mcd_storage_create_account(McdStorage * self,const gchar * provider,const gchar * manager,const gchar * protocol,GHashTable * params,GError ** error)1856 mcd_storage_create_account (McdStorage *self,
1857 const gchar *provider,
1858 const gchar *manager,
1859 const gchar *protocol,
1860 GHashTable *params,
1861 GError **error)
1862 {
1863 GList *store;
1864 McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self);
1865
1866 g_return_val_if_fail (MCD_IS_STORAGE (self), NULL);
1867 g_return_val_if_fail (!tp_str_empty (manager), NULL);
1868 g_return_val_if_fail (!tp_str_empty (protocol), NULL);
1869
1870 /* If a storage provider is specified, use only it or fail */
1871 if (provider != NULL)
1872 {
1873 for (store = stores; store != NULL; store = g_list_next (store))
1874 {
1875 McpAccountStorage *plugin = store->data;
1876
1877 if (!tp_strdiff (mcp_account_storage_provider (plugin), provider))
1878 {
1879 return mcp_account_storage_create (plugin, ma, manager,
1880 protocol, params, error);
1881 }
1882 }
1883
1884 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
1885 "Storage provider '%s' does not exist", provider);
1886
1887 return NULL;
1888 }
1889
1890 /* No provider specified, let's pick the first plugin able to create this
1891 * account in priority order.
1892 *
1893 * FIXME: This is rather subtle, and relies on the fact that accounts
1894 * aren't always strongly tied to a single plugin.
1895 *
1896 * For plugins that only store their accounts set up specifically
1897 * through them (like the libaccounts/SSO pseudo-plugin,
1898 * McdAccountManagerSSO), create() will fail as unimplemented,
1899 * and we'll fall through to the next plugin. Eventually we'll
1900 * reach the default keyfile+gnome-keyring plugin, or another
1901 * plugin that accepts arbitrary accounts. When set() is called,
1902 * the libaccounts/SSO plugin will reject that too, and again,
1903 * we'll fall through to a plugin that accepts arbitrary
1904 * accounts.
1905 *
1906 * Plugins that will accept arbitrary accounts being created
1907 * via D-Bus (like the default keyfile+gnome-keyring plugin,
1908 * and the account-diversion plugin in tests/twisted)
1909 * should, in principle, implement create() to be successful.
1910 * If they do, their create() will succeed, and later, so will
1911 * their set().
1912 *
1913 * We can't necessarily rely on all such plugins implementing
1914 * create(), because it isn't a mandatory part of the plugin
1915 * API (it was added later). However, as it happens, the
1916 * default plugin returns successfully from create() without
1917 * really doing anything. When we iterate through the accounts again
1918 * to call set(), higher-priority plugins are given a second
1919 * chance to intercept that; so we end up with create() in
1920 * the default plugin being followed by set() from the
1921 * higher-priority plugin. In theory that's bad because it
1922 * splits the account across two plugins, but in practice
1923 * it isn't a problem because the default plugin's create()
1924 * doesn't really do anything anyway.
1925 */
1926 for (store = stores; store != NULL; store = g_list_next (store))
1927 {
1928 McpAccountStorage *plugin = store->data;
1929 gchar *ret;
1930
1931 ret = mcp_account_storage_create (plugin, ma, manager, protocol, params,
1932 error);
1933
1934 if (ret != NULL)
1935 return ret;
1936
1937 g_clear_error (error);
1938 }
1939
1940 /* This should never happen since the default storage is always able to create
1941 * an account */
1942 g_warn_if_reached ();
1943 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
1944 "None of the storage provider are able to create the account");
1945
1946 return NULL;
1947 }
1948
1949
1950 /*
1951 * mcd_storage_delete_account:
1952 * @storage: An object implementing the #McdStorage interface
1953 * @account: unique name of the account
1954 *
1955 * Removes an account's settings from long term storage.
1956 * This does not handle any of the other logic to do with removing
1957 * accounts, it merely ensures that no trace of the account remains
1958 * in long term storage once mcd_storage_commit() has been called.
1959 */
1960 void
mcd_storage_delete_account(McdStorage * self,const gchar * account)1961 mcd_storage_delete_account (McdStorage *self,
1962 const gchar *account)
1963 {
1964 GList *store;
1965 McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self);
1966
1967 g_return_if_fail (MCD_IS_STORAGE (self));
1968 g_return_if_fail (account != NULL);
1969
1970 g_hash_table_remove (self->accounts, account);
1971
1972 for (store = stores; store != NULL; store = g_list_next (store))
1973 {
1974 McpAccountStorage *plugin = store->data;
1975
1976 mcp_account_storage_delete (plugin, ma, account, NULL);
1977 }
1978 }
1979
1980 /*
1981 * mcd_storage_commit:
1982 * @storage: An object implementing the #McdStorage interface
1983 * @account: the unique name of an account
1984 *
1985 * Sync the long term storage (whatever it might be) with the current
1986 * state of our internal cache.
1987 */
1988 void
mcd_storage_commit(McdStorage * self,const gchar * account)1989 mcd_storage_commit (McdStorage *self, const gchar *account)
1990 {
1991 GList *store;
1992 McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self);
1993
1994 g_return_if_fail (MCD_IS_STORAGE (self));
1995
1996 for (store = stores; store != NULL; store = g_list_next (store))
1997 {
1998 McpAccountStorage *plugin = store->data;
1999 const gchar *pname = mcp_account_storage_name (plugin);
2000
2001 if (account != NULL)
2002 {
2003 DEBUG ("flushing plugin %s %s to long term storage", pname, account);
2004 mcp_account_storage_commit_one (plugin, ma, account);
2005 }
2006 else
2007 {
2008 DEBUG ("flushing plugin %s to long term storage", pname);
2009 mcp_account_storage_commit (plugin, ma);
2010 }
2011 }
2012 }
2013
2014 /*
2015 * mcd_storage_set_strv:
2016 * @storage: An object implementing the #McdStorage interface
2017 * @account: the unique name of an account
2018 * @attribute: the name of the attribute
2019 * @strv: the string vector to be stored (where %NULL is treated as equivalent
2020 * to an empty vector)
2021 *
2022 * Copies and stores the supplied string vector to the internal cache.
2023 *
2024 * Returns: a #gboolean indicating whether the cache actually required an
2025 * update (so that the caller can decide whether to request a commit to
2026 * long term storage or not). %TRUE indicates the cache was updated and
2027 * may not be in sync with the store any longer, %FALSE indicates we already
2028 * held the value supplied.
2029 */
2030 gboolean
mcd_storage_set_strv(McdStorage * storage,const gchar * account,const gchar * attribute,const gchar * const * strv)2031 mcd_storage_set_strv (McdStorage *storage,
2032 const gchar *account,
2033 const gchar *attribute,
2034 const gchar * const *strv)
2035 {
2036 GValue v = G_VALUE_INIT;
2037 static const gchar * const *empty = { NULL };
2038 gboolean ret;
2039
2040 g_return_val_if_fail (MCD_IS_STORAGE (storage), FALSE);
2041 g_return_val_if_fail (account != NULL, FALSE);
2042 g_return_val_if_fail (attribute != NULL, FALSE);
2043 g_return_val_if_fail (!g_str_has_prefix (attribute, "param-"), FALSE);
2044
2045 g_value_init (&v, G_TYPE_STRV);
2046 g_value_set_static_boxed (&v, strv == NULL ? empty : strv);
2047 ret = mcd_storage_set_attribute (storage, account, attribute, &v);
2048 g_value_unset (&v);
2049 return ret;
2050 }
2051
2052 void
mcd_storage_ready(McdStorage * self)2053 mcd_storage_ready (McdStorage *self)
2054 {
2055 GList *store;
2056 McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self);
2057
2058 for (store = stores; store != NULL; store = g_list_next (store))
2059 {
2060 McpAccountStorage *plugin = store->data;
2061 const gchar *plugin_name = mcp_account_storage_name (plugin);
2062
2063 DEBUG ("Unblocking async account ops by %s", plugin_name);
2064 mcp_account_storage_ready (plugin, ma);
2065 }
2066 }
2067
2068 static void
plugin_iface_init(McpAccountManagerIface * iface,gpointer unused G_GNUC_UNUSED)2069 plugin_iface_init (McpAccountManagerIface *iface,
2070 gpointer unused G_GNUC_UNUSED)
2071 {
2072 DEBUG ();
2073
2074 iface->get_value = get_value;
2075 iface->set_value = set_value;
2076 iface->set_attribute = mcpa_set_attribute;
2077 iface->set_parameter = mcpa_set_parameter;
2078 iface->is_secret = is_secret;
2079 iface->make_secret = make_secret;
2080 iface->unique_name = unique_name;
2081 iface->list_keys = list_keys;
2082 iface->escape_value_for_keyfile = mcpa_escape_value_for_keyfile;
2083 iface->escape_variant_for_keyfile = mcpa_escape_variant_for_keyfile;
2084 iface->unescape_value_from_keyfile = mcpa_unescape_value_from_keyfile;
2085 iface->init_value_for_attribute = mcpa_init_value_for_attribute;
2086 }
2087
2088 gboolean
mcd_storage_add_account_from_plugin(McdStorage * self,McpAccountStorage * plugin,const gchar * account)2089 mcd_storage_add_account_from_plugin (McdStorage *self,
2090 McpAccountStorage *plugin,
2091 const gchar *account)
2092 {
2093 if (!mcp_account_storage_get (plugin, MCP_ACCOUNT_MANAGER (self),
2094 account, NULL))
2095 {
2096 g_warning ("plugin %s disowned account %s",
2097 mcp_account_storage_name (plugin), account);
2098 return FALSE;
2099 }
2100
2101 return TRUE;
2102 }
2103