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 | |
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'
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | data/Empathy.Auth.client | 4 | ||||
-rw-r--r-- | libempathy-gtk/Makefile.am | 2 | ||||
-rw-r--r-- | libempathy-gtk/empathy-password-dialog.c | 325 | ||||
-rw-r--r-- | libempathy-gtk/empathy-password-dialog.h | 66 | ||||
-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 | ||||
-rw-r--r-- | po/POTFILES.in | 1 | ||||
-rw-r--r-- | src/empathy-auth-client.c | 27 |
15 files changed, 1431 insertions, 15 deletions
diff --git a/configure.ac b/configure.ac index 20de382b9..4ed8d9e24 100644 --- a/configure.ac +++ b/configure.ac @@ -39,7 +39,7 @@ KEYRING_REQUIRED=2.26.0 LIBCANBERRA_GTK_REQUIRED=0.25 LIBNOTIFY_REQUIRED=0.7.0 TELEPATHY_FARSIGHT_REQUIRED=0.0.14 -TELEPATHY_GLIB_REQUIRED=0.13.1.1 +TELEPATHY_GLIB_REQUIRED=0.13.7 TELEPATHY_LOGGER=0.1.5 # Optional deps diff --git a/data/Empathy.Auth.client b/data/Empathy.Auth.client index 4c6adc0de..1fbf10368 100644 --- a/data/Empathy.Auth.client +++ b/data/Empathy.Auth.client @@ -4,3 +4,7 @@ Interfaces=org.freedesktop.Telepathy.Client.Handler [org.freedesktop.Telepathy.Client.Handler.HandlerChannelFilter 0] org.freedesktop.Telepathy.Channel.ChannelType s=org.freedesktop.Telepathy.Channel.Type.ServerTLSConnection org.freedesktop.Telepathy.Channel.TargetHandleType u=0 + +[org.freedesktop.Telepathy.Client.Handler.HandlerChannelFilter 1] +org.freedesktop.Telepathy.Channel.ChannelType s=org.freedesktop.Telepathy.Channel.Type.ServerAuthentication +org.freedesktop.Telepathy.Channel.Type.ServerAuthentication s=org.freedesktop.Telepathy.Channel.Interface.SASLAuthentication diff --git a/libempathy-gtk/Makefile.am b/libempathy-gtk/Makefile.am index 7e35929b1..72e489c75 100644 --- a/libempathy-gtk/Makefile.am +++ b/libempathy-gtk/Makefile.am @@ -68,6 +68,7 @@ libempathy_gtk_handwritten_source = \ empathy-new-message-dialog.c \ empathy-new-call-dialog.c \ empathy-notify-manager.c \ + empathy-password-dialog.c \ empathy-persona-store.c \ empathy-persona-view.c \ empathy-presence-chooser.c \ @@ -130,6 +131,7 @@ libempathy_gtk_headers = \ empathy-new-message-dialog.h \ empathy-new-call-dialog.h \ empathy-notify-manager.h \ + empathy-password-dialog.h \ empathy-persona-store.h \ empathy-persona-view.h \ empathy-presence-chooser.h \ diff --git a/libempathy-gtk/empathy-password-dialog.c b/libempathy-gtk/empathy-password-dialog.c new file mode 100644 index 000000000..3c514ee82 --- /dev/null +++ b/libempathy-gtk/empathy-password-dialog.c @@ -0,0 +1,325 @@ +/* + * empathy-password-dialog.c - Source for EmpathyPasswordDialog + * 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-password-dialog.h" + +#include <glib/gi18n-lib.h> + +#define DEBUG_FLAG EMPATHY_DEBUG_SASL +#include <libempathy/empathy-debug.h> +#include <libempathy/empathy-utils.h> + +G_DEFINE_TYPE (EmpathyPasswordDialog, empathy_password_dialog, + GTK_TYPE_MESSAGE_DIALOG) + +enum { + PROP_HANDLER = 1, + + LAST_PROPERTY, +}; + +typedef struct { + EmpathyServerSASLHandler *handler; + + GtkWidget *entry; + GtkWidget *ticky; + + gboolean grabbing; + + gboolean dispose_run; +} EmpathyPasswordDialogPriv; + +static void +empathy_password_dialog_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + EmpathyPasswordDialogPriv *priv = EMPATHY_PASSWORD_DIALOG (object)->priv; + + switch (property_id) + { + case PROP_HANDLER: + g_value_set_object (value, priv->handler); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +empathy_password_dialog_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + EmpathyPasswordDialogPriv *priv = EMPATHY_PASSWORD_DIALOG (object)->priv; + + switch (property_id) + { + case PROP_HANDLER: + g_assert (priv->handler == NULL); /* construct only */ + priv->handler = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +empathy_password_dialog_dispose (GObject *object) +{ + EmpathyPasswordDialogPriv *priv = EMPATHY_PASSWORD_DIALOG (object)->priv; + + if (priv->dispose_run) + return; + + priv->dispose_run = TRUE; + + tp_clear_object (&priv->handler); + + G_OBJECT_CLASS (empathy_password_dialog_parent_class)->dispose (object); +} + +static void +password_dialog_response_cb (GtkDialog *dialog, + gint response, + gpointer user_data) +{ + EmpathyPasswordDialogPriv *priv = EMPATHY_PASSWORD_DIALOG (user_data)->priv; + + if (response == GTK_RESPONSE_OK) + { + empathy_server_sasl_handler_provide_password (priv->handler, + gtk_entry_get_text (GTK_ENTRY (priv->entry)), + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->ticky))); + } + else + { + empathy_server_sasl_handler_cancel (priv->handler); + } + + gtk_widget_destroy (GTK_WIDGET (dialog)); +} + +static void +clear_icon_released_cb (GtkEntry *entry, + GtkEntryIconPosition icon_pos, + GdkEvent *event, + gpointer user_data) +{ + gtk_entry_set_text (entry, ""); +} + +static void +password_entry_changed_cb (GtkEditable *entry, + gpointer user_data) +{ + const gchar *str; + + str = gtk_entry_get_text (GTK_ENTRY (entry)); + + gtk_entry_set_icon_sensitive (GTK_ENTRY (entry), + GTK_ENTRY_ICON_SECONDARY, !EMP_STR_EMPTY (str)); +} + +static gboolean +password_dialog_grab_keyboard (GtkWidget *widget, + GdkEvent *event, + gpointer user_data) +{ + EmpathyPasswordDialogPriv *priv = EMPATHY_PASSWORD_DIALOG (user_data)->priv; + + if (!priv->grabbing) + { + GdkGrabStatus status = gdk_keyboard_grab (gtk_widget_get_window (widget), + FALSE, gdk_event_get_time (event)); + + if (status != GDK_GRAB_SUCCESS) + DEBUG ("Could not grab keyboard; grab status was %u", status); + else + priv->grabbing = TRUE; + } + + return FALSE; +} + +static gboolean +password_dialog_ungrab_keyboard (GtkWidget *widget, + GdkEvent *event, + gpointer user_data) +{ + EmpathyPasswordDialogPriv *priv = EMPATHY_PASSWORD_DIALOG (user_data)->priv; + + if (priv->grabbing) + { + gdk_keyboard_ungrab (gdk_event_get_time (event)); + priv->grabbing = FALSE; + } + + return FALSE; +} + +static gboolean +password_dialog_window_state_changed (GtkWidget *widget, + GdkEventWindowState *event, + gpointer data) +{ + GdkWindowState state = gdk_window_get_state (gtk_widget_get_window (widget)); + + if (state & GDK_WINDOW_STATE_WITHDRAWN + || state & GDK_WINDOW_STATE_ICONIFIED + || state & GDK_WINDOW_STATE_FULLSCREEN + || state & GDK_WINDOW_STATE_MAXIMIZED) + { + password_dialog_ungrab_keyboard (widget, (GdkEvent *) event, data); + } + else + { + password_dialog_grab_keyboard (widget, (GdkEvent *) event, data); + } + + return FALSE; +} + +static void +empathy_password_dialog_constructed (GObject *object) +{ + EmpathyPasswordDialog *dialog; + EmpathyPasswordDialogPriv *priv; + TpAccount *account; + GtkWidget *icon; + GtkBox *box; + gchar *text; + + dialog = EMPATHY_PASSWORD_DIALOG (object); + priv = dialog->priv; + + g_assert (priv->handler != NULL); + + priv->grabbing = FALSE; + + account = empathy_server_sasl_handler_get_account (priv->handler); + + /* dialog */ + gtk_dialog_add_buttons (GTK_DIALOG (dialog), + GTK_STOCK_OK, GTK_RESPONSE_OK, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + NULL); + + text = g_strdup_printf (_("Enter your password for account\n<b>%s</b>"), + tp_account_get_display_name (account)); + gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), text); + g_free (text); + + gtk_window_set_icon_name (GTK_WINDOW (dialog), + GTK_STOCK_DIALOG_AUTHENTICATION); + + box = GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))); + + /* dialog icon */ + icon = gtk_image_new_from_icon_name (tp_account_get_icon_name (account), + GTK_ICON_SIZE_DIALOG); + gtk_message_dialog_set_image (GTK_MESSAGE_DIALOG (dialog), icon); + gtk_widget_show (icon); + + /* entry */ + priv->entry = gtk_entry_new (); + gtk_entry_set_visibility (GTK_ENTRY (priv->entry), FALSE); + + /* entry clear icon */ + gtk_entry_set_icon_from_stock (GTK_ENTRY (priv->entry), + GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR); + gtk_entry_set_icon_sensitive (GTK_ENTRY (priv->entry), + GTK_ENTRY_ICON_SECONDARY, FALSE); + + g_signal_connect (priv->entry, "icon-release", + G_CALLBACK (clear_icon_released_cb), NULL); + g_signal_connect (priv->entry, "changed", + G_CALLBACK (password_entry_changed_cb), NULL); + + gtk_box_pack_start (box, priv->entry, FALSE, FALSE, 0); + gtk_widget_show (priv->entry); + + /* remember password ticky box */ + priv->ticky = gtk_check_button_new_with_label (_("Remember password")); + + /* Don't add this to the dialog yet because we haven't set up + * everything in the UI properly yet and the MC transition isn't + * ready etc. so we'll just force it to never remember a + * password. */ + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->ticky), FALSE); + /* + gtk_box_pack_start (box, priv->ticky, FALSE, FALSE, 0); + gtk_widget_show (priv->ticky); + */ + + g_signal_connect (dialog, "response", + G_CALLBACK (password_dialog_response_cb), dialog); + g_signal_connect (dialog, "window-state-event", + G_CALLBACK (password_dialog_window_state_changed), dialog); + g_signal_connect (dialog, "map-event", + G_CALLBACK (password_dialog_grab_keyboard), dialog); + g_signal_connect (dialog, "unmap-event", + G_CALLBACK (password_dialog_ungrab_keyboard), dialog); + + gtk_widget_grab_focus (priv->entry); + + gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ALWAYS); +} + +static void +empathy_password_dialog_init (EmpathyPasswordDialog *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + EMPATHY_TYPE_PASSWORD_DIALOG, EmpathyPasswordDialogPriv); +} + +static void +empathy_password_dialog_class_init (EmpathyPasswordDialogClass *klass) +{ + GParamSpec *pspec; + GObjectClass *oclass = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (EmpathyPasswordDialogPriv)); + + oclass->set_property = empathy_password_dialog_set_property; + oclass->get_property = empathy_password_dialog_get_property; + oclass->dispose = empathy_password_dialog_dispose; + oclass->constructed = empathy_password_dialog_constructed; + + pspec = g_param_spec_object ("handler", "The EmpathyServerSASLHandler", + "The EmpathyServerSASLHandler to be used.", + EMPATHY_TYPE_SERVER_SASL_HANDLER, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_HANDLER, pspec); +} + +GtkWidget * +empathy_password_dialog_new (EmpathyServerSASLHandler *handler) +{ + g_assert (EMPATHY_IS_SERVER_SASL_HANDLER (handler)); + + return g_object_new (EMPATHY_TYPE_PASSWORD_DIALOG, + "handler", handler, NULL); +} diff --git a/libempathy-gtk/empathy-password-dialog.h b/libempathy-gtk/empathy-password-dialog.h new file mode 100644 index 000000000..566322de1 --- /dev/null +++ b/libempathy-gtk/empathy-password-dialog.h @@ -0,0 +1,66 @@ +/* + * empathy-password-dialog.h - Header for EmpathyPasswordDialog + * 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_PASSWORD_DIALOG_H__ +#define __EMPATHY_PASSWORD_DIALOG_H__ + +#include <glib-object.h> +#include <gtk/gtk.h> + +#include <libempathy/empathy-server-sasl-handler.h> + +#include <extensions/extensions.h> + +G_BEGIN_DECLS + +typedef struct _EmpathyPasswordDialog EmpathyPasswordDialog; +typedef struct _EmpathyPasswordDialogClass EmpathyPasswordDialogClass; + +struct _EmpathyPasswordDialogClass { + GtkMessageDialogClass parent_class; +}; + +struct _EmpathyPasswordDialog { + GtkMessageDialog parent; + gpointer priv; +}; + +GType empathy_password_dialog_get_type (void); + +#define EMPATHY_TYPE_PASSWORD_DIALOG \ + (empathy_password_dialog_get_type ()) +#define EMPATHY_PASSWORD_DIALOG(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), EMPATHY_TYPE_PASSWORD_DIALOG, \ + EmpathyPasswordDialog)) +#define EMPATHY_PASSWORD_DIALOG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), EMPATHY_TYPE_PASSWORD_DIALOG, \ + EmpathyPasswordDialogClass)) +#define EMPATHY_IS_PASSWORD_DIALOG(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), EMPATHY_TYPE_PASSWORD_DIALOG)) +#define EMPATHY_IS_PASSWORD_DIALOG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), EMPATHY_TYPE_PASSWORD_DIALOG)) +#define EMPATHY_PASSWORD_DIALOG_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), EMPATHY_TYPE_PASSWORD_DIALOG, \ + EmpathyPasswordDialogClass)) + +GtkWidget * empathy_password_dialog_new (EmpathyServerSASLHandler *handler); + +G_END_DECLS + +#endif /* #ifndef __EMPATHY_PASSWORD_DIALOG_H__*/ 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__*/ diff --git a/po/POTFILES.in b/po/POTFILES.in index 7add53452..eb49cd438 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -55,6 +55,7 @@ libempathy-gtk/empathy-log-window.c [type: gettext/glade]libempathy-gtk/empathy-contact-selector-dialog.ui libempathy-gtk/empathy-new-message-dialog.c libempathy-gtk/empathy-new-call-dialog.c +libempathy-gtk/empathy-password-dialog.c libempathy-gtk/empathy-presence-chooser.c libempathy-gtk/empathy-protocol-chooser.c [type: gettext/glade]libempathy-gtk/empathy-search-bar.ui diff --git a/src/empathy-auth-client.c b/src/empathy-auth-client.c index 17b66a57d..98a736fb9 100644 --- a/src/empathy-auth-client.c +++ b/src/empathy-auth-client.c @@ -29,9 +29,12 @@ #define DEBUG_FLAG EMPATHY_DEBUG_TLS #include <libempathy/empathy-debug.h> #include <libempathy/empathy-auth-factory.h> +#include <libempathy/empathy-server-sasl-handler.h> #include <libempathy/empathy-server-tls-handler.h> #include <libempathy/empathy-tls-verifier.h> +#include <libempathy/empathy-utils.h> +#include <libempathy-gtk/empathy-password-dialog.h> #include <libempathy-gtk/empathy-tls-dialog.h> #include <libempathy-gtk/empathy-ui-utils.h> @@ -180,7 +183,7 @@ verifier_verify_cb (GObject *source, } static void -auth_factory_new_handler_cb (EmpathyAuthFactory *factory, +auth_factory_new_tls_handler_cb (EmpathyAuthFactory *factory, EmpathyServerTLSHandler *handler, gpointer user_data) { @@ -204,6 +207,23 @@ auth_factory_new_handler_cb (EmpathyAuthFactory *factory, g_free (hostname); } +static void +auth_factory_new_sasl_handler_cb (EmpathyAuthFactory *factory, + EmpathyServerSASLHandler *handler, + gpointer user_data) +{ + GtkWidget *dialog; + + DEBUG ("New SASL server handler received from the factory"); + + /* If the handler has the password it will deal with it itself. */ + if (!empathy_server_sasl_handler_has_password (handler)) + { + dialog = empathy_password_dialog_new (handler); + gtk_widget_show (dialog); + } +} + int main (int argc, char **argv) @@ -238,7 +258,10 @@ main (int argc, factory = empathy_auth_factory_dup_singleton (); g_signal_connect (factory, "new-server-tls-handler", - G_CALLBACK (auth_factory_new_handler_cb), NULL); + G_CALLBACK (auth_factory_new_tls_handler_cb), NULL); + + g_signal_connect (factory, "new-server-sasl-handler", + G_CALLBACK (auth_factory_new_sasl_handler_cb), NULL); if (!empathy_auth_factory_register (factory, &error)) { |