[empathy] UOA mc-plugin: Actually implement it



commit f32e5ba5af1dfa3a0ceb4fde6e96b31a74b195c5
Author: Xavier Claessens <xavier claessens collabora co uk>
Date:   Tue Jul 17 19:50:05 2012 +0200

    UOA mc-plugin: Actually implement it

 configure.ac                                       |    3 +-
 .../mc-plugin/mcp-account-manager-uoa.c            |  611 +++++++++++++++++++-
 2 files changed, 597 insertions(+), 17 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 1e359f2..290280d 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 e380677..87d966f 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
 }
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]