diff options
author | Jonny Lamb <jonnylamb@gnome.org> | 2010-12-07 19:54:33 +0800 |
---|---|---|
committer | Jonny Lamb <jonnylamb@gnome.org> | 2010-12-07 19:54:33 +0800 |
commit | 6c5839c976407292715daf22cdddf6aca469fa75 (patch) | |
tree | 70b451c081480d4fd40bdb3ecbefce12290e1087 /libempathy | |
parent | 9d40a77fae9175d7932c3af0e95cbdf1cfde433b (diff) | |
parent | eb3f7d2fb30be62b801b6086143fb476b5368b5d (diff) | |
download | gsoc2013-empathy-6c5839c976407292715daf22cdddf6aca469fa75.tar gsoc2013-empathy-6c5839c976407292715daf22cdddf6aca469fa75.tar.gz gsoc2013-empathy-6c5839c976407292715daf22cdddf6aca469fa75.tar.bz2 gsoc2013-empathy-6c5839c976407292715daf22cdddf6aca469fa75.tar.lz gsoc2013-empathy-6c5839c976407292715daf22cdddf6aca469fa75.tar.xz gsoc2013-empathy-6c5839c976407292715daf22cdddf6aca469fa75.tar.zst gsoc2013-empathy-6c5839c976407292715daf22cdddf6aca469fa75.zip |
Merge branch 'sasl'
Diffstat (limited to 'libempathy')
-rw-r--r-- | libempathy/Makefile.am | 4 | ||||
-rw-r--r-- | libempathy/empathy-auth-factory.c | 139 | ||||
-rw-r--r-- | libempathy/empathy-debug.c | 1 | ||||
-rw-r--r-- | libempathy/empathy-debug.h | 1 | ||||
-rw-r--r-- | libempathy/empathy-keyring.c | 283 | ||||
-rw-r--r-- | libempathy/empathy-keyring.h | 50 | ||||
-rw-r--r-- | libempathy/empathy-server-sasl-handler.c | 459 | ||||
-rw-r--r-- | libempathy/empathy-server-sasl-handler.h | 82 |
8 files changed, 1007 insertions, 12 deletions
diff --git a/libempathy/Makefile.am b/libempathy/Makefile.am index 9b0dcfb92..ae9e2b5e9 100644 --- a/libempathy/Makefile.am +++ b/libempathy/Makefile.am @@ -47,8 +47,10 @@ libempathy_headers = \ empathy-irc-network-manager.h \ empathy-irc-network.h \ empathy-irc-server.h \ + empathy-keyring.h \ empathy-location.h \ empathy-message.h \ + empathy-server-sasl-handler.h \ empathy-server-tls-handler.h \ empathy-status-presets.h \ empathy-time.h \ @@ -86,7 +88,9 @@ libempathy_la_SOURCES = \ empathy-irc-network-manager.c \ empathy-irc-network.c \ empathy-irc-server.c \ + empathy-keyring.c \ empathy-message.c \ + empathy-server-sasl-handler.c \ empathy-server-tls-handler.c \ empathy-status-presets.c \ empathy-time.c \ diff --git a/libempathy/empathy-auth-factory.c b/libempathy/empathy-auth-factory.c index fa2b7dcd1..578b6d6cd 100644 --- a/libempathy/empathy-auth-factory.c +++ b/libempathy/empathy-auth-factory.c @@ -26,6 +26,7 @@ #define DEBUG_FLAG EMPATHY_DEBUG_TLS #include "empathy-debug.h" +#include "empathy-server-sasl-handler.h" #include "empathy-server-tls-handler.h" #include "empathy-utils.h" @@ -36,11 +37,17 @@ G_DEFINE_TYPE (EmpathyAuthFactory, empathy_auth_factory, G_TYPE_OBJECT); typedef struct { TpBaseClient *handler; + /* Keep a ref here so the auth client doesn't have to mess with + * refs. It will be cleared when the channel (and so the handler) + * gets invalidated. */ + EmpathyServerSASLHandler *sasl_handler; + gboolean dispose_run; } EmpathyAuthFactoryPriv; enum { NEW_SERVER_TLS_HANDLER, + NEW_SERVER_SASL_HANDLER, LAST_SIGNAL, }; @@ -111,6 +118,52 @@ server_tls_handler_ready_cb (GObject *source, } static void +sasl_handler_invalidated_cb (EmpathyServerSASLHandler *handler, + gpointer user_data) +{ + EmpathyAuthFactory *self = user_data; + EmpathyAuthFactoryPriv *priv = GET_PRIV (self); + + DEBUG ("SASL handler is invalidated, unref it"); + + tp_clear_object (&priv->sasl_handler); +} + +static void +server_sasl_handler_ready_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + EmpathyAuthFactoryPriv *priv; + GError *error = NULL; + HandlerContextData *data = user_data; + + priv = GET_PRIV (data->self); + priv->sasl_handler = empathy_server_sasl_handler_new_finish (res, &error); + + if (error != NULL) + { + DEBUG ("Failed to create a server SASL handler; error %s", + error->message); + tp_handle_channels_context_fail (data->context, error); + + g_error_free (error); + } + else + { + tp_handle_channels_context_accept (data->context); + + g_signal_connect (priv->sasl_handler, "invalidated", + G_CALLBACK (sasl_handler_invalidated_cb), data->self); + + g_signal_emit (data->self, signals[NEW_SERVER_SASL_HANDLER], 0, + priv->sasl_handler); + } + + handler_context_data_free (data); +} + +static void handle_channels_cb (TpSimpleHandler *handler, TpAccount *account, TpConnection *connection, @@ -124,18 +177,22 @@ handle_channels_cb (TpSimpleHandler *handler, const GError *dbus_error; GError *error = NULL; EmpathyAuthFactory *self = user_data; + EmpathyAuthFactoryPriv *priv = GET_PRIV (self); HandlerContextData *data; + GHashTable *props; + const gchar * const *available_mechanisms; - DEBUG ("Handle TLS carrier channels."); + DEBUG ("Handle TLS or SASL carrier channels."); - /* there can't be more than one ServerTLSConnection channels - * at the same time, for the same connection/account. + /* there can't be more than one ServerTLSConnection or + * ServerAuthentication channels at the same time, for the same + * connection/account. */ if (g_list_length (channels) != 1) { g_set_error_literal (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, - "Can't handle more than one ServerTLSConnection channel " - "for the same connection."); + "Can't handle more than one ServerTLSConnection or ServerAuthentication " + "channel for the same connection."); goto error; } @@ -143,11 +200,38 @@ handle_channels_cb (TpSimpleHandler *handler, channel = channels->data; if (tp_channel_get_channel_type_id (channel) != - EMP_IFACE_QUARK_CHANNEL_TYPE_SERVER_TLS_CONNECTION) + EMP_IFACE_QUARK_CHANNEL_TYPE_SERVER_TLS_CONNECTION + && tp_channel_get_channel_type_id (channel) != + TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION) { g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, - "Can only handle ServerTLSConnection channels, this was a %s " - "channel", tp_channel_get_channel_type (channel)); + "Can only handle ServerTLSConnection or ServerAuthentication channels, " + "this was a %s channel", tp_channel_get_channel_type (channel)); + + goto error; + } + + if (tp_channel_get_channel_type_id (channel) == + TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION + && priv->sasl_handler != NULL) + { + g_set_error_literal (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Can't handle more than one ServerAuthentication channel at one time"); + + goto error; + } + + 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"); goto error; } @@ -160,12 +244,22 @@ handle_channels_cb (TpSimpleHandler *handler, goto error; } - /* create a handler */ data = handler_context_data_new (self, context); tp_handle_channels_context_delay (context); - empathy_server_tls_handler_new_async (channel, server_tls_handler_ready_cb, - data); + /* create a handler */ + if (tp_channel_get_channel_type_id (channel) == + EMP_IFACE_QUARK_CHANNEL_TYPE_SERVER_TLS_CONNECTION) + { + empathy_server_tls_handler_new_async (channel, server_tls_handler_ready_cb, + data); + } + else if (tp_channel_get_channel_type_id (channel) == + TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION) + { + empathy_server_sasl_handler_new_async (account, channel, + server_sasl_handler_ready_cb, data); + } return; error: @@ -218,12 +312,23 @@ empathy_auth_factory_init (EmpathyAuthFactory *self) FALSE, handle_channels_cb, self, NULL); tp_base_client_take_handler_filter (priv->handler, tp_asv_new ( + /* ChannelType */ TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, EMP_IFACE_CHANNEL_TYPE_SERVER_TLS_CONNECTION, + /* AuthenticationMethod */ TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_NONE, NULL)); - g_object_unref (bus); + tp_base_client_take_handler_filter (priv->handler, tp_asv_new ( + /* ChannelType */ + TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, + TP_IFACE_CHANNEL_TYPE_SERVER_AUTHENTICATION, + /* AuthenticationMethod */ + TP_PROP_CHANNEL_TYPE_SERVER_AUTHENTICATION_AUTHENTICATION_METHOD, + G_TYPE_STRING, TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION, + NULL)); + + g_object_unref (bus); } static void @@ -237,6 +342,7 @@ empathy_auth_factory_dispose (GObject *object) priv->dispose_run = TRUE; tp_clear_object (&priv->handler); + tp_clear_object (&priv->sasl_handler); G_OBJECT_CLASS (empathy_auth_factory_parent_class)->dispose (object); } @@ -259,6 +365,15 @@ empathy_auth_factory_class_init (EmpathyAuthFactoryClass *klass) g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, EMPATHY_TYPE_SERVER_TLS_HANDLER); + + signals[NEW_SERVER_SASL_HANDLER] = + g_signal_new ("new-server-sasl-handler", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, EMPATHY_TYPE_SERVER_SASL_HANDLER); } EmpathyAuthFactory * diff --git a/libempathy/empathy-debug.c b/libempathy/empathy-debug.c index dd507bc42..4f624f8e1 100644 --- a/libempathy/empathy-debug.c +++ b/libempathy/empathy-debug.c @@ -53,6 +53,7 @@ static GDebugKey keys[] = { { "Tests", EMPATHY_DEBUG_TESTS }, { "Voip", EMPATHY_DEBUG_VOIP }, { "Tls", EMPATHY_DEBUG_TLS }, + { "Sasl", EMPATHY_DEBUG_SASL }, { 0, } }; diff --git a/libempathy/empathy-debug.h b/libempathy/empathy-debug.h index ece3af73c..44e197792 100644 --- a/libempathy/empathy-debug.h +++ b/libempathy/empathy-debug.h @@ -47,6 +47,7 @@ typedef enum EMPATHY_DEBUG_TESTS = 1 << 12, EMPATHY_DEBUG_VOIP = 1 << 13, EMPATHY_DEBUG_TLS = 1 << 14, + EMPATHY_DEBUG_SASL = 1 << 15, } EmpathyDebugFlags; gboolean empathy_debug_flag_is_set (EmpathyDebugFlags flag); diff --git a/libempathy/empathy-keyring.c b/libempathy/empathy-keyring.c new file mode 100644 index 000000000..2ce53d361 --- /dev/null +++ b/libempathy/empathy-keyring.c @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2010 Collabora Ltd. + * + * 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" + +#include "empathy-keyring.h" + +#include <string.h> + +#include <gnome-keyring.h> + +#define DEBUG_FLAG EMPATHY_DEBUG_OTHER +#include "empathy-debug.h" + +static GnomeKeyringPasswordSchema keyring_schema = + { GNOME_KEYRING_ITEM_GENERIC_SECRET, + { { "account-id", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, + { "param-name", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, + { NULL } } }; + +static void +find_items_cb (GnomeKeyringResult result, + GList *list, + gpointer user_data) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); + + if (result != GNOME_KEYRING_RESULT_OK) + { + GError *error = g_error_new_literal (TP_ERROR, + TP_ERROR_DOES_NOT_EXIST, + gnome_keyring_result_to_message (result)); + g_simple_async_result_set_from_error (simple, error); + g_clear_error (&error); + } + + if (g_list_length (list) == 1) + { + GnomeKeyringFound *found = list->data; + + DEBUG ("Got secret"); + + g_simple_async_result_set_op_res_gpointer (simple, found->secret, NULL); + } + + g_simple_async_result_complete (simple); + g_object_unref (simple); +} + +void +empathy_keyring_get_password_async (TpAccount *account, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + GnomeKeyringAttributeList *match; + const gchar *account_id; + + g_return_if_fail (TP_IS_ACCOUNT (account)); + g_return_if_fail (callback != NULL); + + simple = g_simple_async_result_new (G_OBJECT (account), callback, + user_data, empathy_keyring_get_password_async); + + account_id = tp_proxy_get_object_path (account) + + strlen (TP_ACCOUNT_OBJECT_PATH_BASE); + + DEBUG ("Trying to get password for: %s", account_id); + + match = gnome_keyring_attribute_list_new (); + gnome_keyring_attribute_list_append_string (match, "account-id", + account_id); + gnome_keyring_attribute_list_append_string (match, "param-name", "password"); + + gnome_keyring_find_items (GNOME_KEYRING_ITEM_GENERIC_SECRET, + match, find_items_cb, simple, NULL); + + gnome_keyring_attribute_list_free (match); +} + +const gchar * +empathy_keyring_get_password_finish (TpAccount *account, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL); + g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return NULL; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, + G_OBJECT (account), empathy_keyring_get_password_async), NULL); + + return g_simple_async_result_get_op_res_gpointer (simple); +} + +static void +store_password_cb (GnomeKeyringResult result, + gpointer user_data) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); + + if (result != GNOME_KEYRING_RESULT_OK) + { + GError *error = g_error_new_literal (TP_ERROR, + TP_ERROR_DOES_NOT_EXIST, + gnome_keyring_result_to_message (result)); + g_simple_async_result_set_from_error (simple, error); + g_clear_error (&error); + } + + g_simple_async_result_complete (simple); + g_object_unref (simple); +} + +void +empathy_keyring_set_password_async (TpAccount *account, + const gchar *password, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + const gchar *account_id; + gchar *name; + + g_return_if_fail (TP_IS_ACCOUNT (account)); + g_return_if_fail (password != NULL); + + simple = g_simple_async_result_new (G_OBJECT (account), callback, + user_data, empathy_keyring_set_password_async); + + account_id = tp_proxy_get_object_path (account) + + strlen (TP_ACCOUNT_OBJECT_PATH_BASE); + + DEBUG ("Remembering password for %s", account_id); + + name = g_strdup_printf ("account: %s; param: password", account_id); + + gnome_keyring_store_password (&keyring_schema, NULL, name, password, + store_password_cb, simple, NULL, + "account-id", account_id, + "param-name", "password", + NULL); + + g_free (name); +} + +gboolean +empathy_keyring_set_password_finish (TpAccount *account, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE); + g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, + G_OBJECT (account), empathy_keyring_set_password_async), FALSE); + + return TRUE; +} + +static void +item_delete_cb (GnomeKeyringResult result, + gpointer user_data) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); + + if (result != GNOME_KEYRING_RESULT_OK) + { + GError *error = g_error_new_literal (TP_ERROR, + TP_ERROR_DOES_NOT_EXIST, + gnome_keyring_result_to_message (result)); + g_simple_async_result_set_from_error (simple, error); + g_clear_error (&error); + } + + g_simple_async_result_complete (simple); + g_object_unref (simple); +} + +static void +find_item_to_delete_cb (GnomeKeyringResult result, + GList *list, + gpointer user_data) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); + GnomeKeyringFound *found; + + if (result != GNOME_KEYRING_RESULT_OK || g_list_length (list) != 1) + { + GError *error = g_error_new_literal (TP_ERROR, + TP_ERROR_DOES_NOT_EXIST, + gnome_keyring_result_to_message (result)); + g_simple_async_result_set_from_error (simple, error); + g_clear_error (&error); + + g_simple_async_result_complete (simple); + g_object_unref (simple); + return; + } + + found = list->data; + + gnome_keyring_item_delete (NULL, found->item_id, item_delete_cb, + simple, NULL); +} + +void +empathy_keyring_delete_password_async (TpAccount *account, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + GnomeKeyringAttributeList *match; + const gchar *account_id; + + g_return_if_fail (TP_IS_ACCOUNT (account)); + + simple = g_simple_async_result_new (G_OBJECT (account), callback, + user_data, empathy_keyring_delete_password_async); + + account_id = tp_proxy_get_object_path (account) + + strlen (TP_ACCOUNT_OBJECT_PATH_BASE); + + match = gnome_keyring_attribute_list_new (); + gnome_keyring_attribute_list_append_string (match, "account-id", + account_id); + gnome_keyring_attribute_list_append_string (match, "param-name", "password"); + + gnome_keyring_find_items (GNOME_KEYRING_ITEM_GENERIC_SECRET, + match, find_item_to_delete_cb, simple, NULL); + + gnome_keyring_attribute_list_free (match); +} + +gboolean +empathy_keyring_delete_password_finish (TpAccount *account, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE); + g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, + G_OBJECT (account), empathy_keyring_delete_password_async), FALSE); + + return TRUE; +} + diff --git a/libempathy/empathy-keyring.h b/libempathy/empathy-keyring.h new file mode 100644 index 000000000..000f987f7 --- /dev/null +++ b/libempathy/empathy-keyring.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010 Collabora Ltd. + * + * 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_KEYRING_H__ +#define __EMPATHY_KEYRING_H__ + +#include <gio/gio.h> + +#include <telepathy-glib/account.h> + +G_BEGIN_DECLS + +void empathy_keyring_get_password_async (TpAccount *account, + GAsyncReadyCallback callback, gpointer user_data); + +const gchar * empathy_keyring_get_password_finish (TpAccount *account, + GAsyncResult *result, GError **error); + +void empathy_keyring_set_password_async (TpAccount *account, + const gchar *password, GAsyncReadyCallback callback, + gpointer user_data); + +gboolean empathy_keyring_set_password_finish (TpAccount *account, + GAsyncResult *result, GError **error); + +void empathy_keyring_delete_password_async (TpAccount *account, + GAsyncReadyCallback callback, gpointer user_data); + +gboolean empathy_keyring_delete_password_finish (TpAccount *account, + GAsyncResult *result, GError **error); + +G_END_DECLS + +#endif /* __EMPATHY_KEYRING_H__ */ + diff --git a/libempathy/empathy-server-sasl-handler.c b/libempathy/empathy-server-sasl-handler.c new file mode 100644 index 000000000..749311d4b --- /dev/null +++ b/libempathy/empathy-server-sasl-handler.c @@ -0,0 +1,459 @@ +/* + * empathy-server-sasl-handler.c - Source for EmpathyServerSASLHandler + * Copyright (C) 2010 Collabora Ltd. + * + * 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 "empathy-server-sasl-handler.h" + +#include <telepathy-glib/util.h> + +#include <string.h> + +#define DEBUG_FLAG EMPATHY_DEBUG_SASL +#include "empathy-debug.h" +#include "empathy-keyring.h" + +enum { + PROP_CHANNEL = 1, + PROP_ACCOUNT, + LAST_PROPERTY, +}; + +/* signal enum */ +enum { + INVALIDATED, + LAST_SIGNAL, +}; + +static guint signals[LAST_SIGNAL] = {0}; + +typedef struct { + TpChannel *channel; + TpAccount *account; + + GSimpleAsyncResult *result; + + gchar *password; + + GSimpleAsyncResult *async_init_res; +} EmpathyServerSASLHandlerPriv; + +static void async_initable_iface_init (GAsyncInitableIface *iface); + +G_DEFINE_TYPE_WITH_CODE (EmpathyServerSASLHandler, empathy_server_sasl_handler, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)); + +static const gchar *sasl_statuses[] = { + "not started", + "in progress", + "server succeeded", + "client accepted", + "succeeded", + "server failed", + "client failed", +}; + +static void +sasl_status_changed_cb (TpChannel *channel, + TpSASLStatus status, + const gchar *error, + GHashTable *details, + gpointer user_data, + GObject *weak_object) +{ + EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (weak_object)->priv; + + /* buh boh */ + if (status >= G_N_ELEMENTS (sasl_statuses)) + { + DEBUG ("SASL status changed to unknown status"); + return; + } + + DEBUG ("SASL status changed to '%s'", sasl_statuses[status]); + + if (status == TP_SASL_STATUS_SERVER_SUCCEEDED) + { + tp_cli_channel_interface_sasl_authentication_call_accept_sasl ( + priv->channel, -1, NULL, NULL, NULL, NULL); + + tp_cli_channel_call_close (priv->channel, -1, + NULL, NULL, NULL, NULL); + } +} + +static gboolean +empathy_server_sasl_handler_give_password (gpointer data) +{ + EmpathyServerSASLHandler *self = data; + EmpathyServerSASLHandlerPriv *priv = self->priv; + + empathy_server_sasl_handler_provide_password (self, + priv->password, FALSE); + + return FALSE; +} + +static void +empathy_server_sasl_handler_get_password_async_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + EmpathyServerSASLHandlerPriv *priv; + const gchar *password; + GError *error = NULL; + + priv = EMPATHY_SERVER_SASL_HANDLER (user_data)->priv; + + password = empathy_keyring_get_password_finish (TP_ACCOUNT (source), + result, &error); + + if (password != NULL) + { + priv->password = g_strdup (password); + + /* Do this in an idle so the async result will get there + * first. */ + g_idle_add (empathy_server_sasl_handler_give_password, user_data); + } + + g_simple_async_result_complete (priv->async_init_res); + tp_clear_object (&priv->async_init_res); +} + +static void +empathy_server_sasl_handler_init_async (GAsyncInitable *initable, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + EmpathyServerSASLHandler *self = EMPATHY_SERVER_SASL_HANDLER (initable); + EmpathyServerSASLHandlerPriv *priv = self->priv; + + g_assert (priv->account != NULL); + + priv->async_init_res = g_simple_async_result_new (G_OBJECT (self), + callback, user_data, empathy_server_sasl_handler_new_async); + + empathy_keyring_get_password_async (priv->account, + empathy_server_sasl_handler_get_password_async_cb, self); +} + +static gboolean +empathy_server_sasl_handler_init_finish (GAsyncInitable *initable, + GAsyncResult *res, + GError **error) +{ + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), + error)) + return FALSE; + + return TRUE; +} + +static void +async_initable_iface_init (GAsyncInitableIface *iface) +{ + iface->init_async = empathy_server_sasl_handler_init_async; + iface->init_finish = empathy_server_sasl_handler_init_finish; +} + +static void +channel_invalidated_cb (TpProxy *proxy, + guint domain, + gint code, + gchar *message, + EmpathyServerSASLHandler *self) +{ + g_signal_emit (self, signals[INVALIDATED], 0); +} + +static void +empathy_server_sasl_handler_constructed (GObject *object) +{ + EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv; + GError *error = NULL; + + tp_cli_channel_interface_sasl_authentication_connect_to_sasl_status_changed ( + priv->channel, sasl_status_changed_cb, NULL, NULL, object, &error); + + if (error != NULL) + { + DEBUG ("Failed to connect to SASLStatusChanged: %s", error->message); + g_clear_error (&error); + } + + tp_g_signal_connect_object (priv->channel, "invalidated", + G_CALLBACK (channel_invalidated_cb), object, 0); +} + +static void +empathy_server_sasl_handler_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv; + + switch (property_id) + { + case PROP_CHANNEL: + g_value_set_object (value, priv->channel); + break; + case PROP_ACCOUNT: + g_value_set_object (value, priv->account); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +empathy_server_sasl_handler_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv; + + switch (property_id) + { + case PROP_CHANNEL: + priv->channel = g_value_dup_object (value); + break; + case PROP_ACCOUNT: + priv->account = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +empathy_server_sasl_handler_dispose (GObject *object) +{ + EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv; + + DEBUG ("%p", object); + + tp_clear_object (&priv->channel); + tp_clear_object (&priv->account); + + G_OBJECT_CLASS (empathy_server_sasl_handler_parent_class)->dispose (object); +} + +static void +empathy_server_sasl_handler_finalize (GObject *object) +{ + EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv; + + DEBUG ("%p", object); + + tp_clear_pointer (&priv->password, g_free); + + G_OBJECT_CLASS (empathy_server_sasl_handler_parent_class)->finalize (object); +} + +static void +empathy_server_sasl_handler_class_init (EmpathyServerSASLHandlerClass *klass) +{ + GObjectClass *oclass = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + oclass->constructed = empathy_server_sasl_handler_constructed; + oclass->get_property = empathy_server_sasl_handler_get_property; + oclass->set_property = empathy_server_sasl_handler_set_property; + oclass->dispose = empathy_server_sasl_handler_dispose; + oclass->dispose = empathy_server_sasl_handler_finalize; + + g_type_class_add_private (klass, sizeof (EmpathyServerSASLHandlerPriv)); + + pspec = g_param_spec_object ("channel", "The TpChannel", + "The TpChannel this handler is supposed to handle.", + TP_TYPE_CHANNEL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_CHANNEL, pspec); + + pspec = g_param_spec_object ("account", "The TpAccount", + "The TpAccount this channel belongs to.", + TP_TYPE_ACCOUNT, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_ACCOUNT, pspec); + + signals[INVALIDATED] = g_signal_new ("invalidated", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static void +empathy_server_sasl_handler_init (EmpathyServerSASLHandler *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + EMPATHY_TYPE_SERVER_SASL_HANDLER, EmpathyServerSASLHandlerPriv); +} + +EmpathyServerSASLHandler * +empathy_server_sasl_handler_new_finish (GAsyncResult *result, + GError **error) +{ + GObject *object, *source_object; + + source_object = g_async_result_get_source_object (result); + + object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), + result, error); + g_object_unref (source_object); + + if (object != NULL) + return EMPATHY_SERVER_SASL_HANDLER (object); + else + return NULL; +} + +void +empathy_server_sasl_handler_new_async (TpAccount *account, + TpChannel *channel, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail (TP_IS_ACCOUNT (account)); + g_return_if_fail (TP_IS_CHANNEL (channel)); + g_return_if_fail (callback != NULL); + + g_async_initable_new_async (EMPATHY_TYPE_SERVER_SASL_HANDLER, + G_PRIORITY_DEFAULT, NULL, callback, user_data, + "account", account, + "channel", channel, + NULL); +} + +static void +start_mechanism_with_data_cb (TpChannel *proxy, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + if (error != NULL) + { + DEBUG ("Failed to start mechanism: %s", error->message); + return; + } + + DEBUG ("Started mechanism successfully"); +} + +static void +empathy_server_sasl_handler_set_password_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GError *error = NULL; + + if (!empathy_keyring_set_password_finish (TP_ACCOUNT (source), result, + &error)) + { + DEBUG ("Failed to set password: %s", error->message); + g_clear_error (&error); + } + else + { + DEBUG ("Password set successfully."); + } +} + +void +empathy_server_sasl_handler_provide_password ( + EmpathyServerSASLHandler *handler, + const gchar *password, + gboolean remember) +{ + EmpathyServerSASLHandlerPriv *priv; + GArray *array; + + g_return_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler)); + + priv = handler->priv; + + array = g_array_sized_new (TRUE, FALSE, + sizeof (gchar), strlen (password)); + + g_array_append_vals (array, password, strlen (password)); + + DEBUG ("Calling StartMechanismWithData with our password"); + + tp_cli_channel_interface_sasl_authentication_call_start_mechanism_with_data ( + priv->channel, -1, "X-TELEPATHY-PASSWORD", array, + start_mechanism_with_data_cb, NULL, NULL, G_OBJECT (handler)); + + g_array_unref (array); + + DEBUG ("%sremembering the password", remember ? "" : "not "); + + if (remember) + { + empathy_keyring_set_password_async (priv->account, password, + empathy_server_sasl_handler_set_password_cb, NULL); + } +} + +void +empathy_server_sasl_handler_cancel (EmpathyServerSASLHandler *handler) +{ + EmpathyServerSASLHandlerPriv *priv; + + g_return_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler)); + + priv = handler->priv; + + DEBUG ("Cancelling SASL mechanism..."); + + tp_cli_channel_interface_sasl_authentication_call_abort_sasl ( + priv->channel, -1, TP_SASL_ABORT_REASON_USER_ABORT, + "User cancelled the authentication", + NULL, NULL, NULL, NULL); +} + +TpAccount * +empathy_server_sasl_handler_get_account (EmpathyServerSASLHandler *handler) +{ + EmpathyServerSASLHandlerPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler), NULL); + + priv = handler->priv; + + return priv->account; +} + +gboolean +empathy_server_sasl_handler_has_password (EmpathyServerSASLHandler *handler) +{ + EmpathyServerSASLHandlerPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler), FALSE); + + priv = handler->priv; + + return (priv->password != NULL); +} diff --git a/libempathy/empathy-server-sasl-handler.h b/libempathy/empathy-server-sasl-handler.h new file mode 100644 index 000000000..1eedc5b27 --- /dev/null +++ b/libempathy/empathy-server-sasl-handler.h @@ -0,0 +1,82 @@ +/* + * empathy-server-sasl-handler.h - Header for EmpathyServerSASLHandler + * Copyright (C) 2010 Collabora Ltd. + * + * 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_SERVER_SASL_HANDLER_H__ +#define __EMPATHY_SERVER_SASL_HANDLER_H__ + +#include <glib-object.h> +#include <gio/gio.h> + +#include <telepathy-glib/account.h> +#include <telepathy-glib/channel.h> + +G_BEGIN_DECLS + +typedef struct _EmpathyServerSASLHandler EmpathyServerSASLHandler; +typedef struct _EmpathyServerSASLHandlerClass EmpathyServerSASLHandlerClass; + +struct _EmpathyServerSASLHandlerClass { + GObjectClass parent_class; +}; + +struct _EmpathyServerSASLHandler { + GObject parent; + gpointer priv; +}; + +GType empathy_server_sasl_handler_get_type (void); + +#define EMPATHY_TYPE_SERVER_SASL_HANDLER \ + (empathy_server_sasl_handler_get_type ()) +#define EMPATHY_SERVER_SASL_HANDLER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), EMPATHY_TYPE_SERVER_SASL_HANDLER, \ + EmpathyServerSASLHandler)) +#define EMPATHY_SERVER_SASL_HANDLER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), EMPATHY_TYPE_SERVER_SASL_HANDLER, \ + EmpathyServerSASLHandlerClass)) +#define EMPATHY_IS_SERVER_SASL_HANDLER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), EMPATHY_TYPE_SERVER_SASL_HANDLER)) +#define EMPATHY_IS_SERVER_SASL_HANDLER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), EMPATHY_TYPE_SERVER_SASL_HANDLER)) +#define EMPATHY_SERVER_SASL_HANDLER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), EMPATHY_TYPE_SERVER_SASL_HANDLER, \ + EmpathyServerSASLHandlerClass)) + +void empathy_server_sasl_handler_new_async ( + TpAccount *account, TpChannel *channel, + GAsyncReadyCallback callback, gpointer user_data); + +EmpathyServerSASLHandler * empathy_server_sasl_handler_new_finish ( + GAsyncResult *result, GError **error); + +void empathy_server_sasl_handler_provide_password ( + EmpathyServerSASLHandler *handler, const gchar *password, + gboolean remember); + +void empathy_server_sasl_handler_cancel (EmpathyServerSASLHandler *handler); + +TpAccount * empathy_server_sasl_handler_get_account ( + EmpathyServerSASLHandler *handler); + +gboolean empathy_server_sasl_handler_has_password ( + EmpathyServerSASLHandler *handler); + +G_END_DECLS + +#endif /* #ifndef __EMPATHY_SERVER_SASL_HANDLER_H__*/ |