2 * Copyright © 2012 Collabora Ltd.
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.
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.
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
20 #include "mcp-account-manager-uoa.h"
22 #include <telepathy-glib/telepathy-glib.h>
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>
32 #define PLUGIN_NAME "uoa"
33 #define PLUGIN_PRIORITY (MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_KEYRING + 10)
34 #define PLUGIN_DESCRIPTION "Provide Telepathy Accounts from UOA via libaccounts-glib"
35 #define PLUGIN_PROVIDER EMPATHY_UOA_PROVIDER
39 #define SERVICE_TYPE "IM"
40 #define KEY_PREFIX "telepathy/"
41 #define KEY_ACCOUNT_NAME "mc-account-name"
42 #define KEY_READONLY_PARAMS "mc-readonly-params"
44 static void account_storage_iface_init (McpAccountStorageIface *iface);
46 G_DEFINE_TYPE_WITH_CODE (McpAccountManagerUoa, mcp_account_manager_uoa,
48 G_IMPLEMENT_INTERFACE (MCP_TYPE_ACCOUNT_STORAGE,
49 account_storage_iface_init));
51 struct _McpAccountManagerUoaPrivate
53 McpAccountManager *am;
57 /* alloc'ed string -> ref'ed AgAccountService
58 * The key is the account_name, an MC unique identifier.
59 * Note: There could be multiple services in this table having the same
60 * AgAccount, even if unlikely. */
63 /* Queue of owned DelayedSignalData */
64 GQueue *pending_signals;
77 AgAccountId account_id;
82 _service_dup_tp_value (AgAccountService *service,
85 gchar *real_key = g_strdup_printf (KEY_PREFIX "%s", key);
86 GValue value = { 0, };
89 g_value_init (&value, G_TYPE_STRING);
90 ag_account_service_get_value (service, real_key, &value);
91 ret = g_value_dup_string (&value);
92 g_value_unset (&value);
98 _service_set_tp_value (AgAccountService *service,
102 gchar *real_key = g_strdup_printf (KEY_PREFIX "%s", key);
106 GValue gvalue = { 0, };
108 g_value_init (&gvalue, G_TYPE_STRING);
109 g_value_set_string (&gvalue, value);
110 ag_account_service_set_value (service, real_key, &gvalue);
111 g_value_unset (&gvalue);
116 ag_account_service_set_value (service, real_key, NULL);
120 /* Returns NULL if the account never has been imported into MC before */
122 _service_dup_tp_account_name (AgAccountService *service)
124 return _service_dup_tp_value (service, KEY_ACCOUNT_NAME);
128 _service_set_tp_account_name (AgAccountService *service,
129 const gchar *account_name)
131 _service_set_tp_value (service, KEY_ACCOUNT_NAME, account_name);
135 _service_enabled_cb (AgAccountService *service,
137 McpAccountManagerUoa *self)
139 gchar *account_name = _service_dup_tp_account_name (service);
141 if (!self->priv->ready || account_name == NULL)
144 DEBUG ("UOA account %s toggled: %s", account_name,
145 enabled ? "enabled" : "disabled");
147 g_signal_emit_by_name (self, "toggled", account_name, enabled);
149 g_free (account_name);
153 _service_changed_cb (AgAccountService *service,
154 McpAccountManagerUoa *self)
156 gchar *account_name = _service_dup_tp_account_name (service);
158 if (!self->priv->ready || account_name == NULL)
161 DEBUG ("UOA account %s changed", account_name);
163 /* FIXME: Could use ag_account_service_get_changed_fields()
164 * and emit "altered-one" */
165 g_signal_emit_by_name (self, "altered", account_name);
167 g_free (account_name);
171 _account_stored_cb (AgAccount *account,
177 DEBUG ("Error storing UOA account '%s': %s",
178 ag_account_get_display_name (account),
184 _add_service (McpAccountManagerUoa *self,
185 AgAccountService *service,
186 const gchar *account_name)
188 DEBUG ("UOA account %s added", account_name);
190 if (g_hash_table_contains (self->priv->accounts, account_name))
192 DEBUG ("Already exists, ignoring");
196 g_hash_table_insert (self->priv->accounts,
197 g_strdup (account_name),
198 g_object_ref (service));
200 g_signal_connect (service, "enabled",
201 G_CALLBACK (_service_enabled_cb), self);
202 g_signal_connect (service, "changed",
203 G_CALLBACK (_service_changed_cb), self);
209 _account_created_cb (AgManager *manager,
211 McpAccountManagerUoa *self)
216 if (!self->priv->ready)
218 DelayedSignalData *data = g_slice_new0 (DelayedSignalData);
220 data->signal = DELAYED_CREATE;
221 data->account_id = id;
223 g_queue_push_tail (self->priv->pending_signals, data);
227 account = ag_manager_get_account (self->priv->manager, id);
229 l = ag_account_list_services_by_type (account, SERVICE_TYPE);
232 AgAccountService *service = ag_account_service_new (account, l->data);
233 gchar *account_name = _service_dup_tp_account_name (service);
235 /* If this is the first time we see this service, we have to generate an
236 * account_name for it. */
237 if (account_name == NULL)
239 gchar *cm_name = NULL;
240 gchar *protocol_name = NULL;
241 gchar *account_param = NULL;
243 cm_name = _service_dup_tp_value (service, "manager");
244 protocol_name = _service_dup_tp_value (service, "protocol");
245 account_param = _service_dup_tp_value (service, "param-account");
247 if (!tp_str_empty (cm_name) &&
248 !tp_str_empty (protocol_name) &&
249 !tp_str_empty (account_param))
253 params = tp_asv_new (
254 "account", G_TYPE_STRING, account_param,
257 account_name = mcp_account_manager_get_unique_name (self->priv->am,
258 cm_name, protocol_name, params);
259 _service_set_tp_account_name (service, account_name);
261 ag_account_store (account, _account_stored_cb, self);
263 g_hash_table_unref (params);
267 g_free (protocol_name);
268 g_free (account_param);
271 if (account_name != NULL)
273 if (_add_service (self, service, account_name))
274 g_signal_emit_by_name (self, "created", account_name);
277 g_free (account_name);
278 g_object_unref (service);
279 ag_service_unref (l->data);
280 l = g_list_delete_link (l, l);
283 g_object_unref (account);
287 _account_deleted_cb (AgManager *manager,
289 McpAccountManagerUoa *self)
294 if (!self->priv->ready)
296 DelayedSignalData *data = g_slice_new0 (DelayedSignalData);
298 data->signal = DELAYED_DELETE;
299 data->account_id = id;
301 g_queue_push_tail (self->priv->pending_signals, data);
305 g_hash_table_iter_init (&iter, self->priv->accounts);
306 while (g_hash_table_iter_next (&iter, NULL, &value))
308 AgAccountService *service = value;
309 AgAccount *account = ag_account_service_get_account (service);
312 if (account->id != id)
315 account_name = _service_dup_tp_account_name (service);
316 if (account_name == NULL)
319 DEBUG ("UOA account %s deleted", account_name);
321 g_hash_table_iter_remove (&iter);
322 g_signal_emit_by_name (self, "deleted", account_name);
324 g_free (account_name);
329 mcp_account_manager_uoa_dispose (GObject *object)
331 McpAccountManagerUoa *self = (McpAccountManagerUoa *) object;
333 tp_clear_object (&self->priv->am);
334 tp_clear_object (&self->priv->manager);
335 tp_clear_pointer (&self->priv->accounts, g_hash_table_unref);
337 G_OBJECT_CLASS (mcp_account_manager_uoa_parent_class)->dispose (object);
341 mcp_account_manager_uoa_init (McpAccountManagerUoa *self)
343 DEBUG ("UOA MC plugin initialised");
345 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
346 MCP_TYPE_ACCOUNT_MANAGER_UOA, McpAccountManagerUoaPrivate);
348 self->priv->accounts = g_hash_table_new_full (g_str_hash, g_str_equal,
349 g_free, g_object_unref);
350 self->priv->pending_signals = g_queue_new ();
352 self->priv->manager = ag_manager_new_for_service_type (SERVICE_TYPE);
354 g_signal_connect (self->priv->manager, "account-created",
355 G_CALLBACK (_account_created_cb), self);
356 g_signal_connect (self->priv->manager, "account-deleted",
357 G_CALLBACK (_account_deleted_cb), self);
361 mcp_account_manager_uoa_class_init (McpAccountManagerUoaClass *klass)
363 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
365 gobject_class->dispose = mcp_account_manager_uoa_dispose;
367 g_type_class_add_private (gobject_class,
368 sizeof (McpAccountManagerUoaPrivate));
372 _ensure_loaded (McpAccountManagerUoa *self)
376 if (self->priv->loaded)
379 self->priv->loaded = TRUE;
381 g_assert (!self->priv->ready);
383 services = ag_manager_get_account_services (self->priv->manager);
384 while (services != NULL)
386 AgAccountService *service = services->data;
387 AgAccount *account = ag_account_service_get_account (service);
388 gchar *account_name = _service_dup_tp_account_name (service);
390 if (account_name != NULL)
392 /* This service was already known, we can add it now */
393 _add_service (self, service, account_name);
394 g_free (account_name);
398 DelayedSignalData *data = g_slice_new0 (DelayedSignalData);
400 /* This service was created while MC was not running, delay its
401 * creation until MC is ready */
402 data->signal = DELAYED_CREATE;
403 data->account_id = account->id;
405 g_queue_push_tail (self->priv->pending_signals, data);
408 g_object_unref (services->data);
409 services = g_list_delete_link (services, services);
414 account_manager_uoa_list (const McpAccountStorage *storage,
415 const McpAccountManager *am)
417 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
418 GList *accounts = NULL;
424 _ensure_loaded (self);
426 g_hash_table_iter_init (&iter, self->priv->accounts);
427 while (g_hash_table_iter_next (&iter, &key, NULL))
428 accounts = g_list_prepend (accounts, g_strdup (key));
434 provider_to_tp_service_name (const gchar *provider_name)
436 /* Well known services are defined in Telepathy spec:
437 * http://telepathy.freedesktop.org/spec/Account.html#Property:Service */
438 if (!tp_strdiff (provider_name, "google"))
439 return "google-talk";
441 return provider_name;
445 account_manager_uoa_get (const McpAccountStorage *storage,
446 const McpAccountManager *am,
447 const gchar *account_name,
450 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
451 AgAccountService *service;
454 gboolean handled = FALSE;
456 service = g_hash_table_lookup (self->priv->accounts, account_name);
460 DEBUG ("%s: %s, %s", G_STRFUNC, account_name, key);
462 account = ag_account_service_get_account (service);
463 s = ag_account_service_get_service (service);
465 /* NULL key means we want all settings */
468 AgAccountSettingIter iter;
472 ag_account_service_settings_iter_init (service, &iter, KEY_PREFIX);
473 while (ag_account_service_settings_iter_next (&iter, &k, &v))
475 if (!G_VALUE_HOLDS_STRING (v))
478 mcp_account_manager_set_value (am, account_name,
479 k, g_value_get_string (v));
483 /* Some special keys that are not stored in setting */
484 if (key == NULL || !tp_strdiff (key, "Enabled"))
486 mcp_account_manager_set_value (am, account_name, "Enabled",
487 ag_account_service_get_enabled (service) ? "true" : "false");
491 if (key == NULL || !tp_strdiff (key, "DisplayName"))
493 mcp_account_manager_set_value (am, account_name, "DisplayName",
494 ag_account_get_display_name (account));
498 if (key == NULL || !tp_strdiff (key, "Service"))
500 mcp_account_manager_set_value (am, account_name, "Service",
501 provider_to_tp_service_name (ag_account_get_provider_name (account)));
505 if (key == NULL || !tp_strdiff (key, "Icon"))
507 mcp_account_manager_set_value (am, account_name, "Icon",
508 ag_service_get_icon_name (s));
512 /* If it was none of the above, then just lookup in service' settings */
515 gchar *value = _service_dup_tp_value (service, key);
517 mcp_account_manager_set_value (am, account_name, key, value);
525 account_manager_uoa_set (const McpAccountStorage *storage,
526 const McpAccountManager *am,
527 const gchar *account_name,
531 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
532 AgAccountService *service;
535 service = g_hash_table_lookup (self->priv->accounts, account_name);
539 account = ag_account_service_get_account (service);
541 DEBUG ("%s: %s, %s, %s", G_STRFUNC, account_name, key, val);
543 if (!tp_strdiff (key, "Enabled"))
545 /* Enabled is a global setting on the account, not per-services,
547 ag_account_select_service (account, NULL);
548 ag_account_set_enabled (account, !tp_strdiff (val, "true"));
550 else if (!tp_strdiff (key, "DisplayName"))
552 ag_account_set_display_name (account, val);
556 _service_set_tp_value (service, key, val);
563 account_manager_uoa_create (const McpAccountStorage *storage,
564 const McpAccountManager *am,
565 const gchar *cm_name,
566 const gchar *protocol_name,
570 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
573 AgAccountService *service;
576 if (!self->priv->ready)
578 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
579 "Cannot create account before being ready");
585 /* Create a new AgAccountService and keep it internally. This won't save it
586 * into persistent storage until account_manager_uoa_commit() is called.
587 * We assume there is only one IM service */
588 account = ag_manager_create_account (self->priv->manager, protocol_name);
589 l = ag_account_list_services_by_type (account, SERVICE_TYPE);
592 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
593 "Cannot create a %s service for %s provider",
594 SERVICE_TYPE, protocol_name);
595 g_object_unref (account);
598 service = ag_account_service_new (account, l->data);
599 ag_service_list_free (l);
600 g_object_unref (account);
602 account_name = mcp_account_manager_get_unique_name (self->priv->am,
603 cm_name, protocol_name, params);
604 _service_set_tp_account_name (service, account_name);
605 g_assert (_add_service (self, service, account_name));
607 /* MC will set all params on the account and commit */
613 account_manager_uoa_delete (const McpAccountStorage *storage,
614 const McpAccountManager *am,
615 const gchar *account_name,
618 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
619 AgAccountService *service;
622 service = g_hash_table_lookup (self->priv->accounts, account_name);
626 account = ag_account_service_get_account (service);
628 DEBUG ("%s: %s, %s", G_STRFUNC, account_name, key);
632 ag_account_delete (account);
633 g_hash_table_remove (self->priv->accounts, account_name);
637 _service_set_tp_value (service, key, NULL);
644 account_manager_uoa_commit (const McpAccountStorage *storage,
645 const McpAccountManager *am)
647 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
653 g_hash_table_iter_init (&iter, self->priv->accounts);
654 while (g_hash_table_iter_next (&iter, NULL, &value))
656 AgAccountService *service = value;
657 AgAccount *account = ag_account_service_get_account (service);
659 ag_account_store (account, _account_stored_cb, self);
666 account_manager_uoa_ready (const McpAccountStorage *storage,
667 const McpAccountManager *am)
669 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
670 DelayedSignalData *data;
672 if (self->priv->ready)
677 self->priv->ready = TRUE;
678 self->priv->am = g_object_ref (G_OBJECT (am));
680 while ((data = g_queue_pop_head (self->priv->pending_signals)) != NULL)
682 switch (data->signal)
685 _account_created_cb (self->priv->manager, data->account_id, self);
688 _account_deleted_cb (self->priv->manager, data->account_id, self);
691 g_assert_not_reached ();
694 g_slice_free (DelayedSignalData, data);
697 g_queue_free (self->priv->pending_signals);
698 self->priv->pending_signals = NULL;
702 account_manager_uoa_get_identifier (const McpAccountStorage *storage,
703 const gchar *account_name,
706 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
707 AgAccountService *service;
710 service = g_hash_table_lookup (self->priv->accounts, account_name);
714 account = ag_account_service_get_account (service);
716 g_value_init (identifier, G_TYPE_UINT);
717 g_value_set_uint (identifier, account->id);
721 account_manager_uoa_get_restrictions (const McpAccountStorage *storage,
722 const gchar *account_name)
724 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
725 AgAccountService *service;
726 guint restrictions = TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_SERVICE;
727 GValue value = G_VALUE_INIT;
729 /* If we don't know this account, we cannot do anything */
730 service = g_hash_table_lookup (self->priv->accounts, account_name);
734 g_value_init (&value, G_TYPE_BOOLEAN);
735 ag_account_service_get_value (service,
736 KEY_PREFIX KEY_READONLY_PARAMS, &value);
738 if (g_value_get_boolean (&value))
739 restrictions |= TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PARAMETERS;
741 g_value_unset (&value);
743 /* FIXME: We can't set Icon either, but there is no flag for that */
748 account_storage_iface_init (McpAccountStorageIface *iface)
750 mcp_account_storage_iface_set_name (iface, PLUGIN_NAME);
751 mcp_account_storage_iface_set_desc (iface, PLUGIN_DESCRIPTION);
752 mcp_account_storage_iface_set_priority (iface, PLUGIN_PRIORITY);
753 mcp_account_storage_iface_set_provider (iface, PLUGIN_PROVIDER);
755 #define IMPLEMENT(x) mcp_account_storage_iface_implement_##x(iface, \
756 account_manager_uoa_##x)
764 IMPLEMENT (get_identifier);
765 IMPLEMENT (get_restrictions);
769 McpAccountManagerUoa *
770 mcp_account_manager_uoa_new (void)
772 return g_object_new (MCP_TYPE_ACCOUNT_MANAGER_UOA, NULL);