aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXavier Claessens <xclaesse@gmail.com>2011-10-27 18:09:59 +0800
committerXavier Claessens <xclaesse@gmail.com>2011-11-04 17:10:19 +0800
commitd3e575fd305487eb8a15a66941651325904ba90b (patch)
tree2aa27a2f1142ba3f9a34bc04263172ccc4494a39
parentbaeff8af237dd7c4a18d578ed71b3ee87ceb278d (diff)
downloadgsoc2013-empathy-d3e575fd305487eb8a15a66941651325904ba90b.tar
gsoc2013-empathy-d3e575fd305487eb8a15a66941651325904ba90b.tar.gz
gsoc2013-empathy-d3e575fd305487eb8a15a66941651325904ba90b.tar.bz2
gsoc2013-empathy-d3e575fd305487eb8a15a66941651325904ba90b.tar.lz
gsoc2013-empathy-d3e575fd305487eb8a15a66941651325904ba90b.tar.xz
gsoc2013-empathy-d3e575fd305487eb8a15a66941651325904ba90b.tar.zst
gsoc2013-empathy-d3e575fd305487eb8a15a66941651325904ba90b.zip
Import Facebook and windows Live GOA accounts
Implement their auth mechanisms Fixes bug #661068 and #652544
-rw-r--r--configure.ac6
-rw-r--r--goa-mc-plugin/mcp-account-manager-goa.c28
-rw-r--r--libempathy/Makefile.am2
-rw-r--r--libempathy/empathy-auth-factory.c98
-rw-r--r--libempathy/empathy-goa-auth-handler.c436
-rw-r--r--libempathy/empathy-goa-auth-handler.h72
-rw-r--r--libempathy/empathy-utils.c15
-rw-r--r--libempathy/empathy-utils.h3
8 files changed, 631 insertions, 29 deletions
diff --git a/configure.ac b/configure.ac
index a8f7e4e70..1c0853cd3 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 d81c60816..1185d7ba3 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 079b4736b..0662f2cf0 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 663dce050..03f2cd16d 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 000000000..a439c72c2
--- /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 000000000..5b7c08774
--- /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 c4c2780ac..83fe4ce7e 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 4983c89a6..f8d47ec8a 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) \