aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac3
-rw-r--r--ubuntu-online-accounts/mc-plugin/mcp-account-manager-uoa.c611
2 files changed, 597 insertions, 17 deletions
diff --git a/configure.ac b/configure.ac
index 1e359f260..290280d1c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -76,6 +76,7 @@ NETWORK_MANAGER_REQUIRED=0.7.0
CHAMPLAIN_REQUIRED=0.12.1
CHEESE_GTK_REQUIRED=3.4.0
LIBACCOUNTS_REQUIRED=1.1
+MC_PLUGINS_REQUIRED=5.13.0
# Use --enable-maintainer-mode to disable deprecated symbols,
# disable single include and enable GSEAL. If this is not a released empathy,
@@ -511,7 +512,7 @@ if test "x$enable_ubuntu_online_accounts" != "xno"; then
PKG_CHECK_MODULES(UOA,
[
account-plugin
- mission-control-plugins
+ mission-control-plugins >= $MC_PLUGINS_REQUIRED
libaccounts-glib >= $LIBACCOUNTS_REQUIRED
], have_uoa="yes", have_uoa="no")
diff --git a/ubuntu-online-accounts/mc-plugin/mcp-account-manager-uoa.c b/ubuntu-online-accounts/mc-plugin/mcp-account-manager-uoa.c
index e38067718..87d966f7d 100644
--- a/ubuntu-online-accounts/mc-plugin/mcp-account-manager-uoa.c
+++ b/ubuntu-online-accounts/mc-plugin/mcp-account-manager-uoa.c
@@ -21,6 +21,11 @@
#include <telepathy-glib/telepathy-glib.h>
+#include <libaccounts-glib/ag-account.h>
+#include <libaccounts-glib/ag-account-service.h>
+#include <libaccounts-glib/ag-manager.h>
+#include <libaccounts-glib/ag-service.h>
+
#include <string.h>
#include <ctype.h>
@@ -31,6 +36,10 @@
#define DEBUG g_debug
+#define SERVICE_TYPE "IM"
+#define KEY_PREFIX "telepathy/"
+#define KEY_ACCOUNT_NAME "mc-account-name"
+
static void account_storage_iface_init (McpAccountStorageIface *iface);
G_DEFINE_TYPE_WITH_CODE (McpAccountManagerUoa, mcp_account_manager_uoa,
@@ -40,11 +49,290 @@ G_DEFINE_TYPE_WITH_CODE (McpAccountManagerUoa, mcp_account_manager_uoa,
struct _McpAccountManagerUoaPrivate
{
+ McpAccountManager *am;
+
+ AgManager *manager;
+
+ /* alloc'ed string -> ref'ed AgAccountService
+ * The key is the account_name, an MC unique identifier.
+ * Note: There could be multiple services in this table having the same
+ * AgAccount, even if unlikely. */
+ GHashTable *accounts;
+
+ /* Queue of owned DelayedSignalData */
+ GQueue *pending_signals;
+
+ gboolean loaded;
+ gboolean ready;
};
+typedef enum {
+ DELAYED_CREATE,
+ DELAYED_DELETE,
+} DelayedSignal;
+
+typedef struct {
+ DelayedSignal signal;
+ AgAccountId account_id;
+} DelayedSignalData;
+
+
+static gchar *
+_service_dup_tp_value (AgAccountService *service,
+ const gchar *key)
+{
+ gchar *real_key = g_strdup_printf (KEY_PREFIX "%s", key);
+ GValue value = { 0, };
+ gchar *ret;
+
+ g_value_init (&value, G_TYPE_STRING);
+ ag_account_service_get_value (service, real_key, &value);
+ ret = g_value_dup_string (&value);
+ g_value_unset (&value);
+
+ return ret;
+}
+
+static void
+_service_set_tp_value (AgAccountService *service,
+ const gchar *key,
+ const gchar *value)
+{
+ gchar *real_key = g_strdup_printf (KEY_PREFIX "%s", key);
+
+ if (value != NULL)
+ {
+ GValue gvalue = { 0, };
+
+ g_value_init (&gvalue, G_TYPE_STRING);
+ g_value_set_string (&gvalue, value);
+ ag_account_service_set_value (service, real_key, &gvalue);
+ g_value_unset (&gvalue);
+ g_free (real_key);
+ }
+ else
+ {
+ ag_account_service_set_value (service, real_key, NULL);
+ }
+}
+
+/* Returns NULL if the account never has been imported into MC before */
+static gchar *
+_service_dup_tp_account_name (AgAccountService *service)
+{
+ return _service_dup_tp_value (service, KEY_ACCOUNT_NAME);
+}
+
+static void
+_service_set_tp_account_name (AgAccountService *service,
+ const gchar *account_name)
+{
+ _service_set_tp_value (service, KEY_ACCOUNT_NAME, account_name);
+}
+
+static void
+_service_enabled_cb (AgAccountService *service,
+ gboolean enabled,
+ McpAccountManagerUoa *self)
+{
+ gchar *account_name = _service_dup_tp_account_name (service);
+
+ if (!self->priv->ready || account_name == NULL)
+ return;
+
+ DEBUG ("UOA account %s toggled: %s", account_name,
+ enabled ? "enabled" : "disabled");
+
+ g_signal_emit_by_name (self, "toggled", account_name, enabled);
+
+ g_free (account_name);
+}
+
+static void
+_service_changed_cb (AgAccountService *service,
+ McpAccountManagerUoa *self)
+{
+ gchar *account_name = _service_dup_tp_account_name (service);
+
+ if (!self->priv->ready || account_name == NULL)
+ return;
+
+ DEBUG ("UOA account %s changed", account_name);
+
+ /* FIXME: Could use ag_account_service_get_changed_fields()
+ * and emit "altered-one" */
+ g_signal_emit_by_name (self, "altered", account_name);
+
+ g_free (account_name);
+}
+
+static void
+_account_stored_cb (AgAccount *account,
+ const GError *error,
+ gpointer user_data)
+{
+ if (error != NULL)
+ {
+ DEBUG ("Error storing UOA account '%s': %s",
+ ag_account_get_display_name (account),
+ error->message);
+ }
+}
+
+static gboolean
+_add_service (McpAccountManagerUoa *self,
+ AgAccountService *service,
+ const gchar *account_name)
+{
+ DEBUG ("UOA account %s added", account_name);
+
+ if (g_hash_table_contains (self->priv->accounts, account_name))
+ {
+ DEBUG ("Already exists, ignoring");
+ return FALSE;
+ }
+
+ g_hash_table_insert (self->priv->accounts,
+ g_strdup (account_name),
+ g_object_ref (service));
+
+ g_signal_connect (service, "enabled",
+ G_CALLBACK (_service_enabled_cb), self);
+ g_signal_connect (service, "changed",
+ G_CALLBACK (_service_changed_cb), self);
+
+ return TRUE;
+}
+
+static void
+_account_created_cb (AgManager *manager,
+ AgAccountId id,
+ McpAccountManagerUoa *self)
+{
+ AgAccount *account;
+ GList *l;
+
+ if (!self->priv->ready)
+ {
+ DelayedSignalData *data = g_slice_new0 (DelayedSignalData);
+
+ data->signal = DELAYED_CREATE;
+ data->account_id = id;
+
+ g_queue_push_tail (self->priv->pending_signals, data);
+ return;
+ }
+
+ account = ag_manager_get_account (self->priv->manager, id);
+
+ l = ag_account_list_services_by_type (account, SERVICE_TYPE);
+ while (l != NULL)
+ {
+ AgAccountService *service = ag_account_service_new (account, l->data);
+ gchar *account_name = _service_dup_tp_account_name (service);
+
+ /* If this is the first time we see this service, we have to generate an
+ * account_name for it. */
+ if (account_name == NULL)
+ {
+ gchar *cm_name = NULL;
+ gchar *protocol_name = NULL;
+ gchar *account_param = NULL;
+
+ cm_name = _service_dup_tp_value (service, "manager");
+ protocol_name = _service_dup_tp_value (service, "protocol");
+ account_param = _service_dup_tp_value (service, "param-account");
+
+ if (!tp_str_empty (cm_name) &&
+ !tp_str_empty (protocol_name) &&
+ !tp_str_empty (account_param))
+ {
+ GHashTable *params;
+
+ params = tp_asv_new (
+ "account", G_TYPE_STRING, account_param,
+ NULL);
+
+ account_name = mcp_account_manager_get_unique_name (self->priv->am,
+ cm_name, protocol_name, params);
+ _service_set_tp_account_name (service, account_name);
+
+ ag_account_store (account, _account_stored_cb, self);
+
+ g_hash_table_unref (params);
+ }
+
+ g_free (cm_name);
+ g_free (protocol_name);
+ g_free (account_param);
+ }
+
+ if (account_name != NULL)
+ {
+ if (_add_service (self, service, account_name))
+ g_signal_emit_by_name (self, "created", account_name);
+ }
+
+ g_free (account_name);
+ g_object_unref (service);
+ ag_service_unref (l->data);
+ l = g_list_delete_link (l, l);
+ }
+
+ g_object_unref (account);
+}
+
+static void
+_account_deleted_cb (AgManager *manager,
+ AgAccountId id,
+ McpAccountManagerUoa *self)
+{
+ GHashTableIter iter;
+ gpointer value;
+
+ if (!self->priv->ready)
+ {
+ DelayedSignalData *data = g_slice_new0 (DelayedSignalData);
+
+ data->signal = DELAYED_DELETE;
+ data->account_id = id;
+
+ g_queue_push_tail (self->priv->pending_signals, data);
+ return;
+ }
+
+ g_hash_table_iter_init (&iter, self->priv->accounts);
+ while (g_hash_table_iter_next (&iter, NULL, &value))
+ {
+ AgAccountService *service = value;
+ AgAccount *account = ag_account_service_get_account (service);
+ gchar *account_name;
+
+ if (account->id != id)
+ continue;
+
+ account_name = _service_dup_tp_account_name (service);
+ if (account_name == NULL)
+ continue;
+
+ DEBUG ("UOA account %s deleted", account_name);
+
+ g_hash_table_iter_remove (&iter);
+ g_signal_emit_by_name (self, "deleted", account_name);
+
+ g_free (account_name);
+ }
+}
+
static void
mcp_account_manager_uoa_dispose (GObject *object)
{
+ McpAccountManagerUoa *self = (McpAccountManagerUoa *) object;
+
+ tp_clear_object (&self->priv->am);
+ tp_clear_object (&self->priv->manager);
+ tp_clear_pointer (&self->priv->accounts, g_hash_table_unref);
+
G_OBJECT_CLASS (mcp_account_manager_uoa_parent_class)->dispose (object);
}
@@ -55,6 +343,17 @@ mcp_account_manager_uoa_init (McpAccountManagerUoa *self)
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
MCP_TYPE_ACCOUNT_MANAGER_UOA, McpAccountManagerUoaPrivate);
+
+ self->priv->accounts = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_object_unref);
+ self->priv->pending_signals = g_queue_new ();
+
+ self->priv->manager = ag_manager_new_for_service_type (SERVICE_TYPE);
+
+ g_signal_connect (self->priv->manager, "account-created",
+ G_CALLBACK (_account_created_cb), self);
+ g_signal_connect (self->priv->manager, "account-deleted",
+ G_CALLBACK (_account_deleted_cb), self);
}
static void
@@ -68,68 +367,347 @@ mcp_account_manager_uoa_class_init (McpAccountManagerUoaClass *klass)
sizeof (McpAccountManagerUoaPrivate));
}
+static void
+_ensure_loaded (McpAccountManagerUoa *self)
+{
+ GList *services;
+
+ if (self->priv->loaded)
+ return;
+
+ self->priv->loaded = TRUE;
+
+ g_assert (!self->priv->ready);
+
+ services = ag_manager_get_account_services (self->priv->manager);
+ while (services != NULL)
+ {
+ AgAccountService *service = services->data;
+ AgAccount *account = ag_account_service_get_account (service);
+ gchar *account_name = _service_dup_tp_account_name (service);
+
+ if (account_name != NULL)
+ {
+ /* This service was already known, we can add it now */
+ _add_service (self, service, account_name);
+ g_free (account_name);
+ }
+ else
+ {
+ DelayedSignalData *data = g_slice_new0 (DelayedSignalData);
+
+ /* This service was created while MC was not running, delay its
+ * creation until MC is ready */
+ data->signal = DELAYED_CREATE;
+ data->account_id = account->id;
+
+ g_queue_push_tail (self->priv->pending_signals, data);
+ }
+
+ g_object_unref (services->data);
+ services = g_list_delete_link (services, services);
+ }
+}
+
static GList *
account_manager_uoa_list (const McpAccountStorage *storage,
const McpAccountManager *am)
{
- return NULL;
+ McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
+ GList *accounts = NULL;
+ GHashTableIter iter;
+ gpointer key;
+
+ DEBUG (G_STRFUNC);
+
+ _ensure_loaded (self);
+
+ g_hash_table_iter_init (&iter, self->priv->accounts);
+ while (g_hash_table_iter_next (&iter, &key, NULL))
+ accounts = g_list_prepend (accounts, g_strdup (key));
+
+ return accounts;
}
static gboolean
account_manager_uoa_get (const McpAccountStorage *storage,
const McpAccountManager *am,
- const gchar *acc,
+ const gchar *account_name,
const gchar *key)
{
- return FALSE;
+ McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
+ AgAccountService *service;
+ AgAccount *account;
+ AgService *s;
+ gboolean handled = FALSE;
+
+ service = g_hash_table_lookup (self->priv->accounts, account_name);
+ if (service == NULL)
+ return FALSE;
+
+ DEBUG ("%s: %s, %s", G_STRFUNC, account_name, key);
+
+ account = ag_account_service_get_account (service);
+ s = ag_account_service_get_service (service);
+
+ /* NULL key means we want all settings */
+ if (key == NULL)
+ {
+ AgAccountSettingIter iter;
+ const gchar *k;
+ const GValue *v;
+
+ ag_account_service_settings_iter_init (service, &iter, KEY_PREFIX);
+ while (ag_account_service_settings_iter_next (&iter, &k, &v))
+ {
+ if (!G_VALUE_HOLDS_STRING (v))
+ continue;
+
+ mcp_account_manager_set_value (am, account_name,
+ k, g_value_get_string (v));
+ }
+ }
+
+ /* Some special keys that are not stored in setting */
+ if (key == NULL || !tp_strdiff (key, "Enabled"))
+ {
+ mcp_account_manager_set_value (am, account_name, "Enabled",
+ ag_account_service_get_enabled (service) ? "true" : "false");
+ handled = TRUE;
+ }
+
+ if (key == NULL || !tp_strdiff (key, "DisplayName"))
+ {
+ mcp_account_manager_set_value (am, account_name, "DisplayName",
+ ag_account_get_display_name (account));
+ handled = TRUE;
+ }
+
+ if (key == NULL || !tp_strdiff (key, "Service"))
+ {
+ mcp_account_manager_set_value (am, account_name, "Service",
+ ag_account_get_provider_name (account));
+ handled = TRUE;
+ }
+
+ if (key == NULL || !tp_strdiff (key, "Icon"))
+ {
+ mcp_account_manager_set_value (am, account_name, "Icon",
+ ag_service_get_icon_name (s));
+ handled = TRUE;
+ }
+
+ /* If it was none of the above, then just lookup in service' settings */
+ if (!handled)
+ {
+ gchar *value = _service_dup_tp_value (service, key);
+
+ mcp_account_manager_set_value (am, account_name, key, value);
+ g_free (value);
+ }
+
+ return TRUE;
}
static gboolean
account_manager_uoa_set (const McpAccountStorage *storage,
const McpAccountManager *am,
- const gchar *acc,
+ const gchar *account_name,
const gchar *key,
const gchar *val)
{
- return FALSE;
+ McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
+ AgAccountService *service;
+ AgAccount *account;
+
+ service = g_hash_table_lookup (self->priv->accounts, account_name);
+ if (service == NULL)
+ return FALSE;
+
+ account = ag_account_service_get_account (service);
+
+ DEBUG ("%s: %s, %s, %s", G_STRFUNC, account_name, key, val);
+
+ if (!tp_strdiff (key, "Enabled"))
+ {
+ ag_account_set_enabled (account, !tp_strdiff (val, "true"));
+ }
+ else if (!tp_strdiff (key, "DisplayName"))
+ {
+ ag_account_set_display_name (account, val);
+ }
+ else
+ {
+ _service_set_tp_value (service, key, val);
+ }
+
+ return TRUE;
+}
+
+static gchar *
+account_manager_uoa_create (const McpAccountStorage *storage,
+ const McpAccountManager *am,
+ const gchar *cm_name,
+ const gchar *protocol_name,
+ GHashTable *params,
+ GError **error)
+{
+ McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
+ gchar *account_name;
+ AgAccount *account;
+ AgAccountService *service;
+ GList *l;
+
+ if (!self->priv->ready)
+ {
+ g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+ "Cannot create account before being ready");
+ return NULL;
+ }
+
+ DEBUG (G_STRFUNC);
+
+ /* Create a new AgAccountService and keep it internally. This won't save it
+ * into persistent storage until account_manager_uoa_commit() is called.
+ * We assume there is only one IM service */
+ account = ag_manager_create_account (self->priv->manager, protocol_name);
+ l = ag_account_list_services_by_type (account, SERVICE_TYPE);
+ if (l == NULL)
+ {
+ g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+ "Cannot create a %s service for %s provider",
+ SERVICE_TYPE, protocol_name);
+ g_object_unref (account);
+ return NULL;
+ }
+ service = ag_account_service_new (account, l->data);
+ ag_service_list_free (l);
+ g_object_unref (account);
+
+ account_name = mcp_account_manager_get_unique_name (self->priv->am,
+ cm_name, protocol_name, params);
+ _service_set_tp_account_name (service, account_name);
+ g_assert (_add_service (self, service, account_name));
+
+ /* MC will set all params on the account and commit */
+
+ return account_name;
}
static gboolean
account_manager_uoa_delete (const McpAccountStorage *storage,
const McpAccountManager *am,
- const gchar *acc,
+ const gchar *account_name,
const gchar *key)
{
- return FALSE;
+ McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
+ AgAccountService *service;
+ AgAccount *account;
+
+ service = g_hash_table_lookup (self->priv->accounts, account_name);
+ if (service == NULL)
+ return FALSE;
+
+ account = ag_account_service_get_account (service);
+
+ DEBUG ("%s: %s, %s", G_STRFUNC, account_name, key);
+
+ if (key == NULL)
+ {
+ ag_account_delete (account);
+ g_hash_table_remove (self->priv->accounts, account_name);
+ }
+ else
+ {
+ _service_set_tp_value (service, key, NULL);
+ }
+
+ return TRUE;
}
static gboolean
account_manager_uoa_commit (const McpAccountStorage *storage,
const McpAccountManager *am)
{
- return FALSE;
+ McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
+ GHashTableIter iter;
+ gpointer value;
+
+ DEBUG (G_STRFUNC);
+
+ g_hash_table_iter_init (&iter, self->priv->accounts);
+ while (g_hash_table_iter_next (&iter, NULL, &value))
+ {
+ AgAccountService *service = value;
+ AgAccount *account = ag_account_service_get_account (service);
+
+ ag_account_store (account, _account_stored_cb, self);
+ }
+
+ return TRUE;
}
static void
account_manager_uoa_ready (const McpAccountStorage *storage,
const McpAccountManager *am)
{
+ McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
+ DelayedSignalData *data;
+
+ if (self->priv->ready)
+ return;
+
+ DEBUG (G_STRFUNC);
+
+ self->priv->ready = TRUE;
+ self->priv->am = g_object_ref (G_OBJECT (am));
+
+ while ((data = g_queue_pop_head (self->priv->pending_signals)) != NULL)
+ {
+ switch (data->signal)
+ {
+ case DELAYED_CREATE:
+ _account_created_cb (self->priv->manager, data->account_id, self);
+ break;
+ case DELAYED_DELETE:
+ _account_deleted_cb (self->priv->manager, data->account_id, self);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ g_slice_free (DelayedSignalData, data);
+ }
+
+ g_queue_free (self->priv->pending_signals);
+ self->priv->pending_signals = NULL;
}
static void
account_manager_uoa_get_identifier (const McpAccountStorage *storage,
- const gchar *acc,
+ const gchar *account_name,
GValue *identifier)
{
+ McpAccountManagerUoa *self = (McpAccountManagerUoa *) storage;
+ AgAccountService *service;
+ AgAccount *account;
+
+ service = g_hash_table_lookup (self->priv->accounts, account_name);
+ if (service == NULL)
+ return;
+
+ account = ag_account_service_get_account (service);
+
+ g_value_init (identifier, G_TYPE_UINT);
+ g_value_set_uint (identifier, account->id);
}
-static gchar *
-account_manager_uoa_create_account (const McpAccountStorage *storage,
- const gchar *cm_name,
- const gchar *protocol_name,
- GHashTable *params)
+static guint
+account_manager_uoa_get_restrictions (const McpAccountStorage *self,
+ const gchar *account_name)
{
- return NULL;
+ /* FIXME: We can't set Icon either, but there is no flag for that */
+ return TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_SERVICE;
}
static void
@@ -145,11 +723,12 @@ account_storage_iface_init (McpAccountStorageIface *iface)
IMPLEMENT (get);
IMPLEMENT (list);
IMPLEMENT (set);
+ IMPLEMENT (create);
IMPLEMENT (delete);
IMPLEMENT (commit);
IMPLEMENT (ready);
IMPLEMENT (get_identifier);
- IMPLEMENT (create_account);
+ IMPLEMENT (get_restrictions);
#undef IMPLEMENT
}