72625e1608cf971824da9b6b9fc322bc85800c1e
[empathy.git] / goa-mc-plugin / mcp-account-manager-goa.c
1 /*
2  * mcp-account-manager-goa.c
3  *
4  * McpAccountManagerGoa - a Mission Control plugin to expose GNOME Online
5  * Accounts with chat capabilities (e.g. Facebook) to Mission Control
6  *
7  * Copyright (C) 2010-2014 Collabora Ltd.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
21  *
22  * Authors:
23  *    Danielle Madeley <danielle.madeley@collabora.co.uk>
24  */
25
26 #include "config.h"
27 #include "mcp-account-manager-goa.h"
28
29 #define GOA_API_IS_SUBJECT_TO_CHANGE /* awesome! */
30 #include <goa/goa.h>
31
32 #define DEBUG g_debug
33 #define GET_PRIVATE(self) (((McpAccountManagerGoa *) self)->priv)
34 #define DECLARE_GASYNC_CALLBACK(name) \
35   static void name (GObject *, GAsyncResult *, gpointer);
36
37 #define PLUGIN_NAME "goa"
38 #define PLUGIN_DESCRIPTION "Provide Telepathy Accounts from GOA"
39 #define PLUGIN_PROVIDER EMPATHY_GOA_PROVIDER
40
41 #define INITIAL_COMMENT "Parameters of GOA Telepathy accounts"
42
43 #ifdef MCP_API_VERSION_5_18
44
45 # define RESTRICTIONS TpStorageRestrictionFlags
46 # define WAS_CONST /* nothing */
47   /* Its historical value was based on the KEYRING priority which no longer
48    * exists. Using a large number is OK, because it uses unusual account names
49    * which are unlikely to collide. */
50 # define PLUGIN_PRIORITY (10010)
51   /* McpAccountStorageSetResult enum is defined by MC */
52
53 #else /* MC 5.16 or older */
54
55 # define RESTRICTIONS guint
56 # define WAS_CONST const
57 # define PLUGIN_PRIORITY (MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_KEYRING + 10)
58
59   /* we use this in helper functions */
60   typedef enum {
61       MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED,
62       MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED,
63       MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED
64   } McpAccountStorageSetResult;
65
66 #endif /* MC 5.16 or older */
67
68 static void account_storage_iface_init (McpAccountStorageIface *iface);
69
70 G_DEFINE_TYPE_WITH_CODE (McpAccountManagerGoa,
71     mcp_account_manager_goa,
72     G_TYPE_OBJECT,
73     G_IMPLEMENT_INTERFACE (MCP_TYPE_ACCOUNT_STORAGE,
74       account_storage_iface_init))
75
76 struct _McpAccountManagerGoaPrivate
77 {
78   gboolean ready;
79
80   GoaClient *client;
81   GHashTable *accounts; /* alloc'ed string -> ref'ed GoaObject */
82
83   GKeyFile *store;
84   gchar *filename;
85 };
86
87
88 static void
89 mcp_account_manager_goa_dispose (GObject *self)
90 {
91   McpAccountManagerGoaPrivate *priv = GET_PRIVATE (self);
92
93   tp_clear_object (&priv->client);
94
95   G_OBJECT_CLASS (mcp_account_manager_goa_parent_class)->dispose (self);
96 }
97
98
99 static void
100 mcp_account_manager_goa_finalize (GObject *self)
101 {
102   McpAccountManagerGoaPrivate *priv = GET_PRIVATE (self);
103
104   g_hash_table_unref (priv->accounts);
105   g_key_file_free (priv->store);
106   g_free (priv->filename);
107
108   G_OBJECT_CLASS (mcp_account_manager_goa_parent_class)->finalize (self);
109 }
110
111
112 static void
113 mcp_account_manager_goa_class_init (McpAccountManagerGoaClass *klass)
114 {
115   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
116
117   gobject_class->dispose = mcp_account_manager_goa_dispose;
118   gobject_class->finalize = mcp_account_manager_goa_finalize;
119
120   g_type_class_add_private (gobject_class,
121       sizeof (McpAccountManagerGoaPrivate));
122 }
123
124 static GHashTable *
125 get_tp_parameters (GoaAccount *account)
126 {
127   GHashTable *params = g_hash_table_new_full (g_str_hash, g_str_equal,
128       NULL, g_free);
129   const char *type = goa_account_get_provider_type (account);
130
131 #define PARAM(key, value) g_hash_table_insert (params, key, g_strdup (value));
132
133   if (!tp_strdiff (type, "google"))
134     {
135       PARAM ("manager", "gabble");
136       PARAM ("protocol", "jabber");
137       PARAM ("Icon", "im-google-talk");
138       PARAM ("Service", "google-talk");
139
140       PARAM ("param-account", goa_account_get_identity (account));
141       PARAM ("param-server", "talk.google.com");
142       PARAM ("param-fallback-servers",
143           "talkx.l.google.com;"
144           "talkx.l.google.com:443,oldssl;"
145           "talkx.l.google.com:80");
146       PARAM ("param-extra-certificate-identities", "talk.google.com");
147       PARAM ("param-require-encryption", "true");
148     }
149   else if (!tp_strdiff (type, "facebook"))
150     {
151       PARAM ("manager", "gabble");
152       PARAM ("protocol", "jabber");
153       PARAM ("Icon", "im-facebook");
154       PARAM ("Service", "facebook");
155
156       PARAM ("param-account", "chat.facebook.com");
157       PARAM ("param-server", "chat.facebook.com");
158       PARAM ("param-require-encryption", "true");
159       PARAM ("param-fallback-servers",
160           "chat.facebook.com:443");
161     }
162   else
163     {
164       DEBUG ("Unknown account type %s", type);
165       g_hash_table_unref (params);
166       return NULL;
167     }
168
169   /* generic properties */
170   PARAM ("DisplayName", goa_account_get_presentation_identity (account));
171
172 #undef PARAM
173
174   return params;
175 }
176
177
178 static char *
179 get_tp_account_name (GoaAccount *account)
180 {
181   GHashTable *params = get_tp_parameters (account);
182   const char *type = goa_account_get_provider_type (account);
183   const char *id = goa_account_get_id (account);
184   char *name;
185
186   if (params == NULL)
187     return NULL;
188
189   name = g_strdup_printf ("%s/%s/goa_%s_%s",
190       (char *) g_hash_table_lookup (params, "manager"),
191       (char *) g_hash_table_lookup (params, "protocol"),
192       type, id);
193
194   g_hash_table_unref (params);
195
196   return name;
197 }
198
199 static void
200 object_chat_changed_cb (GoaObject *object,
201     GParamSpec *spec,
202     McpAccountManagerGoa *self)
203 {
204   GoaAccount *account = goa_object_peek_account (object);
205   char *name = get_tp_account_name (account);
206   gboolean enabled;
207
208   if (name == NULL)
209     return;
210
211   enabled = (goa_object_peek_chat (object) != NULL);
212
213   DEBUG ("%s %s", name, enabled ? "enabled" : "disabled");
214
215   if (self->priv->ready)
216     mcp_account_storage_emit_toggled (MCP_ACCOUNT_STORAGE (self),
217         name, enabled);
218 }
219
220 static void
221 _new_account (McpAccountManagerGoa *self,
222     GoaObject *object)
223 {
224   GoaAccount *account = goa_object_peek_account (object);
225   char *account_name = get_tp_account_name (account);
226
227   if (account_name == NULL)
228     return;
229
230   /* @account_name now is owned by the hash table */
231   g_hash_table_insert (self->priv->accounts, account_name,
232       g_object_ref (object));
233
234   if (self->priv->ready)
235     mcp_account_storage_emit_created (MCP_ACCOUNT_STORAGE (self),
236         account_name);
237
238   tp_g_signal_connect_object (object, "notify::chat",
239       G_CALLBACK (object_chat_changed_cb), self, 0);
240 }
241
242
243 DECLARE_GASYNC_CALLBACK (_goa_client_new_cb);
244
245 static void
246 load_store (McpAccountManagerGoa *self)
247 {
248   GError *error = NULL;
249
250   if (!g_key_file_load_from_file (self->priv->store, self->priv->filename,
251         G_KEY_FILE_KEEP_COMMENTS, &error))
252     {
253       gchar *dir;
254
255       DEBUG ("Failed to load keyfile, creating a new one: %s", error->message);
256
257       dir = g_path_get_dirname (self->priv->filename);
258
259       g_mkdir_with_parents (dir, 0700);
260       g_free (dir);
261
262       g_key_file_set_comment (self->priv->store, NULL, NULL, INITIAL_COMMENT,
263           NULL);
264
265       g_error_free (error);
266     }
267 }
268
269 static void
270 mcp_account_manager_goa_init (McpAccountManagerGoa *self)
271 {
272   DEBUG ("GOA MC plugin initialised");
273
274   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
275       MCP_TYPE_ACCOUNT_MANAGER_GOA, McpAccountManagerGoaPrivate);
276
277 #ifdef MCP_API_VERSION_5_18
278   /* the ready callback no longer exists, we may emit signals at any time */
279   self->priv->ready = TRUE;
280 #endif
281
282   self->priv->accounts = g_hash_table_new_full (g_str_hash, g_str_equal,
283       g_free, g_object_unref);
284
285   goa_client_new (NULL, _goa_client_new_cb, self);
286
287   /* key file store */
288   self->priv->store = g_key_file_new ();
289   self->priv->filename = g_build_filename (g_get_user_data_dir (), "telepathy",
290       "mission-control", "accounts-goa.cfg", NULL);
291
292   load_store (self);
293 }
294
295
296 static void
297 _account_added_cb (GoaClient *client,
298     GoaObject *object,
299     McpAccountManagerGoa *self)
300 {
301   _new_account (self, object);
302 }
303
304
305 static void
306 _account_removed_cb (GoaClient *client,
307     GoaObject *object,
308     McpAccountManagerGoa *self)
309 {
310   GoaAccount *account = goa_object_peek_account (object);
311   char *name = get_tp_account_name (account);
312
313   if (name == NULL)
314     return;
315
316   if (self->priv->ready)
317     mcp_account_storage_emit_deleted (MCP_ACCOUNT_STORAGE (self), name);
318
319   g_hash_table_remove (self->priv->accounts, name);
320
321   g_free (name);
322 }
323
324 static void
325 _goa_client_new_cb (GObject *obj,
326     GAsyncResult *result,
327     gpointer user_data)
328 {
329   McpAccountManagerGoa *self = user_data;
330   GList *accounts, *ptr;
331   GError *error = NULL;
332
333   self->priv->client = goa_client_new_finish (result, &error);
334
335   if (error != NULL)
336     {
337       DEBUG ("Failed to connect to GOA");
338       return;
339     }
340
341   accounts = goa_client_get_accounts (self->priv->client);
342
343   for (ptr = accounts; ptr != NULL; ptr = ptr->next)
344     {
345       _new_account (self, ptr->data);
346     }
347
348   g_list_free_full (accounts, g_object_unref);
349
350   g_signal_connect (self->priv->client, "account-added",
351       G_CALLBACK (_account_added_cb), self);
352   g_signal_connect (self->priv->client, "account-removed",
353       G_CALLBACK (_account_removed_cb), self);
354 }
355
356
357 static GList *
358 mcp_account_manager_goa_list (WAS_CONST McpAccountStorage *self,
359     WAS_CONST McpAccountManager *am)
360 {
361   McpAccountManagerGoaPrivate *priv = GET_PRIVATE (self);
362   GList *accounts = NULL;
363   GHashTableIter iter;
364   gpointer key;
365
366   DEBUG (G_STRFUNC);
367
368   g_hash_table_iter_init (&iter, priv->accounts);
369   while (g_hash_table_iter_next (&iter, &key, NULL))
370     accounts = g_list_prepend (accounts, g_strdup (key));
371
372   return accounts;
373 }
374
375
376 #ifdef MCP_API_VERSION_5_18
377
378 static GVariant *
379 get_item (McpAccountManagerGoa *self,
380     McpAccountManager *am,
381     const gchar *acc,
382     const gchar *key,
383     const GVariantType *type)
384 {
385   GoaObject *object;
386   GoaAccount *account;
387   GHashTable *bits;
388   gchar *esc;
389   GVariant *ret = NULL;
390
391   DEBUG ("%s: %s, %s", G_STRFUNC, acc, key);
392
393   object = g_hash_table_lookup (self->priv->accounts, acc);
394
395   g_return_val_if_fail (object != NULL, NULL);
396
397   account = goa_object_peek_account (object);
398
399   g_return_val_if_fail (account != NULL, NULL);
400
401   if (!tp_strdiff (key, "Enabled"))
402     {
403       return g_variant_ref_sink (g_variant_new_boolean (
404             !goa_account_get_chat_disabled (account)));
405     }
406
407   bits = get_tp_parameters (account);
408
409   esc = g_hash_table_lookup (bits, key);
410
411   if (esc == NULL)
412     esc = g_key_file_get_value (self->priv->store, acc, key, NULL);
413   else
414     esc = g_strdup (esc);
415
416   if (esc != NULL)
417     {
418       ret = mcp_account_manager_unescape_variant_from_keyfile (am,
419           esc, type, NULL);
420
421       g_free (esc);
422     }
423
424   g_hash_table_unref (bits);
425   return ret;
426 }
427
428 static GVariant *
429 mcp_account_manager_goa_get_attribute (McpAccountStorage *self,
430     McpAccountManager *am,
431     const gchar *acc,
432     const gchar *attribute,
433     const GVariantType *type,
434     McpAttributeFlags *flags)
435 {
436   return get_item (MCP_ACCOUNT_MANAGER_GOA (self), am, acc, attribute, type);
437 }
438
439 static GVariant *
440 mcp_account_manager_goa_get_parameter (McpAccountStorage *storage,
441     McpAccountManager *am,
442     const gchar *acc,
443     const gchar *parameter,
444     const GVariantType *type,
445     McpParameterFlags *flags)
446 {
447   gchar *key;
448   GVariant *ret;
449
450   key = g_strdup_printf ("param-%s", parameter);
451
452   ret = get_item (MCP_ACCOUNT_MANAGER_GOA (storage), am, acc, key, type);
453   g_free (key);
454   return ret;
455 }
456
457 #else /* MC 5.16 or older */
458
459 static void
460 get_enabled (WAS_CONST McpAccountStorage *self,
461     WAS_CONST McpAccountManager *am,
462     const gchar *acc,
463     GoaAccount *account)
464 {
465   mcp_account_manager_set_value (am, acc, "Enabled",
466       goa_account_get_chat_disabled (account) == FALSE ? "true" : "false");
467 }
468
469 static gboolean
470 mcp_account_manager_goa_get (WAS_CONST McpAccountStorage *self,
471     WAS_CONST McpAccountManager *am,
472     const gchar *acc,
473     const gchar *key)
474 {
475   McpAccountManagerGoaPrivate *priv = GET_PRIVATE (self);
476   GoaObject *object;
477   GoaAccount *account;
478
479   DEBUG ("%s: %s, %s", G_STRFUNC, acc, key);
480
481   object = g_hash_table_lookup (priv->accounts, acc);
482
483   if (object == NULL)
484     return FALSE;
485
486   account = goa_object_peek_account (object);
487
488   if (account == NULL)
489     return FALSE;
490
491   if (key == NULL)
492     {
493       /* load all keys */
494       GHashTable *params = get_tp_parameters (account);
495       GHashTableIter iter;
496       gpointer k, value;
497       GStrv keys;
498       guint i;
499       gsize nkeys = 0;
500
501       /* Properties from GOA */
502       g_hash_table_iter_init (&iter, params);
503       while (g_hash_table_iter_next (&iter, &k, &value))
504         mcp_account_manager_set_value (am, acc, k, value);
505
506       g_hash_table_unref (params);
507
508       /* Stored properties */
509       keys = g_key_file_get_keys (priv->store, acc, &nkeys, NULL);
510
511       for (i = 0; i < nkeys; i++)
512         {
513           gchar *v = g_key_file_get_value (priv->store, acc, keys[i], NULL);
514
515           if (v != NULL)
516             {
517               mcp_account_manager_set_value (am, acc, keys[i], v);
518               g_free (v);
519             }
520         }
521
522       g_strfreev (keys);
523
524       /* Enabled */
525       get_enabled (self, am, acc, account);
526     }
527   else if (!tp_strdiff (key, "Enabled"))
528     {
529       get_enabled (self, am, acc, account);
530     }
531   else
532     {
533       /* get a specific key */
534       GHashTable *params = get_tp_parameters (account);
535       gchar *value;
536
537       value = g_hash_table_lookup (params, key);
538
539       if (value == NULL)
540         value = g_key_file_get_value (priv->store, acc, key, NULL);
541       else
542         value = g_strdup (value);
543
544       mcp_account_manager_set_value (am, acc, key, value);
545
546       g_hash_table_unref (params);
547       g_free (value);
548     }
549
550   return TRUE;
551 }
552
553 #endif /* MC 5.16 or older */
554
555
556 static gboolean
557 account_is_in_goa (WAS_CONST McpAccountStorage *self,
558     const gchar *account)
559 {
560   McpAccountManagerGoaPrivate *priv = GET_PRIVATE (self);
561
562   return (g_hash_table_lookup (priv->accounts, account) != NULL);
563 }
564
565 static McpAccountStorageSetResult
566 mcp_account_manager_goa_set_enabled (McpAccountStorage *self,
567     const gchar *account,
568     gboolean enabled)
569 {
570   McpAccountManagerGoaPrivate *priv = GET_PRIVATE (self);
571   GoaObject *object;
572   GoaAccount *acc;
573
574   object = g_hash_table_lookup (priv->accounts, account);
575
576   if (object == NULL)
577     return MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED;
578
579   acc = goa_object_peek_account (object);
580
581   if (acc == NULL)
582     return MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED;
583
584   if (goa_account_get_chat_disabled (acc) == !enabled)
585     return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED;
586
587   goa_account_set_chat_disabled (acc, !enabled);
588   return MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED;
589 }
590
591 #ifdef MCP_API_VERSION_5_18
592
593 static McpAccountStorageSetResult
594 mcp_account_manager_goa_set_attribute (McpAccountStorage *storage,
595     McpAccountManager *am,
596     const gchar *account_name,
597     const gchar *attribute,
598     GVariant *value,
599     McpAttributeFlags flags)
600 {
601   McpAccountManagerGoaPrivate *priv = GET_PRIVATE (storage);
602   McpAccountStorageSetResult ret = MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED;
603
604   g_return_val_if_fail (account_is_in_goa (storage, account_name),
605       MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED);
606
607   if (!tp_strdiff (attribute, "Enabled"))
608     {
609       g_return_val_if_fail (
610           g_variant_classify (value) == G_VARIANT_CLASS_BOOLEAN,
611           MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED);
612
613       return mcp_account_manager_goa_set_enabled (storage, account_name,
614             g_variant_get_boolean (value));
615     }
616   /* FIXME: filter out manager, protocol, Icon, Service? */
617   else if (value != NULL)
618     {
619       gchar *esc = mcp_account_manager_escape_variant_for_keyfile (am,
620           value);
621       gchar *old;
622
623       if (esc == NULL)
624         return MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED;
625
626       old = g_key_file_get_value (priv->store, account_name, attribute, NULL);
627
628       if (tp_strdiff (old, esc))
629         {
630           g_key_file_set_value (priv->store, account_name, attribute, esc);
631           ret = MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED;
632         }
633       else
634         {
635           ret = MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED;
636         }
637
638       g_free (esc);
639       g_free (old);
640     }
641   else
642     {
643       if (g_key_file_has_key (priv->store, account_name, attribute, NULL))
644         {
645           g_key_file_remove_key (priv->store, account_name, attribute, NULL);
646           ret = MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED;
647         }
648       else
649         {
650           ret = MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED;
651         }
652     }
653
654   return ret;
655 }
656
657 static McpAccountStorageSetResult
658 mcp_account_manager_goa_set_parameter (McpAccountStorage *storage,
659     McpAccountManager *am,
660     const gchar *account_name,
661     const gchar *parameter,
662     GVariant *value,
663     McpParameterFlags flags)
664 {
665   McpAccountManagerGoaPrivate *priv = GET_PRIVATE (storage);
666   McpAccountStorageSetResult ret = MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED;
667   gchar *key = NULL;
668   gchar *esc = NULL;
669   gchar *old = NULL;
670
671   g_return_val_if_fail (account_is_in_goa (storage, account_name),
672       MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED);
673
674   key = g_strdup_printf ("param-%s", parameter);
675
676   /* FIXME: filter out reserved keys for this account? */
677   if (value != NULL)
678     {
679       esc = mcp_account_manager_escape_variant_for_keyfile (am,
680           value);
681
682       if (esc == NULL)
683         goto out;
684
685       old = g_key_file_get_value (priv->store, account_name, key, NULL);
686
687       if (tp_strdiff (esc, old))
688         {
689           g_key_file_set_value (priv->store, account_name, key, esc);
690           ret = MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED;
691         }
692       else
693         {
694           ret = MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED;
695         }
696     }
697   else
698     {
699       if (g_key_file_has_key (priv->store, account_name, key, NULL))
700         {
701           g_key_file_remove_key (priv->store, account_name, key, NULL);
702           ret = MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED;
703         }
704       else
705         {
706           ret = MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED;
707         }
708
709     }
710
711 out:
712   g_free (key);
713   g_free (esc);
714   g_free (old);
715   return ret;
716 }
717
718 #else /* MC 5.16 or older */
719
720 static gboolean
721 mcp_account_manager_goa_set (const McpAccountStorage *self,
722     const McpAccountManager *am,
723     const gchar *account,
724     const gchar *key,
725     const gchar *val)
726 {
727   McpAccountManagerGoaPrivate *priv = GET_PRIVATE (self);
728
729   if (!account_is_in_goa (self, account))
730     return FALSE;
731
732   DEBUG ("%s: (%s, %s, %s)", G_STRFUNC, account, key, val);
733
734   if (!tp_strdiff (key, "Enabled"))
735     {
736       if (mcp_account_manager_goa_set_enabled ((McpAccountStorage *) self,
737           account,
738           !tp_strdiff (val, "true")) != MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED)
739         return TRUE;
740       else
741         return FALSE;
742     }
743
744   if (val != NULL)
745     g_key_file_set_value (priv->store, account, key, val);
746   else
747     g_key_file_remove_key (priv->store, account, key, NULL);
748
749   /* Pretend we save everything so MC won't save this in accounts.cfg */
750   return TRUE;
751 }
752
753 #endif /* MC 5.16 or older */
754
755
756 static gboolean
757 mcp_account_manager_goa_delete (WAS_CONST McpAccountStorage *self,
758     WAS_CONST McpAccountManager *am,
759     const gchar *account,
760     const gchar *key)
761 {
762   McpAccountManagerGoaPrivate *priv = GET_PRIVATE (self);
763
764   if (!account_is_in_goa (self, account))
765     return FALSE;
766
767   DEBUG ("%s: (%s, %s)", G_STRFUNC, account, key);
768
769   if (key == NULL)
770     {
771       g_key_file_remove_group (priv->store, account, NULL);
772     }
773   else
774     {
775       g_key_file_remove_key (priv->store, account, key, NULL);
776     }
777
778   /* Pretend we deleted everything */
779   return TRUE;
780 }
781
782
783 #ifdef MCP_API_VERSION_5_18
784 static void
785 mcp_account_manager_goa_delete_async (McpAccountStorage *self,
786     McpAccountManager *am,
787     const gchar *account_name,
788     GCancellable *cancellable,
789     GAsyncReadyCallback callback,
790     gpointer user_data)
791 {
792   GTask *task = g_task_new (self, cancellable, callback, user_data);
793
794   if (mcp_account_manager_goa_delete (self, am, account_name, NULL))
795     {
796       g_task_return_boolean (task, TRUE);
797     }
798   else
799     {
800       g_task_return_new_error (task, TP_ERROR, TP_ERROR_DOES_NOT_EXIST,
801           "Account does not exist in GOA");
802     }
803
804   g_object_unref (task);
805 }
806
807 static gboolean
808 mcp_account_manager_goa_delete_finish (McpAccountStorage *self,
809     GAsyncResult *res,
810     GError **error)
811 {
812   return g_task_propagate_boolean (G_TASK (res), error);
813 }
814 #endif /* MC >= 5.18 API */
815
816 static gboolean
817 commit (McpAccountManagerGoa *self)
818 {
819   McpAccountManagerGoaPrivate *priv = self->priv;
820   gchar *data;
821   gsize len;
822   GError *error = NULL;
823
824   DEBUG ("Save config to %s", priv->filename);
825
826   data = g_key_file_to_data (priv->store, &len, &error);
827   if (data == NULL)
828     {
829       DEBUG ("Failed to get data from store: %s", error->message);
830
831       g_error_free (error);
832       return FALSE;
833     }
834
835   if (!g_file_set_contents (priv->filename, data, len, &error))
836     {
837       DEBUG ("Failed to write file: %s", error->message);
838
839       g_free (data);
840       g_error_free (error);
841       return FALSE;
842     }
843
844   g_free (data);
845
846   return TRUE;
847 }
848
849 #ifdef MCP_API_VERSION_5_18
850 static gboolean
851 mcp_account_manager_goa_commit (McpAccountStorage *self,
852   McpAccountManager *am,
853   const gchar *account)
854 {
855   return commit (MCP_ACCOUNT_MANAGER_GOA (self));
856 }
857 #else
858 static gboolean
859 mcp_account_manager_goa_commit (const McpAccountStorage *self,
860   const McpAccountManager *am)
861 {
862   return commit (MCP_ACCOUNT_MANAGER_GOA (self));
863 }
864 #endif
865
866
867 #ifndef MCP_API_VERSION_5_18
868 /* removed in 5.18, MC should now be ready to receive signals at any time */
869 static void
870 mcp_account_manager_goa_ready (WAS_CONST McpAccountStorage *self,
871     WAS_CONST McpAccountManager *am)
872 {
873   McpAccountManagerGoaPrivate *priv = GET_PRIVATE (self);
874
875   priv->ready = TRUE;
876 }
877 #endif
878
879
880 static RESTRICTIONS
881 mcp_account_manager_goa_get_restrictions (WAS_CONST McpAccountStorage *self,
882     const gchar *account)
883 {
884   return TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PARAMETERS |
885          TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_SERVICE;
886 }
887
888
889 static void
890 mcp_account_manager_goa_get_identifier (WAS_CONST McpAccountStorage *self,
891     const gchar *acc,
892     GValue *identifier)
893 {
894   McpAccountManagerGoaPrivate *priv = GET_PRIVATE (self);
895   GoaObject *object;
896   GoaAccount *account;
897
898   object = g_hash_table_lookup (priv->accounts, acc);
899   g_return_if_fail (object != NULL);
900
901   account = goa_object_peek_account (object);
902   g_return_if_fail (account != NULL);
903
904   g_value_init (identifier, G_TYPE_STRING);
905   g_value_set_string (identifier, goa_account_get_id (account));
906 }
907
908
909 static void
910 account_storage_iface_init (McpAccountStorageIface *iface)
911 {
912   iface->name = PLUGIN_NAME;
913   iface->desc = PLUGIN_DESCRIPTION;
914   iface->priority = PLUGIN_PRIORITY;
915   iface->provider = PLUGIN_PROVIDER;
916
917 #define IMPLEMENT(x) iface->x = mcp_account_manager_goa_##x
918
919   IMPLEMENT (list);
920   IMPLEMENT (commit);
921   IMPLEMENT (get_restrictions);
922   IMPLEMENT (get_identifier);
923
924 #ifdef MCP_API_VERSION_5_18
925   IMPLEMENT (delete_async);
926   IMPLEMENT (delete_finish);
927   IMPLEMENT (get_attribute);
928   IMPLEMENT (get_parameter);
929   IMPLEMENT (set_attribute);
930   IMPLEMENT (set_parameter);
931 #else
932   IMPLEMENT (get);
933   IMPLEMENT (set);
934   IMPLEMENT (delete);
935   IMPLEMENT (ready);
936 #endif
937
938 #undef IMPLEMENT
939 }