]> git.0d.be Git - empathy.git/blob - ubuntu-online-accounts/mc-plugin/mcp-account-manager-uoa.c
304e4f38afe67515f842c03bea793cfaab552909
[empathy.git] / ubuntu-online-accounts / mc-plugin / mcp-account-manager-uoa.c
1 /*
2  * Copyright © 2012 Collabora Ltd.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18
19 #include "config.h"
20 #include "mcp-account-manager-uoa.h"
21
22 #include <telepathy-glib/telepathy-glib.h>
23
24 #include <libaccounts-glib/ag-account.h>
25 #include <libaccounts-glib/ag-account-service.h>
26 #include <libaccounts-glib/ag-manager.h>
27 #include <libaccounts-glib/ag-service.h>
28
29 #include <string.h>
30 #include <ctype.h>
31
32 #include "empathy-webcredentials-monitor.h"
33
34 #define PLUGIN_NAME "uoa"
35 #define PLUGIN_PRIORITY (MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_KEYRING + 10)
36 #define PLUGIN_DESCRIPTION "Provide Telepathy Accounts from UOA via libaccounts-glib"
37 #define PLUGIN_PROVIDER EMPATHY_UOA_PROVIDER
38
39 #define DEBUG g_debug
40
41 #define SERVICE_TYPE "IM"
42 #define KEY_PREFIX "telepathy/"
43 #define KEY_ACCOUNT_NAME "mc-account-name"
44 #define KEY_READONLY_PARAMS "mc-readonly-params"
45
46 static void account_storage_iface_init (McpAccountStorageIface *iface);
47
48 G_DEFINE_TYPE_WITH_CODE (McpAccountManagerUoa, mcp_account_manager_uoa,
49     G_TYPE_OBJECT,
50     G_IMPLEMENT_INTERFACE (MCP_TYPE_ACCOUNT_STORAGE,
51         account_storage_iface_init));
52
53 struct _McpAccountManagerUoaPrivate
54 {
55   McpAccountManager *am;
56
57   AgManager *manager;
58   EmpathyWebcredentialsMonitor *monitor;
59
60   /* alloc'ed string -> ref'ed AgAccountService
61    * The key is the account_name, an MC unique identifier.
62    * Note: There could be multiple services in this table having the same
63    * AgAccount, even if unlikely. */
64   GHashTable *accounts;
65
66   /* Queue of owned DelayedSignalData */
67   GQueue *pending_signals;
68
69   gboolean loaded;
70   gboolean ready;
71 };
72
73 typedef enum {
74   DELAYED_CREATE,
75   DELAYED_DELETE,
76 } DelayedSignal;
77
78 typedef struct {
79   DelayedSignal signal;
80   AgAccountId account_id;
81 } DelayedSignalData;
82
83
84 static gchar *
85 _service_dup_tp_value (AgAccountService *service,
86     const gchar *key)
87 {
88   gchar *real_key = g_strdup_printf (KEY_PREFIX "%s", key);
89   GValue value = { 0, };
90   gchar *ret;
91
92   g_value_init (&value, G_TYPE_STRING);
93   ag_account_service_get_value (service, real_key, &value);
94   ret = g_value_dup_string (&value);
95   g_value_unset (&value);
96
97   return ret;
98 }
99
100 static void
101 _service_set_tp_value (AgAccountService *service,
102     const gchar *key,
103     const gchar *value)
104 {
105   gchar *real_key = g_strdup_printf (KEY_PREFIX "%s", key);
106
107   if (value != NULL)
108     {
109       GValue gvalue = { 0, };
110
111       g_value_init (&gvalue, G_TYPE_STRING);
112       g_value_set_string (&gvalue, value);
113       ag_account_service_set_value (service, real_key, &gvalue);
114       g_value_unset (&gvalue);
115       g_free (real_key);
116     }
117   else
118     {
119       ag_account_service_set_value (service, real_key, NULL);
120     }
121 }
122
123 /* Returns NULL if the account never has been imported into MC before */
124 static gchar *
125 _service_dup_tp_account_name (AgAccountService *service)
126 {
127   return _service_dup_tp_value (service, KEY_ACCOUNT_NAME);
128 }
129
130 static void
131 _service_set_tp_account_name (AgAccountService *service,
132     const gchar *account_name)
133 {
134   _service_set_tp_value (service, KEY_ACCOUNT_NAME, account_name);
135 }
136
137 static void
138 _service_enabled_cb (AgAccountService *service,
139     gboolean enabled,
140     McpAccountManagerUoa *self)
141 {
142   gchar *account_name = _service_dup_tp_account_name (service);
143
144   if (!self->priv->ready || account_name == NULL)
145     return;
146
147   DEBUG ("UOA account %s toggled: %s", account_name,
148       enabled ? "enabled" : "disabled");
149
150   g_signal_emit_by_name (self, "toggled", account_name, enabled);
151
152   g_free (account_name);
153 }
154
155 static void
156 _service_changed_cb (AgAccountService *service,
157     McpAccountManagerUoa *self)
158 {
159   gchar *account_name = _service_dup_tp_account_name (service);
160
161   if (!self->priv->ready || account_name == NULL)
162     return;
163
164   DEBUG ("UOA account %s changed", account_name);
165
166   /* FIXME: Could use ag_account_service_get_changed_fields()
167    * and emit "altered-one" */
168   g_signal_emit_by_name (self, "altered", account_name);
169
170   g_free (account_name);
171 }
172
173 static void
174 _account_stored_cb (AgAccount *account,
175     const GError *error,
176     gpointer user_data)
177 {
178   if (error != NULL)
179     {
180       DEBUG ("Error storing UOA account '%s': %s",
181           ag_account_get_display_name (account),
182           error->message);
183     }
184 }
185
186 static gboolean
187 _add_service (McpAccountManagerUoa *self,
188     AgAccountService *service,
189     const gchar *account_name)
190 {
191   DEBUG ("UOA account %s added", account_name);
192
193   if (g_hash_table_contains (self->priv->accounts, account_name))
194     {
195       DEBUG ("Already exists, ignoring");
196       return FALSE;
197     }
198
199   g_hash_table_insert (self->priv->accounts,
200       g_strdup (account_name),
201       g_object_ref (service));
202
203   g_signal_connect (service, "enabled",
204       G_CALLBACK (_service_enabled_cb), self);
205   g_signal_connect (service, "changed",
206       G_CALLBACK (_service_changed_cb), self);
207
208   return TRUE;
209 }
210
211 static void
212 _account_created_cb (AgManager *manager,
213     AgAccountId id,
214     McpAccountManagerUoa *self)
215 {
216   AgAccount *account;
217   GList *l;
218
219   if (!self->priv->ready)
220     {
221       DelayedSignalData *data = g_slice_new0 (DelayedSignalData);
222
223       data->signal = DELAYED_CREATE;
224       data->account_id = id;
225
226       g_queue_push_tail (self->priv->pending_signals, data);
227       return;
228     }
229
230   account = ag_manager_get_account (self->priv->manager, id);
231
232   l = ag_account_list_services_by_type (account, SERVICE_TYPE);
233   while (l != NULL)
234     {
235       AgAccountService *service = ag_account_service_new (account, l->data);
236       gchar *account_name = _service_dup_tp_account_name (service);
237
238       /* If this is the first time we see this service, we have to generate an
239        * account_name for it. */
240       if (account_name == NULL)
241         {
242           gchar *cm_name = NULL;
243           gchar *protocol_name = NULL;
244           gchar *account_param = NULL;
245
246           cm_name = _service_dup_tp_value (service, "manager");
247           protocol_name = _service_dup_tp_value (service, "protocol");
248           account_param = _service_dup_tp_value (service, "param-account");
249
250           if (!tp_str_empty (cm_name) &&
251               !tp_str_empty (protocol_name) &&
252               !tp_str_empty (account_param))
253             {
254               GHashTable *params;
255
256               params = tp_asv_new (
257                   "account", G_TYPE_STRING, account_param,
258                   NULL);
259
260               account_name = mcp_account_manager_get_unique_name (self->priv->am,
261                   cm_name, protocol_name, params);
262               _service_set_tp_account_name (service, account_name);
263
264               ag_account_store (account, _account_stored_cb, self);
265
266               g_hash_table_unref (params);
267             }
268
269           g_free (cm_name);
270           g_free (protocol_name);
271           g_free (account_param);
272         }
273
274       if (account_name != NULL)
275         {
276           if (_add_service (self, service, account_name))
277             g_signal_emit_by_name (self, "created", account_name);
278         }
279
280       g_free (account_name);
281       g_object_unref (service);
282       ag_service_unref (l->data);
283       l = g_list_delete_link (l, l);
284     }
285
286   g_object_unref (account);
287 }
288
289 static void
290 _account_deleted_cb (AgManager *manager,
291     AgAccountId id,
292     McpAccountManagerUoa *self)
293 {
294   GHashTableIter iter;
295   gpointer value;
296
297   if (!self->priv->ready)
298     {
299       DelayedSignalData *data = g_slice_new0 (DelayedSignalData);
300
301       data->signal = DELAYED_DELETE;
302       data->account_id = id;
303
304       g_queue_push_tail (self->priv->pending_signals, data);
305       return;
306     }
307
308   g_hash_table_iter_init (&iter, self->priv->accounts);
309   while (g_hash_table_iter_next (&iter, NULL, &value))
310     {
311       AgAccountService *service = value;
312       AgAccount *account = ag_account_service_get_account (service);
313       gchar *account_name;
314
315       if (account->id != id)
316         continue;
317
318       account_name = _service_dup_tp_account_name (service);
319       if (account_name == NULL)
320         continue;
321
322       DEBUG ("UOA account %s deleted", account_name);
323
324       g_hash_table_iter_remove (&iter);
325       g_signal_emit_by_name (self, "deleted", account_name);
326
327       g_free (account_name);
328     }
329 }
330
331 static void
332 mcp_account_manager_uoa_dispose (GObject *object)
333 {
334   McpAccountManagerUoa *self = (McpAccountManagerUoa *) object;
335
336   tp_clear_object (&self->priv->am);
337   tp_clear_object (&self->priv->manager);
338   tp_clear_pointer (&self->priv->accounts, g_hash_table_unref);
339   tp_clear_object (&self->priv->monitor);
340
341   G_OBJECT_CLASS (mcp_account_manager_uoa_parent_class)->dispose (object);
342 }
343
344 static void
345 mcp_account_manager_uoa_init (McpAccountManagerUoa *self)
346 {
347   DEBUG ("UOA MC plugin initialised");
348
349   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
350       MCP_TYPE_ACCOUNT_MANAGER_UOA, McpAccountManagerUoaPrivate);
351
352   self->priv->accounts = g_hash_table_new_full (g_str_hash, g_str_equal,
353       g_free, g_object_unref);
354   self->priv->pending_signals = g_queue_new ();
355
356   self->priv->manager = ag_manager_new_for_service_type (SERVICE_TYPE);
357   g_return_if_fail (self->priv->manager != NULL);
358
359   g_signal_connect (self->priv->manager, "account-created",
360       G_CALLBACK (_account_created_cb), self);
361   g_signal_connect (self->priv->manager, "account-deleted",
362       G_CALLBACK (_account_deleted_cb), self);
363
364   self->priv->monitor = empathy_webcredentials_monitor_new (
365       self->priv->manager);
366 }
367
368 static void
369 mcp_account_manager_uoa_class_init (McpAccountManagerUoaClass *klass)
370 {
371   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
372
373   gobject_class->dispose = mcp_account_manager_uoa_dispose;
374
375   g_type_class_add_private (gobject_class,
376       sizeof (McpAccountManagerUoaPrivate));
377 }
378
379 static void
380 _ensure_loaded (McpAccountManagerUoa *self)
381 {
382   GList *services;
383
384   if (self->priv->loaded)
385     return;
386
387   self->priv->loaded = TRUE;
388
389   g_assert (!self->priv->ready);
390
391   services = ag_manager_get_account_services (self->priv->manager);
392   while (services != NULL)
393     {
394       AgAccountService *service = services->data;
395       AgAccount *account = ag_account_service_get_account (service);
396       gchar *account_name = _service_dup_tp_account_name (service);
397
398       if (account_name != NULL)
399         {
400           /* This service was already known, we can add it now */
401           _add_service (self, service, account_name);
402           g_free (account_name);
403         }
404       else
405         {
406           DelayedSignalData *data = g_slice_new0 (DelayedSignalData);
407
408           /* This service was created while MC was not running, delay its
409            * creation until MC is ready */
410           data->signal = DELAYED_CREATE;
411           data->account_id = account->id;
412
413           g_queue_push_tail (self->priv->pending_signals, data);
414         }
415
416       g_object_unref (services->data);
417       services = g_list_delete_link (services, services);
418     }
419 }
420
421 static GList *
422 account_manager_uoa_list (const McpAccountStorage *storage,
423     const McpAccountManager *am)
424 {
425   McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
426   GList *accounts = NULL;
427   GHashTableIter iter;
428   gpointer key;
429
430   DEBUG (G_STRFUNC);
431
432   g_return_val_if_fail (self->priv->manager != NULL, NULL);
433
434   _ensure_loaded (self);
435
436   g_hash_table_iter_init (&iter, self->priv->accounts);
437   while (g_hash_table_iter_next (&iter, &key, NULL))
438     accounts = g_list_prepend (accounts, g_strdup (key));
439
440   return accounts;
441 }
442
443 static const gchar *
444 provider_to_tp_service_name (const gchar *provider_name)
445 {
446   /* Well known services are defined in Telepathy spec:
447    * http://telepathy.freedesktop.org/spec/Account.html#Property:Service */
448   if (!tp_strdiff (provider_name, "google"))
449     return "google-talk";
450
451   return provider_name;
452 }
453
454 static gboolean
455 account_manager_uoa_get (const McpAccountStorage *storage,
456     const McpAccountManager *am,
457     const gchar *account_name,
458     const gchar *key)
459 {
460   McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
461   AgAccountService *service;
462   AgAccount *account;
463   AgService *s;
464   gboolean handled = FALSE;
465
466   g_return_val_if_fail (self->priv->manager != NULL, FALSE);
467
468   service = g_hash_table_lookup (self->priv->accounts, account_name);
469   if (service == NULL)
470     return FALSE;
471
472   DEBUG ("%s: %s, %s", G_STRFUNC, account_name, key);
473
474   account = ag_account_service_get_account (service);
475   s = ag_account_service_get_service (service);
476
477   /* NULL key means we want all settings */
478   if (key == NULL)
479     {
480       AgAccountSettingIter iter;
481       const gchar *k;
482       const GValue *v;
483
484       ag_account_service_settings_iter_init (service, &iter, KEY_PREFIX);
485       while (ag_account_service_settings_iter_next (&iter, &k, &v))
486         {
487           if (!G_VALUE_HOLDS_STRING (v))
488             continue;
489
490           mcp_account_manager_set_value (am, account_name,
491               k, g_value_get_string (v));
492         }
493     }
494
495   /* Some special keys that are not stored in setting */
496   if (key == NULL || !tp_strdiff (key, "Enabled"))
497     {
498       mcp_account_manager_set_value (am, account_name, "Enabled",
499           ag_account_service_get_enabled (service) ? "true" : "false");
500       handled = TRUE;
501     }
502
503   if (key == NULL || !tp_strdiff (key, "DisplayName"))
504     {
505       mcp_account_manager_set_value (am, account_name, "DisplayName",
506           ag_account_get_display_name (account));
507       handled = TRUE;
508     }
509
510   if (key == NULL || !tp_strdiff (key, "Service"))
511     {
512       mcp_account_manager_set_value (am, account_name, "Service",
513           provider_to_tp_service_name (ag_account_get_provider_name (account)));
514       handled = TRUE;
515     }
516
517   if (key == NULL || !tp_strdiff (key, "Icon"))
518     {
519       mcp_account_manager_set_value (am, account_name, "Icon",
520           ag_service_get_icon_name (s));
521       handled = TRUE;
522     }
523
524   /* If it was none of the above, then just lookup in service' settings */
525   if (!handled)
526     {
527       gchar *value = _service_dup_tp_value (service, key);
528
529       mcp_account_manager_set_value (am, account_name, key, value);
530       g_free (value);
531     }
532
533   return TRUE;
534 }
535
536 static gboolean
537 account_manager_uoa_set (const McpAccountStorage *storage,
538     const McpAccountManager *am,
539     const gchar *account_name,
540     const gchar *key,
541     const gchar *val)
542 {
543   McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
544   AgAccountService *service;
545   AgAccount *account;
546
547   g_return_val_if_fail (self->priv->manager != NULL, FALSE);
548
549   service = g_hash_table_lookup (self->priv->accounts, account_name);
550   if (service == NULL)
551     return FALSE;
552
553   account = ag_account_service_get_account (service);
554
555   DEBUG ("%s: %s, %s, %s", G_STRFUNC, account_name, key, val);
556
557   if (!tp_strdiff (key, "Enabled"))
558     {
559       /* Enabled is a global setting on the account, not per-services,
560        * unfortunately */
561       ag_account_select_service (account, NULL);
562       ag_account_set_enabled (account, !tp_strdiff (val, "true"));
563     }
564   else if (!tp_strdiff (key, "DisplayName"))
565     {
566       ag_account_set_display_name (account, val);
567     }
568   else
569     {
570       _service_set_tp_value (service, key, val);
571     }
572
573   return TRUE;
574 }
575
576 static gchar *
577 account_manager_uoa_create (const McpAccountStorage *storage,
578     const McpAccountManager *am,
579     const gchar *cm_name,
580     const gchar *protocol_name,
581     GHashTable *params,
582     GError **error)
583 {
584   McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
585   gchar *account_name;
586   AgAccount *account;
587   AgAccountService *service;
588   GList *l;
589
590   g_return_val_if_fail (self->priv->manager != NULL, NULL);
591
592   if (!self->priv->ready)
593     {
594       g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
595           "Cannot create account before being ready");
596       return NULL;
597     }
598
599   DEBUG (G_STRFUNC);
600
601   /* Create a new AgAccountService and keep it internally. This won't save it
602    * into persistent storage until account_manager_uoa_commit() is called.
603    * We assume there is only one IM service */
604   account = ag_manager_create_account (self->priv->manager, protocol_name);
605   l = ag_account_list_services_by_type (account, SERVICE_TYPE);
606   if (l == NULL)
607     {
608       g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
609           "Cannot create a %s service for %s provider",
610           SERVICE_TYPE, protocol_name);
611       g_object_unref (account);
612       return NULL;
613     }
614   service = ag_account_service_new (account, l->data);
615   ag_service_list_free (l);
616   g_object_unref (account);
617
618   account_name = mcp_account_manager_get_unique_name (self->priv->am,
619       cm_name, protocol_name, params);
620   _service_set_tp_account_name (service, account_name);
621   g_assert (_add_service (self, service, account_name));
622
623   /* MC will set all params on the account and commit */
624
625   return account_name;
626 }
627
628 static gboolean
629 account_manager_uoa_delete (const McpAccountStorage *storage,
630     const McpAccountManager *am,
631     const gchar *account_name,
632     const gchar *key)
633 {
634   McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
635   AgAccountService *service;
636   AgAccount *account;
637
638   g_return_val_if_fail (self->priv->manager != NULL, FALSE);
639
640   service = g_hash_table_lookup (self->priv->accounts, account_name);
641   if (service == NULL)
642     return FALSE;
643
644   account = ag_account_service_get_account (service);
645
646   DEBUG ("%s: %s, %s", G_STRFUNC, account_name, key);
647
648   if (key == NULL)
649     {
650       ag_account_delete (account);
651       g_hash_table_remove (self->priv->accounts, account_name);
652     }
653   else
654     {
655       _service_set_tp_value (service, key, NULL);
656     }
657
658   return TRUE;
659 }
660
661 static gboolean
662 account_manager_uoa_commit (const McpAccountStorage *storage,
663     const McpAccountManager *am)
664 {
665   McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
666   GHashTableIter iter;
667   gpointer value;
668
669   DEBUG (G_STRFUNC);
670
671   g_return_val_if_fail (self->priv->manager != NULL, FALSE);
672
673   g_hash_table_iter_init (&iter, self->priv->accounts);
674   while (g_hash_table_iter_next (&iter, NULL, &value))
675     {
676       AgAccountService *service = value;
677       AgAccount *account = ag_account_service_get_account (service);
678
679       ag_account_store (account, _account_stored_cb, self);
680     }
681
682   return TRUE;
683 }
684
685 static void
686 failure_removed_cb (EmpathyWebcredentialsMonitor *monitor,
687     AgAccount *account,
688     McpAccountManagerUoa *self)
689 {
690   GList *l;
691
692   DEBUG ("Account '%u' is not failing any more", account->id);
693
694   l = ag_account_list_services_by_type (account, SERVICE_TYPE);
695   while (l != NULL)
696     {
697       AgAccountService *service = ag_account_service_new (account, l->data);
698       gchar *account_name = _service_dup_tp_account_name (service);
699
700       if (account_name != NULL)
701         {
702           DEBUG ("Reconnect account %s", account_name);
703
704           mcp_account_storage_emit_reconnect (MCP_ACCOUNT_STORAGE (self),
705               account_name);
706         }
707
708       g_free (account_name);
709       g_object_unref (service);
710       ag_service_unref (l->data);
711       l = g_list_delete_link (l, l);
712     }
713 }
714
715 static void
716 account_manager_uoa_ready (const McpAccountStorage *storage,
717     const McpAccountManager *am)
718 {
719   McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
720   DelayedSignalData *data;
721
722   g_return_if_fail (self->priv->manager != NULL);
723
724   if (self->priv->ready)
725     return;
726
727   DEBUG (G_STRFUNC);
728
729   self->priv->ready = TRUE;
730   self->priv->am = g_object_ref (G_OBJECT (am));
731
732   while ((data = g_queue_pop_head (self->priv->pending_signals)) != NULL)
733     {
734       switch (data->signal)
735         {
736           case DELAYED_CREATE:
737             _account_created_cb (self->priv->manager, data->account_id, self);
738             break;
739           case DELAYED_DELETE:
740             _account_deleted_cb (self->priv->manager, data->account_id, self);
741             break;
742           default:
743             g_assert_not_reached ();
744         }
745
746       g_slice_free (DelayedSignalData, data);
747     }
748
749   g_queue_free (self->priv->pending_signals);
750   self->priv->pending_signals = NULL;
751
752   g_signal_connect (self->priv->monitor, "failure-removed",
753       G_CALLBACK (failure_removed_cb), self);
754 }
755
756 static void
757 account_manager_uoa_get_identifier (const McpAccountStorage *storage,
758     const gchar *account_name,
759     GValue *identifier)
760 {
761   McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
762   AgAccountService *service;
763   AgAccount *account;
764
765   g_return_if_fail (self->priv->manager != NULL);
766
767   service = g_hash_table_lookup (self->priv->accounts, account_name);
768   if (service == NULL)
769     return;
770
771   account = ag_account_service_get_account (service);
772
773   g_value_init (identifier, G_TYPE_UINT);
774   g_value_set_uint (identifier, account->id);
775 }
776
777 static guint
778 account_manager_uoa_get_restrictions (const McpAccountStorage *storage,
779     const gchar *account_name)
780 {
781   McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
782   AgAccountService *service;
783   guint restrictions = TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_SERVICE;
784   GValue value = G_VALUE_INIT;
785
786   g_return_val_if_fail (self->priv->manager != NULL, 0);
787
788   /* If we don't know this account, we cannot do anything */
789   service = g_hash_table_lookup (self->priv->accounts, account_name);
790   if (service == NULL)
791     return G_MAXUINT;
792
793   g_value_init (&value, G_TYPE_BOOLEAN);
794   ag_account_service_get_value (service,
795       KEY_PREFIX KEY_READONLY_PARAMS, &value);
796
797   if (g_value_get_boolean (&value))
798     restrictions |= TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PARAMETERS;
799
800   g_value_unset (&value);
801
802   /* FIXME: We can't set Icon either, but there is no flag for that */
803   return restrictions;
804 }
805
806 static void
807 account_storage_iface_init (McpAccountStorageIface *iface)
808 {
809   mcp_account_storage_iface_set_name (iface, PLUGIN_NAME);
810   mcp_account_storage_iface_set_desc (iface, PLUGIN_DESCRIPTION);
811   mcp_account_storage_iface_set_priority (iface, PLUGIN_PRIORITY);
812   mcp_account_storage_iface_set_provider (iface, PLUGIN_PROVIDER);
813
814 #define IMPLEMENT(x) mcp_account_storage_iface_implement_##x(iface, \
815     account_manager_uoa_##x)
816   IMPLEMENT (get);
817   IMPLEMENT (list);
818   IMPLEMENT (set);
819   IMPLEMENT (create);
820   IMPLEMENT (delete);
821   IMPLEMENT (commit);
822   IMPLEMENT (ready);
823   IMPLEMENT (get_identifier);
824   IMPLEMENT (get_restrictions);
825 #undef IMPLEMENT
826 }
827
828 McpAccountManagerUoa *
829 mcp_account_manager_uoa_new (void)
830 {
831   return g_object_new (MCP_TYPE_ACCOUNT_MANAGER_UOA, NULL);
832 }