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 #include "empathy-webcredentials-monitor.h"
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
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"
46 static void account_storage_iface_init (McpAccountStorageIface *iface);
48 G_DEFINE_TYPE_WITH_CODE (McpAccountManagerUoa, mcp_account_manager_uoa,
50 G_IMPLEMENT_INTERFACE (MCP_TYPE_ACCOUNT_STORAGE,
51 account_storage_iface_init));
53 struct _McpAccountManagerUoaPrivate
55 McpAccountManager *am;
58 EmpathyWebcredentialsMonitor *monitor;
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. */
66 /* Queue of owned DelayedSignalData */
67 GQueue *pending_signals;
80 AgAccountId account_id;
85 _service_dup_tp_value (AgAccountService *service,
88 gchar *real_key = g_strdup_printf (KEY_PREFIX "%s", key);
91 value = ag_account_service_get_variant (service, real_key, NULL);
96 return g_variant_dup_string (value, NULL);
100 _service_set_tp_value (AgAccountService *service,
104 gchar *real_key = g_strdup_printf (KEY_PREFIX "%s", key);
108 GVariant *gvariant = g_variant_new_string (value);
109 ag_account_service_set_variant (service, real_key, gvariant);
113 ag_account_service_set_variant (service, real_key, NULL);
118 /* Returns NULL if the account never has been imported into MC before */
120 _service_dup_tp_account_name (AgAccountService *service)
122 return _service_dup_tp_value (service, KEY_ACCOUNT_NAME);
126 _service_set_tp_account_name (AgAccountService *service,
127 const gchar *account_name)
129 _service_set_tp_value (service, KEY_ACCOUNT_NAME, account_name);
133 _service_enabled_cb (AgAccountService *service,
135 McpAccountManagerUoa *self)
137 gchar *account_name = _service_dup_tp_account_name (service);
139 if (!self->priv->ready || account_name == NULL)
142 DEBUG ("UOA account %s toggled: %s", account_name,
143 enabled ? "enabled" : "disabled");
145 g_signal_emit_by_name (self, "toggled", account_name, enabled);
147 g_free (account_name);
151 _service_changed_cb (AgAccountService *service,
152 McpAccountManagerUoa *self)
154 gchar *account_name = _service_dup_tp_account_name (service);
156 if (!self->priv->ready || account_name == NULL)
159 DEBUG ("UOA account %s changed", account_name);
161 /* FIXME: Could use ag_account_service_get_changed_fields()
162 * and emit "altered-one" */
163 g_signal_emit_by_name (self, "altered", account_name);
165 g_free (account_name);
169 _account_stored_cb (GObject *source_object,
173 AgAccount *account = AG_ACCOUNT(source_object);
174 GError *error = NULL;
176 if (!ag_account_store_finish (account, res, &error))
178 g_assert (error != NULL);
179 DEBUG ("Error storing UOA account '%s': %s",
180 ag_account_get_display_name (account),
182 g_error_free (error);
187 _add_service (McpAccountManagerUoa *self,
188 AgAccountService *service,
189 const gchar *account_name)
191 DEBUG ("UOA account %s added", account_name);
193 if (g_hash_table_contains (self->priv->accounts, account_name))
195 DEBUG ("Already exists, ignoring");
199 g_hash_table_insert (self->priv->accounts,
200 g_strdup (account_name),
201 g_object_ref (service));
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);
212 _account_created_cb (AgManager *manager,
214 McpAccountManagerUoa *self)
219 if (!self->priv->ready)
221 DelayedSignalData *data = g_slice_new0 (DelayedSignalData);
223 data->signal = DELAYED_CREATE;
224 data->account_id = id;
226 g_queue_push_tail (self->priv->pending_signals, data);
230 account = ag_manager_get_account (self->priv->manager, id);
232 l = ag_account_list_services_by_type (account, SERVICE_TYPE);
235 AgAccountService *service = ag_account_service_new (account, l->data);
236 gchar *account_name = _service_dup_tp_account_name (service);
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)
242 gchar *cm_name = NULL;
243 gchar *protocol_name = NULL;
244 gchar *account_param = NULL;
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");
250 if (!tp_str_empty (cm_name) &&
251 !tp_str_empty (protocol_name) &&
252 !tp_str_empty (account_param))
256 params = tp_asv_new (
257 "account", G_TYPE_STRING, account_param,
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);
264 ag_account_store_async (account, NULL, _account_stored_cb, self);
266 g_hash_table_unref (params);
270 g_free (protocol_name);
271 g_free (account_param);
274 if (account_name != NULL)
276 if (_add_service (self, service, account_name))
277 g_signal_emit_by_name (self, "created", account_name);
280 g_free (account_name);
281 g_object_unref (service);
282 ag_service_unref (l->data);
283 l = g_list_delete_link (l, l);
286 g_object_unref (account);
290 _account_deleted_cb (AgManager *manager,
292 McpAccountManagerUoa *self)
297 if (!self->priv->ready)
299 DelayedSignalData *data = g_slice_new0 (DelayedSignalData);
301 data->signal = DELAYED_DELETE;
302 data->account_id = id;
304 g_queue_push_tail (self->priv->pending_signals, data);
308 g_hash_table_iter_init (&iter, self->priv->accounts);
309 while (g_hash_table_iter_next (&iter, NULL, &value))
311 AgAccountService *service = value;
312 AgAccount *account = ag_account_service_get_account (service);
315 if (account->id != id)
318 account_name = _service_dup_tp_account_name (service);
319 if (account_name == NULL)
322 DEBUG ("UOA account %s deleted", account_name);
324 g_hash_table_iter_remove (&iter);
325 g_signal_emit_by_name (self, "deleted", account_name);
327 g_free (account_name);
332 mcp_account_manager_uoa_dispose (GObject *object)
334 McpAccountManagerUoa *self = (McpAccountManagerUoa *) object;
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);
341 G_OBJECT_CLASS (mcp_account_manager_uoa_parent_class)->dispose (object);
345 mcp_account_manager_uoa_init (McpAccountManagerUoa *self)
347 DEBUG ("UOA MC plugin initialised");
349 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
350 MCP_TYPE_ACCOUNT_MANAGER_UOA, McpAccountManagerUoaPrivate);
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 ();
356 self->priv->manager = ag_manager_new_for_service_type (SERVICE_TYPE);
357 g_return_if_fail (self->priv->manager != NULL);
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);
364 self->priv->monitor = empathy_webcredentials_monitor_new (
365 self->priv->manager);
369 mcp_account_manager_uoa_class_init (McpAccountManagerUoaClass *klass)
371 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
373 gobject_class->dispose = mcp_account_manager_uoa_dispose;
375 g_type_class_add_private (gobject_class,
376 sizeof (McpAccountManagerUoaPrivate));
380 _ensure_loaded (McpAccountManagerUoa *self)
384 if (self->priv->loaded)
387 self->priv->loaded = TRUE;
389 g_assert (!self->priv->ready);
391 services = ag_manager_get_account_services (self->priv->manager);
392 while (services != NULL)
394 AgAccountService *service = services->data;
395 AgAccount *account = ag_account_service_get_account (service);
396 gchar *account_name = _service_dup_tp_account_name (service);
398 if (account_name != NULL)
400 /* This service was already known, we can add it now */
401 _add_service (self, service, account_name);
402 g_free (account_name);
406 DelayedSignalData *data = g_slice_new0 (DelayedSignalData);
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;
413 g_queue_push_tail (self->priv->pending_signals, data);
416 g_object_unref (services->data);
417 services = g_list_delete_link (services, services);
422 account_manager_uoa_list (const McpAccountStorage *storage,
423 const McpAccountManager *am)
425 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
426 GList *accounts = NULL;
432 g_return_val_if_fail (self->priv->manager != NULL, NULL);
434 _ensure_loaded (self);
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));
444 provider_to_tp_service_name (const gchar *provider_name)
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";
451 return provider_name;
455 account_manager_uoa_get (const McpAccountStorage *storage,
456 const McpAccountManager *am,
457 const gchar *account_name,
460 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
461 AgAccountService *service;
464 gboolean handled = FALSE;
466 g_return_val_if_fail (self->priv->manager != NULL, FALSE);
468 service = g_hash_table_lookup (self->priv->accounts, account_name);
472 DEBUG ("%s: %s, %s", G_STRFUNC, account_name, key);
474 account = ag_account_service_get_account (service);
475 s = ag_account_service_get_service (service);
477 /* NULL key means we want all settings */
480 AgAccountSettingIter iter;
484 ag_account_service_settings_iter_init (service, &iter, KEY_PREFIX);
485 while (ag_account_settings_iter_get_next (&iter, &k, &v))
487 if (!g_variant_is_of_type (v, G_VARIANT_TYPE_STRING))
490 mcp_account_manager_set_value (am, account_name,
491 k, g_variant_get_string (v, NULL));
495 /* Some special keys that are not stored in setting */
496 if (key == NULL || !tp_strdiff (key, "Enabled"))
498 mcp_account_manager_set_value (am, account_name, "Enabled",
499 ag_account_service_get_enabled (service) ? "true" : "false");
503 if (key == NULL || !tp_strdiff (key, "DisplayName"))
505 mcp_account_manager_set_value (am, account_name, "DisplayName",
506 ag_account_get_display_name (account));
510 if (key == NULL || !tp_strdiff (key, "Service"))
512 mcp_account_manager_set_value (am, account_name, "Service",
513 provider_to_tp_service_name (ag_account_get_provider_name (account)));
517 if (key == NULL || !tp_strdiff (key, "Icon"))
519 mcp_account_manager_set_value (am, account_name, "Icon",
520 ag_service_get_icon_name (s));
524 /* If it was none of the above, then just lookup in service' settings */
527 gchar *value = _service_dup_tp_value (service, key);
529 mcp_account_manager_set_value (am, account_name, key, value);
537 account_manager_uoa_set (const McpAccountStorage *storage,
538 const McpAccountManager *am,
539 const gchar *account_name,
543 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
544 AgAccountService *service;
547 g_return_val_if_fail (self->priv->manager != NULL, FALSE);
549 service = g_hash_table_lookup (self->priv->accounts, account_name);
553 account = ag_account_service_get_account (service);
555 DEBUG ("%s: %s, %s, %s", G_STRFUNC, account_name, key, val);
557 if (!tp_strdiff (key, "Enabled"))
559 /* Enabled is a global setting on the account, not per-services,
561 ag_account_select_service (account, NULL);
562 ag_account_set_enabled (account, !tp_strdiff (val, "true"));
564 else if (!tp_strdiff (key, "DisplayName"))
566 ag_account_set_display_name (account, val);
570 _service_set_tp_value (service, key, val);
577 account_manager_uoa_create (const McpAccountStorage *storage,
578 const McpAccountManager *am,
579 const gchar *cm_name,
580 const gchar *protocol_name,
584 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
587 AgAccountService *service;
590 g_return_val_if_fail (self->priv->manager != NULL, NULL);
592 if (!self->priv->ready)
594 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
595 "Cannot create account before being ready");
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);
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);
614 service = ag_account_service_new (account, l->data);
615 ag_service_list_free (l);
616 g_object_unref (account);
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));
623 /* MC will set all params on the account and commit */
629 account_manager_uoa_delete (const McpAccountStorage *storage,
630 const McpAccountManager *am,
631 const gchar *account_name,
634 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
635 AgAccountService *service;
638 g_return_val_if_fail (self->priv->manager != NULL, FALSE);
640 service = g_hash_table_lookup (self->priv->accounts, account_name);
644 account = ag_account_service_get_account (service);
646 DEBUG ("%s: %s, %s", G_STRFUNC, account_name, key);
650 ag_account_delete (account);
651 g_hash_table_remove (self->priv->accounts, account_name);
655 _service_set_tp_value (service, key, NULL);
662 account_manager_uoa_commit (const McpAccountStorage *storage,
663 const McpAccountManager *am)
665 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
671 g_return_val_if_fail (self->priv->manager != NULL, FALSE);
673 g_hash_table_iter_init (&iter, self->priv->accounts);
674 while (g_hash_table_iter_next (&iter, NULL, &value))
676 AgAccountService *service = value;
677 AgAccount *account = ag_account_service_get_account (service);
679 ag_account_store_async (account, NULL, _account_stored_cb, self);
686 failure_removed_cb (EmpathyWebcredentialsMonitor *monitor,
688 McpAccountManagerUoa *self)
692 DEBUG ("Account '%u' is not failing any more", account->id);
694 l = ag_account_list_services_by_type (account, SERVICE_TYPE);
697 AgAccountService *service = ag_account_service_new (account, l->data);
698 gchar *account_name = _service_dup_tp_account_name (service);
700 if (account_name != NULL)
702 DEBUG ("Reconnect account %s", account_name);
704 mcp_account_storage_emit_reconnect (MCP_ACCOUNT_STORAGE (self),
708 g_free (account_name);
709 g_object_unref (service);
710 ag_service_unref (l->data);
711 l = g_list_delete_link (l, l);
716 account_manager_uoa_ready (const McpAccountStorage *storage,
717 const McpAccountManager *am)
719 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
720 DelayedSignalData *data;
722 g_return_if_fail (self->priv->manager != NULL);
724 if (self->priv->ready)
729 self->priv->ready = TRUE;
730 self->priv->am = g_object_ref (G_OBJECT (am));
732 while ((data = g_queue_pop_head (self->priv->pending_signals)) != NULL)
734 switch (data->signal)
737 _account_created_cb (self->priv->manager, data->account_id, self);
740 _account_deleted_cb (self->priv->manager, data->account_id, self);
743 g_assert_not_reached ();
746 g_slice_free (DelayedSignalData, data);
749 g_queue_free (self->priv->pending_signals);
750 self->priv->pending_signals = NULL;
752 g_signal_connect (self->priv->monitor, "failure-removed",
753 G_CALLBACK (failure_removed_cb), self);
757 account_manager_uoa_get_identifier (const McpAccountStorage *storage,
758 const gchar *account_name,
761 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
762 AgAccountService *service;
765 g_return_if_fail (self->priv->manager != NULL);
767 service = g_hash_table_lookup (self->priv->accounts, account_name);
771 account = ag_account_service_get_account (service);
773 g_value_init (identifier, G_TYPE_UINT);
774 g_value_set_uint (identifier, account->id);
778 account_manager_uoa_get_restrictions (const McpAccountStorage *storage,
779 const gchar *account_name)
781 McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
782 AgAccountService *service;
783 guint restrictions = TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_SERVICE;
786 g_return_val_if_fail (self->priv->manager != NULL, 0);
788 /* If we don't know this account, we cannot do anything */
789 service = g_hash_table_lookup (self->priv->accounts, account_name);
793 value = ag_account_service_get_variant (service,
794 KEY_PREFIX KEY_READONLY_PARAMS, NULL);
796 if (value != NULL && g_variant_get_boolean (value))
797 restrictions |= TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PARAMETERS;
799 /* FIXME: We can't set Icon either, but there is no flag for that */
804 account_storage_iface_init (McpAccountStorageIface *iface)
806 mcp_account_storage_iface_set_name (iface, PLUGIN_NAME);
807 mcp_account_storage_iface_set_desc (iface, PLUGIN_DESCRIPTION);
808 mcp_account_storage_iface_set_priority (iface, PLUGIN_PRIORITY);
809 mcp_account_storage_iface_set_provider (iface, PLUGIN_PROVIDER);
811 #define IMPLEMENT(x) mcp_account_storage_iface_implement_##x(iface, \
812 account_manager_uoa_##x)
820 IMPLEMENT (get_identifier);
821 IMPLEMENT (get_restrictions);
825 McpAccountManagerUoa *
826 mcp_account_manager_uoa_new (void)
828 return g_object_new (MCP_TYPE_ACCOUNT_MANAGER_UOA, NULL);