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>
23 #include <libaccounts-glib/ag-account.h>
24 #include <libaccounts-glib/ag-account-service.h>
25 #include <libaccounts-glib/ag-manager.h>
26 #include <libaccounts-glib/ag-service.h>
30 #include "empathy-webcredentials-monitor.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;
56 EmpathyWebcredentialsMonitor *monitor;
58 /* alloc'ed string -> ref'ed AgAccountService
59 * The key is the account_name, an MC unique identifier.
60 * Note: There could be multiple services in this table having the same
61 * AgAccount, even if unlikely. */
64 /* Queue of owned DelayedSignalData */
65 GQueue *pending_signals;
78 AgAccountId account_id;
83 _service_dup_tp_value (AgAccountService *service,
86 gchar *real_key = g_strdup_printf (KEY_PREFIX "%s", key);
89 value = ag_account_service_get_variant (service, real_key, NULL);
94 return g_variant_dup_string (value, NULL);
98 _service_set_tp_value (AgAccountService *service,
102 gchar *real_key = g_strdup_printf (KEY_PREFIX "%s", key);
106 GVariant *gvariant = g_variant_new_string (value);
107 ag_account_service_set_variant (service, real_key, gvariant);
111 ag_account_service_set_variant (service, real_key, NULL);
116 /* Returns NULL if the account never has been imported into MC before */
118 _service_dup_tp_account_name (AgAccountService *service)
120 return _service_dup_tp_value (service, KEY_ACCOUNT_NAME);
124 _service_set_tp_account_name (AgAccountService *service,
125 const gchar *account_name)
127 _service_set_tp_value (service, KEY_ACCOUNT_NAME, account_name);
131 _service_enabled_cb (AgAccountService *service,
133 McpAccountManagerUoa *self)
135 gchar *account_name = _service_dup_tp_account_name (service);
137 if (!self->priv->ready || account_name == NULL)
140 DEBUG ("UOA account %s toggled: %s", account_name,
141 enabled ? "enabled" : "disabled");
143 g_signal_emit_by_name (self, "toggled", account_name, enabled);
145 g_free (account_name);
149 _service_changed_cb (AgAccountService *service,
150 McpAccountManagerUoa *self)
152 gchar *account_name = _service_dup_tp_account_name (service);
154 if (!self->priv->ready || account_name == NULL)
157 DEBUG ("UOA account %s changed", account_name);
159 /* FIXME: Could use ag_account_service_get_changed_fields()
160 * and emit "altered-one" */
161 g_signal_emit_by_name (self, "altered", account_name);
163 g_free (account_name);
167 _account_stored_cb (GObject *source_object,
171 AgAccount *account = AG_ACCOUNT(source_object);
172 GError *error = NULL;
174 if (!ag_account_store_finish (account, res, &error))
176 g_assert (error != NULL);
177 DEBUG ("Error storing UOA account '%s': %s",
178 ag_account_get_display_name (account),
180 g_error_free (error);
185 _add_service (McpAccountManagerUoa *self,
186 AgAccountService *service,
187 const gchar *account_name)
189 DEBUG ("UOA account %s added", account_name);
191 if (g_hash_table_contains (self->priv->accounts, account_name))
193 DEBUG ("Already exists, ignoring");
197 g_hash_table_insert (self->priv->accounts,
198 g_strdup (account_name),
199 g_object_ref (service));
201 g_signal_connect (service, "enabled",
202 G_CALLBACK (_service_enabled_cb), self);
203 g_signal_connect (service, "changed",
204 G_CALLBACK (_service_changed_cb), self);
210 _account_created_cb (AgManager *manager,
212 McpAccountManagerUoa *self)
217 if (!self->priv->ready)
219 DelayedSignalData *data = g_slice_new0 (DelayedSignalData);
221 data->signal = DELAYED_CREATE;
222 data->account_id = id;
224 g_queue_push_tail (self->priv->pending_signals, data);
228 account = ag_manager_get_account (self->priv->manager, id);
230 l = ag_account_list_services_by_type (account, SERVICE_TYPE);
233 AgAccountService *service = ag_account_service_new (account, l->data);
234 gchar *account_name = _service_dup_tp_account_name (service);
236 /* If this is the first time we see this service, we have to generate an
237 * account_name for it. */
238 if (account_name == NULL)
240 gchar *cm_name = NULL;
241 gchar *protocol_name = NULL;
242 gchar *account_param = NULL;
244 cm_name = _service_dup_tp_value (service, "manager");
245 protocol_name = _service_dup_tp_value (service, "protocol");
246 account_param = _service_dup_tp_value (service, "param-account");
248 if (!tp_str_empty (cm_name) &&
249 !tp_str_empty (protocol_name) &&
250 !tp_str_empty (account_param))
254 params = tp_asv_new (
255 "account", G_TYPE_STRING, account_param,
258 account_name = mcp_account_manager_get_unique_name (self->priv->am,
259 cm_name, protocol_name, params);
260 _service_set_tp_account_name (service, account_name);
262 ag_account_store_async (account, NULL, _account_stored_cb, self);
264 g_hash_table_unref (params);
268 g_free (protocol_name);
269 g_free (account_param);
272 if (account_name != NULL)
274 if (_add_service (self, service, account_name))
275 g_signal_emit_by_name (self, "created", account_name);
278 g_free (account_name);
279 g_object_unref (service);
280 ag_service_unref (l->data);
281 l = g_list_delete_link (l, l);
284 g_object_unref (account);
288 _account_deleted_cb (AgManager *manager,
290 McpAccountManagerUoa *self)
295 if (!self->priv->ready)
297 DelayedSignalData *data = g_slice_new0 (DelayedSignalData);
299 data->signal = DELAYED_DELETE;
300 data->account_id = id;
302 g_queue_push_tail (self->priv->pending_signals, data);
306 g_hash_table_iter_init (&iter, self->priv->accounts);
307 while (g_hash_table_iter_next (&iter, NULL, &value))
309 AgAccountService *service = value;
310 AgAccount *account = ag_account_service_get_account (service);
313 if (account->id != id)
316 account_name = _service_dup_tp_account_name (service);
317 if (account_name == NULL)
320 DEBUG ("UOA account %s deleted", account_name);
322 g_hash_table_iter_remove (&iter);
323 g_signal_emit_by_name (self, "deleted", account_name);
325 g_free (account_name);
330 mcp_account_manager_uoa_dispose (GObject *object)
332 McpAccountManagerUoa *self = (McpAccountManagerUoa *) object;
334 tp_clear_object (&self->priv->am);
335 tp_clear_object (&self->priv->manager);
336 tp_clear_pointer (&self->priv->accounts, g_hash_table_unref);
337 tp_clear_object (&self->priv->monitor);
339 G_OBJECT_CLASS (mcp_account_manager_uoa_parent_class)->dispose (object);
343 mcp_account_manager_uoa_init (McpAccountManagerUoa *self)
345 DEBUG ("UOA MC plugin initialised");
347 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
348 MCP_TYPE_ACCOUNT_MANAGER_UOA, McpAccountManagerUoaPrivate);
350 self->priv->accounts = g_hash_table_new_full (g_str_hash, g_str_equal,
351 g_free, g_object_unref);
352 self->priv->pending_signals = g_queue_new ();
354 self->priv->manager = ag_manager_new_for_service_type (SERVICE_TYPE);
355 g_return_if_fail (self->priv->manager != NULL);
357 g_signal_connect (self->priv->manager, "account-created",
358 G_CALLBACK (_account_created_cb), self);
359 g_signal_connect (self->priv->manager, "account-deleted",
360 G_CALLBACK (_account_deleted_cb), self);
362 self->priv->monitor = empathy_webcredentials_monitor_new (
363 self->priv->manager);
367 mcp_account_manager_uoa_class_init (McpAccountManagerUoaClass *klass)
369 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
371 gobject_class->dispose = mcp_account_manager_uoa_dispose;
373 g_type_class_add_private (gobject_class,
374 sizeof (McpAccountManagerUoaPrivate));
378 _ensure_loaded (McpAccountManagerUoa *self)
382 if (self->priv->loaded)
385 self->priv->loaded = TRUE;
387 g_assert (!self->priv->ready);
389 services = ag_manager_get_account_services (self->priv->manager);
390 while (services != NULL)
392 AgAccountService *service = services->data;
393 AgAccount *account = ag_account_service_get_account (service);
394 gchar *account_name = _service_dup_tp_account_name (service);
396 if (account_name != NULL)
398 /* This service was already known, we can add it now */
399 _add_service (self, service, account_name);
400 g_free (account_name);
404 DelayedSignalData *data = g_slice_new0 (DelayedSignalData);
406 /* This service was created while MC was not running, delay its
407 * creation until MC is ready */
408 data->signal = DELAYED_CREATE;
409 data->account_id = account->id;
411 g_queue_push_tail (self->priv->pending_signals, data);
414 g_object_unref (services->data);
415 services = g_list_delete_link (services, services);
420 account_manager_uoa_list (const McpAccountStorage *storage,
421 const McpAccountManager *am)
423 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
424 GList *accounts = NULL;
430 g_return_val_if_fail (self->priv->manager != NULL, NULL);
432 _ensure_loaded (self);
434 g_hash_table_iter_init (&iter, self->priv->accounts);
435 while (g_hash_table_iter_next (&iter, &key, NULL))
436 accounts = g_list_prepend (accounts, g_strdup (key));
442 provider_to_tp_service_name (const gchar *provider_name)
444 /* Well known services are defined in Telepathy spec:
445 * http://telepathy.freedesktop.org/spec/Account.html#Property:Service */
446 if (!tp_strdiff (provider_name, "google"))
447 return "google-talk";
449 return provider_name;
453 account_manager_uoa_get (const McpAccountStorage *storage,
454 const McpAccountManager *am,
455 const gchar *account_name,
458 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
459 AgAccountService *service;
462 gboolean handled = FALSE;
464 g_return_val_if_fail (self->priv->manager != NULL, FALSE);
466 service = g_hash_table_lookup (self->priv->accounts, account_name);
470 DEBUG ("%s: %s, %s", G_STRFUNC, account_name, key);
472 account = ag_account_service_get_account (service);
473 s = ag_account_service_get_service (service);
475 /* NULL key means we want all settings */
478 AgAccountSettingIter iter;
482 ag_account_service_settings_iter_init (service, &iter, KEY_PREFIX);
483 while (ag_account_settings_iter_get_next (&iter, &k, &v))
485 if (!g_variant_is_of_type (v, G_VARIANT_TYPE_STRING))
488 mcp_account_manager_set_value (am, account_name,
489 k, g_variant_get_string (v, NULL));
493 /* Some special keys that are not stored in setting */
494 if (key == NULL || !tp_strdiff (key, "Enabled"))
496 mcp_account_manager_set_value (am, account_name, "Enabled",
497 ag_account_service_get_enabled (service) ? "true" : "false");
501 if (key == NULL || !tp_strdiff (key, "DisplayName"))
503 mcp_account_manager_set_value (am, account_name, "DisplayName",
504 ag_account_get_display_name (account));
508 if (key == NULL || !tp_strdiff (key, "Service"))
510 mcp_account_manager_set_value (am, account_name, "Service",
511 provider_to_tp_service_name (ag_account_get_provider_name (account)));
515 if (key == NULL || !tp_strdiff (key, "Icon"))
517 mcp_account_manager_set_value (am, account_name, "Icon",
518 ag_service_get_icon_name (s));
522 /* If it was none of the above, then just lookup in service' settings */
525 gchar *value = _service_dup_tp_value (service, key);
527 mcp_account_manager_set_value (am, account_name, key, value);
535 account_manager_uoa_set (const McpAccountStorage *storage,
536 const McpAccountManager *am,
537 const gchar *account_name,
541 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
542 AgAccountService *service;
545 g_return_val_if_fail (self->priv->manager != NULL, FALSE);
547 service = g_hash_table_lookup (self->priv->accounts, account_name);
551 account = ag_account_service_get_account (service);
553 DEBUG ("%s: %s, %s, %s", G_STRFUNC, account_name, key, val);
555 if (!tp_strdiff (key, "Enabled"))
557 /* Enabled is a global setting on the account, not per-services,
559 ag_account_select_service (account, NULL);
560 ag_account_set_enabled (account, !tp_strdiff (val, "true"));
562 else if (!tp_strdiff (key, "DisplayName"))
564 ag_account_set_display_name (account, val);
568 _service_set_tp_value (service, key, val);
575 account_manager_uoa_create (const McpAccountStorage *storage,
576 const McpAccountManager *am,
577 const gchar *cm_name,
578 const gchar *protocol_name,
582 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
585 AgAccountService *service;
588 g_return_val_if_fail (self->priv->manager != NULL, NULL);
590 if (!self->priv->ready)
592 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
593 "Cannot create account before being ready");
599 /* Create a new AgAccountService and keep it internally. This won't save it
600 * into persistent storage until account_manager_uoa_commit() is called.
601 * We assume there is only one IM service */
602 account = ag_manager_create_account (self->priv->manager, protocol_name);
603 l = ag_account_list_services_by_type (account, SERVICE_TYPE);
606 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
607 "Cannot create a %s service for %s provider",
608 SERVICE_TYPE, protocol_name);
609 g_object_unref (account);
612 service = ag_account_service_new (account, l->data);
613 ag_service_list_free (l);
614 g_object_unref (account);
616 account_name = mcp_account_manager_get_unique_name (self->priv->am,
617 cm_name, protocol_name, params);
618 _service_set_tp_account_name (service, account_name);
619 g_assert (_add_service (self, service, account_name));
621 /* MC will set all params on the account and commit */
627 account_manager_uoa_delete (const McpAccountStorage *storage,
628 const McpAccountManager *am,
629 const gchar *account_name,
632 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
633 AgAccountService *service;
636 g_return_val_if_fail (self->priv->manager != NULL, FALSE);
638 service = g_hash_table_lookup (self->priv->accounts, account_name);
642 account = ag_account_service_get_account (service);
644 DEBUG ("%s: %s, %s", G_STRFUNC, account_name, key);
648 ag_account_delete (account);
649 g_hash_table_remove (self->priv->accounts, account_name);
653 _service_set_tp_value (service, key, NULL);
660 account_manager_uoa_commit (const McpAccountStorage *storage,
661 const McpAccountManager *am)
663 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
669 g_return_val_if_fail (self->priv->manager != NULL, FALSE);
671 g_hash_table_iter_init (&iter, self->priv->accounts);
672 while (g_hash_table_iter_next (&iter, NULL, &value))
674 AgAccountService *service = value;
675 AgAccount *account = ag_account_service_get_account (service);
677 ag_account_store_async (account, NULL, _account_stored_cb, self);
684 failure_removed_cb (EmpathyWebcredentialsMonitor *monitor,
686 McpAccountManagerUoa *self)
690 DEBUG ("Account '%u' is not failing any more", account->id);
692 l = ag_account_list_services_by_type (account, SERVICE_TYPE);
695 AgAccountService *service = ag_account_service_new (account, l->data);
696 gchar *account_name = _service_dup_tp_account_name (service);
698 if (account_name != NULL)
700 DEBUG ("Reconnect account %s", account_name);
702 mcp_account_storage_emit_reconnect (MCP_ACCOUNT_STORAGE (self),
706 g_free (account_name);
707 g_object_unref (service);
708 ag_service_unref (l->data);
709 l = g_list_delete_link (l, l);
714 account_manager_uoa_ready (const McpAccountStorage *storage,
715 const McpAccountManager *am)
717 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
718 DelayedSignalData *data;
720 g_return_if_fail (self->priv->manager != NULL);
722 if (self->priv->ready)
727 self->priv->ready = TRUE;
728 self->priv->am = g_object_ref (G_OBJECT (am));
730 while ((data = g_queue_pop_head (self->priv->pending_signals)) != NULL)
732 switch (data->signal)
735 _account_created_cb (self->priv->manager, data->account_id, self);
738 _account_deleted_cb (self->priv->manager, data->account_id, self);
741 g_assert_not_reached ();
744 g_slice_free (DelayedSignalData, data);
747 g_queue_free (self->priv->pending_signals);
748 self->priv->pending_signals = NULL;
750 g_signal_connect (self->priv->monitor, "failure-removed",
751 G_CALLBACK (failure_removed_cb), self);
755 account_manager_uoa_get_identifier (const McpAccountStorage *storage,
756 const gchar *account_name,
759 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
760 AgAccountService *service;
763 g_return_if_fail (self->priv->manager != NULL);
765 service = g_hash_table_lookup (self->priv->accounts, account_name);
769 account = ag_account_service_get_account (service);
771 g_value_init (identifier, G_TYPE_UINT);
772 g_value_set_uint (identifier, account->id);
776 account_manager_uoa_get_restrictions (const McpAccountStorage *storage,
777 const gchar *account_name)
779 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
780 AgAccountService *service;
781 guint restrictions = TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_SERVICE;
784 g_return_val_if_fail (self->priv->manager != NULL, 0);
786 /* If we don't know this account, we cannot do anything */
787 service = g_hash_table_lookup (self->priv->accounts, account_name);
791 value = ag_account_service_get_variant (service,
792 KEY_PREFIX KEY_READONLY_PARAMS, NULL);
794 if (value != NULL && g_variant_get_boolean (value))
795 restrictions |= TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PARAMETERS;
797 /* FIXME: We can't set Icon either, but there is no flag for that */
802 account_storage_iface_init (McpAccountStorageIface *iface)
804 mcp_account_storage_iface_set_name (iface, PLUGIN_NAME);
805 mcp_account_storage_iface_set_desc (iface, PLUGIN_DESCRIPTION);
806 mcp_account_storage_iface_set_priority (iface, PLUGIN_PRIORITY);
807 mcp_account_storage_iface_set_provider (iface, PLUGIN_PROVIDER);
809 #define IMPLEMENT(x) mcp_account_storage_iface_implement_##x(iface, \
810 account_manager_uoa_##x)
818 IMPLEMENT (get_identifier);
819 IMPLEMENT (get_restrictions);
823 McpAccountManagerUoa *
824 mcp_account_manager_uoa_new (void)
826 return g_object_new (MCP_TYPE_ACCOUNT_MANAGER_UOA, NULL);