[empathy] Import Facebook and windows Live GOA accounts



commit d3e575fd305487eb8a15a66941651325904ba90b
Author: Xavier Claessens <xclaesse gmail com>
Date:   Thu Oct 27 12:09:59 2011 +0200

    Import Facebook and windows Live GOA accounts
    
    Implement their auth mechanisms
    
    Fixes bug #661068 and #652544

 configure.ac                            |    6 +
 goa-mc-plugin/mcp-account-manager-goa.c |   28 ++-
 libempathy/Makefile.am                  |    2 +
 libempathy/empathy-auth-factory.c       |   98 +++++--
 libempathy/empathy-goa-auth-handler.c   |  436 +++++++++++++++++++++++++++++++
 libempathy/empathy-goa-auth-handler.h   |   72 +++++
 libempathy/empathy-utils.c              |   15 +
 libempathy/empathy-utils.h              |    3 +
 8 files changed, 631 insertions(+), 29 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index a8f7e4e..1c0853c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -47,6 +47,7 @@ TELEPATHY_FARSIGHT_REQUIRED=0.0.14
 TELEPATHY_GLIB_REQUIRED=0.16.0
 TELEPATHY_LOGGER=0.2.10
 WEBKIT_REQUIRED=1.3.13
+GOA_REQUIRED=3.3.0
 
 # Optional deps
 CLUTTER_REQUIRED=1.7.14
@@ -199,6 +200,8 @@ PKG_CHECK_MODULES(EMPATHY,
    libpulse
    libpulse-mainloop-glib
    webkitgtk-3.0 >= $WEBKIT_REQUIRED
+   libsoup-2.4
+   goa-1.0 >= $GOA_REQUIRED
 ])
 
 PKG_CHECK_MODULES(YELL, [telepathy-yell])
@@ -209,6 +212,9 @@ PKG_CHECK_MODULES(EMPATHY_AV,
    telepathy-farsight >= $TELEPATHY_FARSIGHT_REQUIRED
 ])
 
+AC_DEFINE(EMPATHY_GOA_PROVIDER, "org.gnome.OnlineAccounts",
+   [Name of provider for accounts imported from GOA])
+
 # -----------------------------------------------------------
 # Call interface
 # -----------------------------------------------------------
diff --git a/goa-mc-plugin/mcp-account-manager-goa.c b/goa-mc-plugin/mcp-account-manager-goa.c
index d81c608..1185d7b 100644
--- a/goa-mc-plugin/mcp-account-manager-goa.c
+++ b/goa-mc-plugin/mcp-account-manager-goa.c
@@ -23,6 +23,8 @@
  *    Danielle Madeley <danielle madeley collabora co uk>
  */
 
+#include "config.h"
+
 #include <glib/gi18n.h>
 
 #include <telepathy-glib/util.h>
@@ -40,7 +42,7 @@
 #define PLUGIN_NAME "goa"
 #define PLUGIN_PRIORITY (MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_KEYRING + 10)
 #define PLUGIN_DESCRIPTION "Provide Telepathy Accounts from GOA"
-#define PLUGIN_PROVIDER "org.gnome.OnlineAccounts"
+#define PLUGIN_PROVIDER EMPATHY_GOA_PROVIDER
 
 #define INITIAL_COMMENT "Parameters of GOA Telepathy accounts"
 
@@ -125,15 +127,33 @@ get_tp_parameters (GoaAccount *account)
       PARAM ("param-extra-certificate-identities", "talk.google.com");
       PARAM ("param-require-encryption", "true");
     }
+  else if (!tp_strdiff (type, "facebook"))
+    {
+      PARAM ("manager", "gabble");
+      PARAM ("protocol", "jabber");
+      PARAM ("Icon", "im-facebook");
+      PARAM ("Service", "facebook");
+
+      PARAM ("param-account", "chat.facebook.com");
+      PARAM ("param-require-encryption", "true");
+    }
+  else if (!tp_strdiff (type, "windows_live"))
+    {
+      PARAM ("manager", "gabble");
+      PARAM ("protocol", "jabber");
+      PARAM ("Icon", "im-msn");
+      PARAM ("Service", "windows-live");
+
+      PARAM ("param-account", "messenger.live.com");
+      PARAM ("param-require-encryption", "true");
+    }
   else
     {
-      /* unknown account type */
+      DEBUG ("Unknown account type %s", type);
       g_hash_table_destroy (params);
       return NULL;
     }
 
-  /* TODO: add Facebook support */
-
   /* generic properties */
   PARAM ("DisplayName", goa_account_get_presentation_identity (account));
 
diff --git a/libempathy/Makefile.am b/libempathy/Makefile.am
index 079b473..0662f2c 100644
--- a/libempathy/Makefile.am
+++ b/libempathy/Makefile.am
@@ -29,6 +29,7 @@ libempathy_headers =				\
 	action-chain-internal.h			\
 	empathy-account-settings.h		\
 	empathy-auth-factory.h			\
+	empathy-goa-auth-handler.h		\
 	empathy-camera-monitor.h		\
 	empathy-chatroom-manager.h		\
 	empathy-chatroom.h			\
@@ -72,6 +73,7 @@ libempathy_handwritten_source =				\
 	action-chain.c					\
 	empathy-account-settings.c			\
 	empathy-auth-factory.c				\
+	empathy-goa-auth-handler.c			\
 	empathy-camera-monitor.c			\
 	empathy-chatroom-manager.c			\
 	empathy-chatroom.c				\
diff --git a/libempathy/empathy-auth-factory.c b/libempathy/empathy-auth-factory.c
index 663dce0..03f2cd1 100644
--- a/libempathy/empathy-auth-factory.c
+++ b/libempathy/empathy-auth-factory.c
@@ -27,6 +27,7 @@
 #include "empathy-keyring.h"
 #include "empathy-server-sasl-handler.h"
 #include "empathy-server-tls-handler.h"
+#include "empathy-goa-auth-handler.h"
 #include "empathy-utils.h"
 
 #include "extensions/extensions.h"
@@ -42,6 +43,7 @@ struct _EmpathyAuthFactoryPriv {
    * reffed (EmpathyServerSASLHandler *)
    * */
   GHashTable *sasl_handlers;
+  EmpathyGoaAuthHandler *goa_handler;
 
   gboolean dispose_run;
 };
@@ -190,8 +192,6 @@ common_checks (EmpathyAuthFactory *self,
 {
   EmpathyAuthFactoryPriv *priv = GET_PRIV (self);
   TpChannel *channel;
-  GHashTable *props;
-  const gchar * const *available_mechanisms;
   const GError *dbus_error;
   EmpathyServerSASLHandler *handler;
 
@@ -244,23 +244,7 @@ common_checks (EmpathyAuthFactory *self,
       return FALSE;
     }
 
-  props = tp_channel_borrow_immutable_properties (channel);
-  available_mechanisms = tp_asv_get_boxed (props,
-      TP_PROP_CHANNEL_INTERFACE_SASL_AUTHENTICATION_AVAILABLE_MECHANISMS,
-      G_TYPE_STRV);
-
-  if (tp_channel_get_channel_type_id (channel) ==
-      TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION
-      && !tp_strv_contains (available_mechanisms, "X-TELEPATHY-PASSWORD"))
-    {
-      g_set_error_literal (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
-          "Only the X-TELEPATHY-PASSWORD SASL mechanism is supported");
-
-      return FALSE;
-    }
-
   dbus_error = tp_proxy_get_invalidated (channel);
-
   if (dbus_error != NULL)
     {
       *error = g_error_copy (dbus_error);
@@ -297,6 +281,20 @@ handle_channels (TpBaseClient *handler,
   /* The common checks above have checked this is fine. */
   channel = channels->data;
 
+  /* Only password authentication is supported from here */
+  if (tp_channel_get_channel_type_id (channel) ==
+      TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION &&
+      !empathy_sasl_channel_supports_mechanism (channel,
+          "X-TELEPATHY-PASSWORD"))
+    {
+      g_set_error_literal (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+          "Only the X-TELEPATHY-PASSWORD SASL mechanism is supported");
+      DEBUG ("%s", error->message);
+      tp_handle_channels_context_fail (context, error);
+      g_clear_error (&error);
+      return;
+    }
+
   data = handler_context_data_new (self, context);
   tp_handle_channels_context_delay (context);
 
@@ -335,7 +333,7 @@ observe_channels_data_free (ObserveChannelsData *data)
 }
 
 static void
-claim_cb (GObject *source,
+password_claim_cb (GObject *source,
     GAsyncResult *result,
     gpointer user_data)
 {
@@ -387,13 +385,37 @@ get_password_cb (GObject *source,
           tp_proxy_get_object_path (source));
 
       tp_channel_dispatch_operation_claim_with_async (data->dispatch_operation,
-          TP_BASE_CLIENT (data->self), claim_cb, data);
+          TP_BASE_CLIENT (data->self), password_claim_cb, data);
 
       tp_observe_channels_context_accept (data->context);
     }
 }
 
 static void
+goa_claim_cb (GObject *source,
+    GAsyncResult *result,
+    gpointer user_data)
+{
+  ObserveChannelsData *data = user_data;
+  EmpathyAuthFactory *self = data->self;
+  GError *error = NULL;
+
+  if (!tp_channel_dispatch_operation_claim_with_finish (data->dispatch_operation,
+          result, &error))
+    {
+      DEBUG ("Failed to claim: %s", error->message);
+      g_clear_error (&error);
+    }
+  else
+    {
+      empathy_goa_auth_handler_start (self->priv->goa_handler,
+          data->channel, data->account);
+    }
+
+  observe_channels_data_free (data);
+}
+
+static void
 observe_channels (TpBaseClient *client,
     TpAccount *account,
     TpConnection *connection,
@@ -417,9 +439,7 @@ observe_channels (TpBaseClient *client,
       return;
     }
 
-  /* We're now sure this is a server auth channel using the SASL auth
-   * type and X-TELEPATHY-PASSWORD is available. Great. */
-
+  /* The common checks above have checked this is fine. */
   channel = channels->data;
 
   data = g_slice_new0 (ObserveChannelsData);
@@ -429,9 +449,35 @@ observe_channels (TpBaseClient *client,
   data->account = g_object_ref (account);
   data->channel = g_object_ref (channel);
 
-  empathy_keyring_get_account_password_async (account, get_password_cb, data);
+  /* GOA auth? */
+  if (empathy_goa_auth_handler_supports (self->priv->goa_handler, channel, account))
+    {
+      DEBUG ("Supported GOA account (%s), claim SASL channel",
+          tp_proxy_get_object_path (account));
+
+      tp_channel_dispatch_operation_claim_with_async (dispatch_operation,
+          client, goa_claim_cb, data);
+      tp_observe_channels_context_accept (context);
+      return;
+    }
 
-  tp_observe_channels_context_delay (context);
+  /* Password auth? */
+  if (empathy_sasl_channel_supports_mechanism (data->channel,
+          "X-TELEPATHY-PASSWORD"))
+    {
+      empathy_keyring_get_account_password_async (data->account,
+          get_password_cb, data);
+      tp_observe_channels_context_delay (context);
+      return;
+    }
+
+  /* Unknown auth */
+  error = g_error_new_literal (TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+      "Unknown auth mechanism");
+  tp_observe_channels_context_fail (context, error);
+  g_clear_error (&error);
+
+  observe_channels_data_free (data);
 }
 
 static GObject *
@@ -465,6 +511,7 @@ empathy_auth_factory_init (EmpathyAuthFactory *self)
 
   self->priv->sasl_handlers = g_hash_table_new_full (g_str_hash, g_str_equal,
       NULL, g_object_unref);
+  self->priv->goa_handler = empathy_goa_auth_handler_new ();
 }
 
 static void
@@ -526,6 +573,7 @@ empathy_auth_factory_dispose (GObject *object)
   priv->dispose_run = TRUE;
 
   g_hash_table_unref (priv->sasl_handlers);
+  g_object_unref (priv->goa_handler);
 
   G_OBJECT_CLASS (empathy_auth_factory_parent_class)->dispose (object);
 }
diff --git a/libempathy/empathy-goa-auth-handler.c b/libempathy/empathy-goa-auth-handler.c
new file mode 100644
index 0000000..a439c72
--- /dev/null
+++ b/libempathy/empathy-goa-auth-handler.c
@@ -0,0 +1,436 @@
+/*
+ * empathy-auth-goa.c - Source for Goa SASL authentication
+ * Copyright (C) 2011 Collabora Ltd.
+ * @author Xavier Claessens <xavier claessens collabora co uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "config.h"
+
+#define GOA_API_IS_SUBJECT_TO_CHANGE /* awesome! */
+#include <goa/goa.h>
+
+#include <libsoup/soup.h>
+#include <string.h>
+
+#define DEBUG_FLAG EMPATHY_DEBUG_SASL
+#include "empathy-debug.h"
+#include "empathy-utils.h"
+#include "empathy-goa-auth-handler.h"
+
+#define MECH_FACEBOOK "X-FACEBOOK-PLATFORM"
+#define MECH_MSN "X-MESSENGER-OAUTH2"
+
+static const gchar *supported_mechanisms[] = {
+    MECH_FACEBOOK,
+    MECH_MSN,
+    NULL};
+
+struct _EmpathyGoaAuthHandlerPriv
+{
+  GoaClient *client;
+  gboolean client_preparing;
+
+  /* List of AuthData waiting for client to be created */
+  GList *auth_queue;
+};
+
+G_DEFINE_TYPE (EmpathyGoaAuthHandler, empathy_goa_auth_handler, G_TYPE_OBJECT);
+
+static void
+empathy_goa_auth_handler_init (EmpathyGoaAuthHandler *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+      EMPATHY_TYPE_GOA_AUTH_HANDLER, EmpathyGoaAuthHandlerPriv);
+}
+
+static void
+empathy_goa_auth_handler_dispose (GObject *object)
+{
+  EmpathyGoaAuthHandler *self = (EmpathyGoaAuthHandler *) object;
+
+  /* AuthData keeps a ref on self */
+  g_assert (self->priv->auth_queue == NULL);
+
+  tp_clear_object (&self->priv->client);
+
+  G_OBJECT_CLASS (empathy_goa_auth_handler_parent_class)->dispose (object);
+}
+
+static void
+empathy_goa_auth_handler_class_init (EmpathyGoaAuthHandlerClass *klass)
+{
+  GObjectClass *oclass = G_OBJECT_CLASS (klass);
+
+  oclass->dispose = empathy_goa_auth_handler_dispose;
+
+  g_type_class_add_private (klass, sizeof (EmpathyGoaAuthHandlerPriv));
+}
+
+EmpathyGoaAuthHandler *
+empathy_goa_auth_handler_new (void)
+{
+  return g_object_new (EMPATHY_TYPE_GOA_AUTH_HANDLER, NULL);
+}
+
+typedef struct
+{
+  EmpathyGoaAuthHandler *self;
+  TpChannel *channel;
+  TpAccount *account;
+
+  GoaObject *goa_object;
+  gchar *access_token;
+} AuthData;
+
+static void
+auth_data_free (AuthData *data)
+{
+  tp_clear_object (&data->self);
+  tp_clear_object (&data->channel);
+  tp_clear_object (&data->account);
+  tp_clear_object (&data->goa_object);
+  g_free (data->access_token);
+  g_slice_free (AuthData, data);
+}
+
+static void
+fail_auth (AuthData *data)
+{
+  DEBUG ("Auth failed for account %s",
+      tp_proxy_get_object_path (data->account));
+
+  tp_channel_close_async (data->channel, NULL, NULL);
+  auth_data_free (data);
+}
+
+static void
+sasl_status_changed_cb (TpChannel *channel,
+    guint status,
+    const gchar *reason,
+    GHashTable *details,
+    gpointer user_data,
+    GObject *self)
+{
+  switch (status)
+    {
+      case TP_SASL_STATUS_SERVER_SUCCEEDED:
+        tp_cli_channel_interface_sasl_authentication_call_accept_sasl (channel,
+            -1, NULL, NULL, NULL, NULL);
+        break;
+
+      case TP_SASL_STATUS_SUCCEEDED:
+      case TP_SASL_STATUS_SERVER_FAILED:
+      case TP_SASL_STATUS_CLIENT_FAILED:
+        tp_cli_channel_call_close (channel, -1, NULL, NULL, NULL, NULL);
+        break;
+
+      default:
+        break;
+    }
+}
+
+static void
+facebook_new_challenge_cb (TpChannel *channel,
+    const GArray *challenge,
+    gpointer user_data,
+    GObject *weak_object)
+{
+  AuthData *data = user_data;
+  GoaOAuth2Based *oauth2;
+  const gchar *client_id;
+  GHashTable *h;
+  GHashTable *params;
+  gchar *response;
+  GArray *response_array;
+
+  DEBUG ("new challenge for %s:\n%s",
+      tp_proxy_get_object_path (data->account),
+      challenge->data);
+
+  h = soup_form_decode (challenge->data);
+
+  oauth2 = goa_object_get_oauth2_based (data->goa_object);
+  client_id = goa_oauth2_based_get_client_id (oauth2);
+
+  /* See https://developers.facebook.com/docs/chat/#platauth */
+  params = g_hash_table_new (g_str_hash, g_str_equal);
+  g_hash_table_insert (params, "method", g_hash_table_lookup (h, "method"));
+  g_hash_table_insert (params, "nonce", g_hash_table_lookup (h, "nonce"));
+  g_hash_table_insert (params, "access_token", data->access_token);
+  g_hash_table_insert (params, "api_key", (gpointer) client_id);
+  g_hash_table_insert (params, "call_id", "0");
+  g_hash_table_insert (params, "v", "1.0");
+
+  response = soup_form_encode_hash (params);
+  DEBUG ("Response: %s", response);
+
+  response_array = g_array_new (FALSE, FALSE, sizeof (gchar));
+  g_array_append_vals (response_array, response, strlen (response));
+
+  tp_cli_channel_interface_sasl_authentication_call_respond (data->channel, -1,
+      response_array, NULL, NULL, NULL, NULL);
+
+  g_hash_table_unref (h);
+  g_hash_table_unref (params);
+  g_object_unref (oauth2);
+  g_free (response);
+  g_array_unref (response_array);
+}
+
+static void
+got_oauth2_access_token_cb (GObject *source,
+    GAsyncResult *result,
+    gpointer user_data)
+{
+  GoaOAuth2Based *oauth2 = (GoaOAuth2Based *) source;
+  AuthData *data = user_data;
+  gint expires_in;
+  GError *error = NULL;
+
+  if (!goa_oauth2_based_call_get_access_token_finish (oauth2,
+      &data->access_token, &expires_in, result, &error))
+    {
+      DEBUG ("Failed to get access token: %s", error->message);
+      fail_auth (data);
+      g_clear_error (&error);
+      return;
+    }
+
+  DEBUG ("Got access token for %s:\n%s",
+      tp_proxy_get_object_path (data->account),
+      data->access_token);
+
+  tp_cli_channel_interface_sasl_authentication_connect_to_sasl_status_changed (
+      data->channel, sasl_status_changed_cb, NULL, NULL, NULL, NULL);
+  g_assert_no_error (error);
+
+  if (empathy_sasl_channel_supports_mechanism (data->channel, MECH_FACEBOOK))
+    {
+      /* Give ownership of data to signal connection */
+      tp_cli_channel_interface_sasl_authentication_connect_to_new_challenge (
+          data->channel, facebook_new_challenge_cb,
+          data, (GDestroyNotify) auth_data_free,
+          NULL, NULL);
+
+      DEBUG ("Start %s mechanism for account %s", MECH_FACEBOOK,
+          tp_proxy_get_object_path (data->account));
+
+      tp_cli_channel_interface_sasl_authentication_call_start_mechanism (
+          data->channel, -1, MECH_FACEBOOK, NULL, NULL, NULL, NULL);
+    }
+  else if (empathy_sasl_channel_supports_mechanism (data->channel, MECH_MSN))
+    {
+      guchar *token_decoded;
+      gsize token_decoded_len;
+      GArray *token_decoded_array;
+
+      /* Wocky will base64 encode, but token actually already is base64, so we
+       * decode now and it will be re-encoded. */
+      token_decoded = g_base64_decode (data->access_token, &token_decoded_len);
+      token_decoded_array = g_array_new (FALSE, FALSE, sizeof (guchar));
+      g_array_append_vals (token_decoded_array, token_decoded, token_decoded_len);
+
+      DEBUG ("Start %s mechanism for account %s", MECH_MSN,
+          tp_proxy_get_object_path (data->account));
+
+      tp_cli_channel_interface_sasl_authentication_call_start_mechanism_with_data (
+          data->channel, -1, MECH_MSN, token_decoded_array,
+          NULL, NULL, NULL, NULL);
+
+      g_array_unref (token_decoded_array);
+      g_free (token_decoded);
+      auth_data_free (data);
+    }
+  else
+    {
+      /* We already checked it supports one of supported_mechanisms, so this
+       * can't happen */
+      g_assert_not_reached ();
+    }
+}
+
+static void
+ensure_credentials_cb (GObject *source,
+    GAsyncResult *result,
+    gpointer user_data)
+{
+  AuthData *data = user_data;
+  GoaAccount *goa_account = (GoaAccount *) source;
+  GoaOAuth2Based *oauth2;
+  gint expires_in;
+  GError *error = NULL;
+
+  if (!goa_account_call_ensure_credentials_finish (goa_account, &expires_in,
+      result, &error))
+    {
+      DEBUG ("Failed to EnsureCredentials: %s", error->message);
+      fail_auth (data);
+      g_clear_error (&error);
+      return;
+    }
+
+  /* We support only oaut2 */
+  oauth2 = goa_object_get_oauth2_based (data->goa_object);
+  if (oauth2 == NULL)
+    {
+      DEBUG ("GoaObject does not implement oauth2");
+      fail_auth (data);
+      return;
+    }
+
+  DEBUG ("Goa daemon has credentials for %s, get the access token",
+      tp_proxy_get_object_path (data->account));
+
+  goa_oauth2_based_call_get_access_token (oauth2, NULL,
+      got_oauth2_access_token_cb, data);
+
+  g_object_unref (oauth2);
+}
+
+static void
+start_auth (AuthData *data)
+{
+  EmpathyGoaAuthHandler *self = data->self;
+  const GValue *id_value;
+  const gchar *id;
+  GList *goa_accounts, *l;
+  gboolean found = FALSE;
+
+  id_value = tp_account_get_storage_identifier (data->account);
+  id = g_value_get_string (id_value);
+
+  goa_accounts = goa_client_get_accounts (self->priv->client);
+  for (l = goa_accounts; l != NULL && !found; l = l->next)
+    {
+      GoaObject *goa_object = l->data;
+      GoaAccount *goa_account;
+
+      goa_account = goa_object_get_account (goa_object);
+      if (!tp_strdiff (goa_account_get_id (goa_account), id))
+        {
+          data->goa_object = g_object_ref (goa_object);
+
+          DEBUG ("Found the GoaAccount for %s, ensure credentials",
+              tp_proxy_get_object_path (data->account));
+
+          goa_account_call_ensure_credentials (goa_account, NULL,
+              ensure_credentials_cb, data);
+
+          found = TRUE;
+        }
+
+      g_object_unref (goa_account);
+    }
+  g_list_free_full (goa_accounts, g_object_unref);
+
+  if (!found)
+    {
+      DEBUG ("Cannot find GoaAccount");
+      fail_auth (data);
+    }
+}
+
+static void
+client_new_cb (GObject *source,
+    GAsyncResult *result,
+    gpointer user_data)
+{
+  EmpathyGoaAuthHandler *self = user_data;
+  GList *l;
+  GError *error = NULL;
+
+  self->priv->client_preparing = FALSE;
+  self->priv->client = goa_client_new_finish (result, &error);
+  if (self->priv->client == NULL)
+    {
+      DEBUG ("Error getting GoaClient: %s", error->message);
+      g_clear_error (&error);
+    }
+
+  /* process queued data */
+  for (l = self->priv->auth_queue; l != NULL; l = l->next)
+    {
+      AuthData *data = l->data;
+
+      if (self->priv->client != NULL)
+        start_auth (data);
+      else
+        fail_auth (data);
+    }
+
+  tp_clear_pointer (&self->priv->auth_queue, g_list_free);
+}
+
+void
+empathy_goa_auth_handler_start (EmpathyGoaAuthHandler *self,
+    TpChannel *channel,
+    TpAccount *account)
+{
+  AuthData *data;
+
+  g_return_if_fail (TP_IS_CHANNEL (channel));
+  g_return_if_fail (TP_IS_ACCOUNT (account));
+  g_return_if_fail (empathy_goa_auth_handler_supports (self, channel, account));
+
+  DEBUG ("Start Goa auth for account: %s",
+      tp_proxy_get_object_path (account));
+
+  data = g_slice_new0 (AuthData);
+  data->self = g_object_ref (self);
+  data->channel = g_object_ref (channel);
+  data->account = g_object_ref (account);
+
+  if (self->priv->client == NULL)
+    {
+      /* GOA client not ready yet, queue data */
+      if (!self->priv->client_preparing)
+        {
+          goa_client_new (NULL, client_new_cb, self);
+          self->priv->client_preparing = TRUE;
+        }
+
+      self->priv->auth_queue = g_list_prepend (self->priv->auth_queue, data);
+    }
+  else
+    {
+      start_auth (data);
+    }
+}
+
+gboolean
+empathy_goa_auth_handler_supports (EmpathyGoaAuthHandler *self,
+    TpChannel *channel,
+    TpAccount *account)
+{
+  const gchar *provider;
+  const gchar * const *iter;
+
+  g_return_val_if_fail (TP_IS_CHANNEL (channel), FALSE);
+  g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE);
+
+  provider = tp_account_get_storage_provider (account);
+  if (tp_strdiff (provider, EMPATHY_GOA_PROVIDER))
+    return FALSE;
+
+  for (iter = supported_mechanisms; *iter != NULL; iter++)
+    {
+      if (empathy_sasl_channel_supports_mechanism (channel, *iter))
+        return TRUE;
+    }
+
+  return FALSE;
+}
diff --git a/libempathy/empathy-goa-auth-handler.h b/libempathy/empathy-goa-auth-handler.h
new file mode 100644
index 0000000..5b7c087
--- /dev/null
+++ b/libempathy/empathy-goa-auth-handler.h
@@ -0,0 +1,72 @@
+/*
+ * empathy-auth-goa.h - Header for Goa SASL authentication
+ * Copyright (C) 2011 Collabora Ltd.
+ * @author Xavier Claessens <xavier claessens collabora co uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __EMPATHY_GOA_AUTH_HANDLER_H__
+#define __EMPATHY_GOA_AUTH_HANDLER_H__
+
+#include <telepathy-glib/telepathy-glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _EmpathyGoaAuthHandler EmpathyGoaAuthHandler;
+typedef struct _EmpathyGoaAuthHandlerClass EmpathyGoaAuthHandlerClass;
+typedef struct _EmpathyGoaAuthHandlerPriv EmpathyGoaAuthHandlerPriv;
+
+struct _EmpathyGoaAuthHandlerClass {
+    GObjectClass parent_class;
+};
+
+struct _EmpathyGoaAuthHandler {
+    GObject parent;
+    EmpathyGoaAuthHandlerPriv *priv;
+};
+
+GType empathy_goa_auth_handler_get_type (void);
+
+/* TYPE MACROS */
+#define EMPATHY_TYPE_GOA_AUTH_HANDLER \
+  (empathy_goa_auth_handler_get_type ())
+#define EMPATHY_GOA_AUTH_HANDLER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), EMPATHY_TYPE_GOA_AUTH_HANDLER, \
+    EmpathyGoaAuthHandler))
+#define EMPATHY_GOA_AUTH_HANDLER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), EMPATHY_TYPE_GOA_AUTH_HANDLER, \
+    EmpathyGoaAuthHandlerClass))
+#define EMPATHY_IS_GOA_AUTH_HANDLER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), EMPATHY_TYPE_GOA_AUTH_HANDLER))
+#define EMPATHY_IS_GOA_AUTH_HANDLER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), EMPATHY_TYPE_GOA_AUTH_HANDLER))
+#define EMPATHY_GOA_AUTH_HANDLER_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), EMPATHY_TYPE_GOA_AUTH_HANDLER, \
+    EmpathyGoaAuthHandlerClass))
+
+EmpathyGoaAuthHandler *empathy_goa_auth_handler_new (void);
+
+void empathy_goa_auth_handler_start (EmpathyGoaAuthHandler *self,
+    TpChannel *channel,
+    TpAccount *account);
+
+gboolean empathy_goa_auth_handler_supports (EmpathyGoaAuthHandler *self,
+    TpChannel *channel,
+    TpAccount *account);
+
+G_END_DECLS
+
+#endif /* #ifndef __EMPATHY_GOA_AUTH_HANDLER_H__*/
diff --git a/libempathy/empathy-utils.c b/libempathy/empathy-utils.c
index c4c2780..83fe4ce 100644
--- a/libempathy/empathy-utils.c
+++ b/libempathy/empathy-utils.c
@@ -1148,3 +1148,18 @@ while_finish:
   if (can_video_call != NULL)
     *can_video_call = can_video;
 }
+
+gboolean
+empathy_sasl_channel_supports_mechanism (TpChannel *channel,
+    const gchar *mechanism)
+{
+  GHashTable *props;
+  const gchar * const *available_mechanisms;
+
+  props = tp_channel_borrow_immutable_properties (channel);
+  available_mechanisms = tp_asv_get_boxed (props,
+      TP_PROP_CHANNEL_INTERFACE_SASL_AUTHENTICATION_AVAILABLE_MECHANISMS,
+      G_TYPE_STRV);
+
+  return tp_strv_contains (available_mechanisms, mechanism);
+}
diff --git a/libempathy/empathy-utils.h b/libempathy/empathy-utils.h
index 4983c89..f8d47ec 100644
--- a/libempathy/empathy-utils.h
+++ b/libempathy/empathy-utils.h
@@ -118,6 +118,9 @@ void empathy_individual_can_audio_video_call (FolksIndividual *individual,
     gboolean *can_video_call,
     EmpathyContact **out_contact);
 
+gboolean empathy_sasl_channel_supports_mechanism (TpChannel *channel,
+    const gchar *mechanism);
+
 /* Copied from wocky/wocky-utils.h */
 
 #define empathy_implement_finish_void(source, tag) \



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